nxcomp/0000755000076400007640000000000011576152211012333 5ustar svetonisvetoninxcomp/ImageText8.cpp0000644000076400007640000001535311323113030015007 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ImageText8.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int ImageText8Store::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { ImageText8Message *imageText8 = (ImageText8Message *) message; // // Here is the fingerprint. // imageText8 -> len = *(buffer + 1); imageText8 -> drawable = GetULONG(buffer + 4, bigEndian); imageText8 -> gcontext = GetULONG(buffer + 8, bigEndian); imageText8 -> x = GetUINT(buffer + 12, bigEndian); imageText8 -> y = GetUINT(buffer + 14, bigEndian); if ((int) size > dataOffset) { int pad = (size - dataOffset) - imageText8 -> len; if (pad > 0) { CleanData((unsigned char *) buffer + size - pad, pad); } } #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int ImageText8Store::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { ImageText8Message *imageText8 = (ImageText8Message *) message; // // Fill all the message's fields. // *(buffer + 1) = imageText8 -> len; PutULONG(imageText8 -> drawable, buffer + 4, bigEndian); PutULONG(imageText8 -> gcontext, buffer + 8, bigEndian); PutUINT(imageText8 -> x, buffer + 12, bigEndian); PutUINT(imageText8 -> y, buffer + 14, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void ImageText8Store::dumpIdentity(const Message *message) const { #ifdef DUMP ImageText8Message *imageText8 = (ImageText8Message *) message; *logofs << name() << ": Identity len " << (unsigned int) imageText8 -> len << " drawable " << imageText8 -> drawable << ", gcontext " << imageText8 -> gcontext << ", x " << imageText8 -> x << ", y " << imageText8 -> y << ", size " << imageText8 -> size_ << ".\n"; #endif } void ImageText8Store::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 1, 1); } void ImageText8Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { ImageText8Message *imageText8 = (ImageText8Message *) message; ImageText8Message *cachedImageText8 = (ImageText8Message *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << imageText8 -> drawable << " as " << "drawable" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(imageText8 -> drawable, clientCache -> drawableCache); cachedImageText8 -> drawable = imageText8 -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << imageText8 -> gcontext << " as " << "gcontext" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(imageText8 -> gcontext, clientCache -> gcCache); cachedImageText8 -> gcontext = imageText8 -> gcontext; #ifdef TEST *logofs << name() << ": Encoding value " << imageText8 -> x << " as " << "x" << " field.\n" << logofs_flush; #endif unsigned short int diff_x = imageText8 -> x - cachedImageText8 -> x; encodeBuffer.encodeCachedValue(diff_x, 16, clientCache -> imageTextCacheX); cachedImageText8 -> x = imageText8 -> x; #ifdef TEST *logofs << name() << ": Encoding value " << imageText8 -> y << " as " << "y" << " field.\n" << logofs_flush; #endif unsigned short int diff_y = imageText8 -> y - cachedImageText8 -> y; encodeBuffer.encodeCachedValue(diff_y, 16, clientCache -> imageTextCacheY); cachedImageText8 -> y = imageText8 -> y; } void ImageText8Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { ImageText8Message *imageText8 = (ImageText8Message *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); imageText8 -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << imageText8 -> drawable << " as " << "drawable" << " field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); imageText8 -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << imageText8 -> gcontext << " as gcontext field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> imageTextCacheX); imageText8 -> x += value; imageText8 -> x &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << imageText8 -> x << " as x field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> imageTextCacheY); imageText8 -> y += value; imageText8 -> y &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << imageText8 -> y << " as y field.\n" << logofs_flush; #endif } nxcomp/ConfigureWindow.cpp0000644000076400007640000000775211323113030016145 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ConfigureWindow.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int ConfigureWindowStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message; // // Here is the fingerprint. // configureWindow -> window = GetULONG(buffer + 4, bigEndian); configureWindow -> value_mask = GetUINT(buffer + 8, bigEndian); // // To increase effectiveness of the caching algorithm // we remove the unused bytes carried in the data part. // if ((int) size > dataOffset) { #ifdef DEBUG *logofs << name() << ": Removing unused bytes from the data payload.\n" << logofs_flush; #endif configureWindow -> value_mask &= (1 << 7) - 1; unsigned int mask = 0x1; unsigned char *source = (unsigned char *) buffer + CONFIGUREWINDOW_DATA_OFFSET; unsigned long value = 0; for (unsigned int i = 0; i < 7; i++) { if (configureWindow -> value_mask & mask) { value = GetULONG(source, bigEndian); value &= (1 << CONFIGUREWINDOW_FIELD_WIDTH[i]) - 1; PutULONG(value, source, bigEndian); source += 4; } mask <<= 1; } } #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int ConfigureWindowStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message; // // Fill all the message's fields. // PutULONG(configureWindow -> window, buffer + 4, bigEndian); PutUINT(configureWindow -> value_mask, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void ConfigureWindowStore::dumpIdentity(const Message *message) const { #ifdef DUMP ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message; *logofs << "ConfigureWindow: window " << configureWindow -> window << ", value_mask " << configureWindow -> value_mask << ", size " << configureWindow -> size_ << ".\n"; #endif } void ConfigureWindowStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 4, 4); md5_append(md5_state_, buffer + 8, 2); } nxcomp/SetUnpackColormapCompat.h0000644000076400007640000001114311323113027017232 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef SetUnpackColormapCompat_H #define SetUnpackColormapCompat_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define SETUNPACKCOLORMAP_ENABLE_CACHE 1 #define SETUNPACKCOLORMAP_ENABLE_DATA 1 #define SETUNPACKCOLORMAP_ENABLE_SPLIT 1 #define SETUNPACKCOLORMAP_ENABLE_COMPRESS 1 #define SETUNPACKCOLORMAP_DATA_LIMIT 4096 #define SETUNPACKCOLORMAP_DATA_OFFSET 8 #define SETUNPACKCOLORMAP_CACHE_SLOTS 2000 #define SETUNPACKCOLORMAP_CACHE_THRESHOLD 5 #define SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD 0 // // The message class. // class SetUnpackColormapCompatMessage : public Message { friend class SetUnpackColormapCompatStore; public: SetUnpackColormapCompatMessage() { } ~SetUnpackColormapCompatMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char client; unsigned int entries; }; class SetUnpackColormapCompatStore : public MessageStore { public: SetUnpackColormapCompatStore(StaticCompressor *compressor); virtual ~SetUnpackColormapCompatStore(); virtual const char *name() const { return "SetUnpackColormapCompat"; } virtual unsigned char opcode() const { return X_NXSetUnpackColormap; } virtual unsigned int storage() const { return sizeof(SetUnpackColormapCompatMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new SetUnpackColormapCompatMessage(); } virtual Message *create(const Message &message) const { return new SetUnpackColormapCompatMessage((const SetUnpackColormapCompatMessage &) message); } virtual void destroy(Message *message) const { delete (SetUnpackColormapCompatMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* SetUnpackColormapCompat_H */ nxcomp/IntCache.cpp0000644000076400007640000001235311323113026014510 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include "Misc.h" #include "IntCache.h" #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP IntCache::IntCache(unsigned int size) : size_(size), length_(0), buffer_(new unsigned int[size]), lastDiff_(0), lastValueInserted_(0), predictedBlockSize_(0) { } int IntCache::lookup(unsigned int &value, unsigned int &index, unsigned int mask, unsigned int &sameDiff) { for (unsigned int i = 0; i < length_; i++) { if (value == buffer_[i]) { index = i; if (i) { unsigned int target = (i >> 1); do { buffer_[i] = buffer_[i - 1]; i--; } while (i > target); buffer_[target] = value; } return 1; } } unsigned int insertionPoint; if (2 >= length_) insertionPoint = length_; else insertionPoint = 2; unsigned int start; if (length_ >= size_) start = size_ - 1; else { start = length_; length_++; } for (unsigned int k = start; k > insertionPoint; k--) buffer_[k] = buffer_[k - 1]; buffer_[insertionPoint] = value; unsigned int diff = value - lastValueInserted_; lastValueInserted_ = (value & mask); value = (diff & mask); sameDiff = (value == lastDiff_); if (!sameDiff) { lastDiff_ = value; unsigned int lastChangeIndex = 0; unsigned int lastBitIsOne = (lastDiff_ & 0x1); unsigned int j = 1; for (unsigned int nextMask = 0x2; nextMask & mask; nextMask <<= 1) { unsigned int nextBitIsOne = (lastDiff_ & nextMask); if (nextBitIsOne) { if (!lastBitIsOne) { lastChangeIndex = j; lastBitIsOne = nextBitIsOne; } } else { if (lastBitIsOne) { lastChangeIndex = j; lastBitIsOne = nextBitIsOne; } } j++; } predictedBlockSize_ = lastChangeIndex + 1; if (predictedBlockSize_ < 2) predictedBlockSize_ = 2; } return 0; } void IntCache::insert(unsigned int &value, unsigned int mask) { unsigned int insertionPoint; if (2 >= length_) insertionPoint = length_; else insertionPoint = 2; unsigned int start; if (length_ >= size_) start = size_ - 1; else { start = length_; length_++; } for (unsigned int k = start; k > insertionPoint; k--) buffer_[k] = buffer_[k - 1]; if (lastDiff_ != value) { lastDiff_ = value; unsigned int lastChangeIndex = 0; unsigned int lastBitIsOne = (lastDiff_ & 0x1); unsigned int j = 1; for (unsigned int nextMask = 0x2; nextMask & mask; nextMask <<= 1) { unsigned int nextBitIsOne = (lastDiff_ & nextMask); if (nextBitIsOne) { if (!lastBitIsOne) { lastChangeIndex = j; lastBitIsOne = nextBitIsOne; } } else { if (lastBitIsOne) { lastChangeIndex = j; lastBitIsOne = nextBitIsOne; } } j++; } predictedBlockSize_ = lastChangeIndex + 1; if (predictedBlockSize_ < 2) predictedBlockSize_ = 2; } lastValueInserted_ += value; lastValueInserted_ &= mask; buffer_[insertionPoint] = lastValueInserted_; value = lastValueInserted_; } void IntCache::push(unsigned int &value, unsigned int mask) { // // Using a memmove() appears to be slower. // // memmove((char *) &buffer_[1], (char *) &buffer_[0], // sizeof(unsigned int) * (size_ - 1)); // // if (length_ < size_) // { // length_++; // } // unsigned int start; if (length_ >= size_) { start = size_ - 1; } else { start = length_; length_++; } for (unsigned int k = start; k > 0; k--) { buffer_[k] = buffer_[k - 1]; } value &= mask; buffer_[0] = value; } void IntCache::dump() { #ifdef DUMP *logofs << "IntCache: Dumping content of cache at " << (void *) this << ":\n" << logofs_flush; for (unsigned int i = 0; i < length_; i++) { *logofs << "IntCache: [" << i << "][" << buffer_[i] << "]\n"; } #endif } nxcomp/PolyText8.cpp0000644000076400007640000001776311323113031014720 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "PolyText8.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int PolyText8Store::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { PolyText8Message *polyText8 = (PolyText8Message *) message; // // Here is the fingerprint. // polyText8 -> drawable = GetULONG(buffer + 4, bigEndian); polyText8 -> gcontext = GetULONG(buffer + 8, bigEndian); polyText8 -> x = GetUINT(buffer + 12, bigEndian); polyText8 -> y = GetUINT(buffer + 14, bigEndian); // // Clean up padding bytes. // #ifdef DUMP DumpData(buffer, size); *logofs << "\n\n" << logofs_flush; #endif if ((int) size > dataOffset) { int length; int current; int delta; int item; unsigned int nitem; unsigned char *pad = NULL; unsigned char *end = NULL; delta = 1; nitem = 0; #ifdef DUMP *logofs << name() << " Size " << size << ".\n" << logofs_flush; #endif // // Data is a list of TextItem where element // can be a string or a font shift. // current = POLYTEXT8_DATA_OFFSET; length = POLYTEXT8_DATA_OFFSET; do { #ifdef DUMP *logofs << name() << " Current " << current << ".\n" << logofs_flush; #endif item = GetUINT(buffer + length , bigEndian); if (item < 255) { // // Text element. Number represents // the 'Length of string' field. // length += (item + delta + 1); nitem++; } else if (item == 255) { // // Element is a font shift. // length += 5; nitem++; } #ifdef DUMP *logofs << name() << " Item " << item << ".\n" << logofs_flush; #endif current += length; } while(current < (int) size && item != 0); #ifdef DUMP *logofs << name() << " Final length " << length << ".\n" << logofs_flush; #endif end = ((unsigned char *) buffer) + size; pad = ((unsigned char *) buffer) + length; for (; pad < end && nitem >= 1; pad++) { #ifdef DUMP *logofs << name() << " Padding " << " .\n" << logofs_flush; #endif *pad = 0; } } #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int PolyText8Store::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { PolyText8Message *polyText8 = (PolyText8Message *) message; // // Fill all the message's fields. // PutULONG(polyText8 -> drawable, buffer + 4, bigEndian); PutULONG(polyText8 -> gcontext, buffer + 8, bigEndian); PutUINT(polyText8 -> x, buffer + 12, bigEndian); PutUINT(polyText8 -> y, buffer + 14, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void PolyText8Store::dumpIdentity(const Message *message) const { #ifdef DUMP PolyText8Message *polyText8 = (PolyText8Message *) message; *logofs << name() << ": Identity drawable " << polyText8 -> drawable << ", gcontext " << polyText8 -> gcontext << ", x " << polyText8 -> x << ", y " << polyText8 -> y << ", size " << polyText8 -> size_ << ".\n"; #endif } void PolyText8Store::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { } void PolyText8Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { PolyText8Message *polyText8 = (PolyText8Message *) message; PolyText8Message *cachedPolyText8 = (PolyText8Message *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << polyText8 -> drawable << " as " << "drawable" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyText8 -> drawable, clientCache -> drawableCache); cachedPolyText8 -> drawable = polyText8 -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << polyText8 -> gcontext << " as " << "gcontext" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyText8 -> gcontext, clientCache -> gcCache); cachedPolyText8 -> gcontext = polyText8 -> gcontext; #ifdef TEST *logofs << name() << ": Encoding value " << polyText8 -> x << " as " << "x" << " field.\n" << logofs_flush; #endif unsigned short int diff_x = polyText8 -> x - cachedPolyText8 -> x; encodeBuffer.encodeCachedValue(diff_x, 16, clientCache -> polyTextCacheX); cachedPolyText8 -> x = polyText8 -> x; #ifdef TEST *logofs << name() << ": Encoding value " << polyText8 -> y << " as " << "y" << " field.\n" << logofs_flush; #endif unsigned short int diff_y = polyText8 -> y - cachedPolyText8 -> y; encodeBuffer.encodeCachedValue(diff_y, 16, clientCache -> polyTextCacheY); cachedPolyText8 -> y = polyText8 -> y; } void PolyText8Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { PolyText8Message *polyText8 = (PolyText8Message *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); polyText8 -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyText8 -> drawable << " as " << "drawable" << " field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); polyText8 -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyText8 -> gcontext << " as gcontext field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> polyTextCacheX); polyText8 -> x += value; polyText8 -> x &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyText8 -> x << " as x field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> polyTextCacheY); polyText8 -> y += value; polyText8 -> y &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyText8 -> y << " as y field.\n" << logofs_flush; #endif } nxcomp/TextCompressor.cpp0000644000076400007640000000560011323113027016031 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "TextCompressor.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // The compression obtained by this class is // very poor. In newer versions the text is // simply appended to the encode buffer and // compressed by leveraging the final stream // compression. // void TextCompressor::encodeChar(unsigned char ch, EncodeBuffer& encodeBuffer) { // encode each successive character of text using // a predictive model where most of the last 3 characters // (low order 7 bits of the previous character, plus the // low order 5 bits of the character before that, plus // the low order 3 bits of the character before that) // are used to find the right cache... CharCache& cache = cache_[key_ % cacheSize_]; if ((key_ >= 128) && (cache.getSize() == 0)) { // 3rd-order model doesn't have any statistics yet, // so use the 1st-order one instead CharCache& cache2 = cache_[(key_ & 0x7f) % cacheSize_]; encodeBuffer.encodeCachedValue((unsigned int) ch, 8, cache2); cache.insert(ch); } else { encodeBuffer.encodeCachedValue((unsigned int) ch, 8, cache); } key_ = (((key_ & 0x1f) << 7) | ((key_ & 0x380) << 5) | (ch & 0x7f)); } unsigned char TextCompressor::decodeChar(DecodeBuffer& decodeBuffer) { unsigned char nextChar; CharCache& cache = cache_[key_ % cacheSize_]; if ((key_ >= 128) && (cache.getSize() == 0)) { CharCache& cache2 = cache_[(key_ & 0x7f) % cacheSize_]; decodeBuffer.decodeCachedValue(nextChar, 8, cache2); cache.insert(nextChar); } else { decodeBuffer.decodeCachedValue(nextChar, 8, cache); } key_ = (((key_ & 0x1f) << 7) | ((key_ & 0x380) << 5) | (nextChar & 0x7f)); return nextChar; } nxcomp/PolyText16.cpp0000644000076400007640000002007411323113027014771 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "PolyText16.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int PolyText16Store::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { PolyText16Message *polyText16 = (PolyText16Message *) message; // // Here is the fingerprint. // polyText16 -> drawable = GetULONG(buffer + 4, bigEndian); polyText16 -> gcontext = GetULONG(buffer + 8, bigEndian); polyText16 -> x = GetUINT(buffer + 12, bigEndian); polyText16 -> y = GetUINT(buffer + 14, bigEndian); // // Clean up padding bytes. // #ifdef DUMP DumpData(buffer, size); *logofs << "\n" << logofs_flush; #endif if ((int) size > dataOffset) { int current; int length; int delta; int item; unsigned int nitem; unsigned char *pad = NULL; unsigned char *end = NULL; delta = 1; nitem = 0; #ifdef DUMP *logofs << name() << " Size " << size << ".\n" << logofs_flush; #endif // // Data is a list of TextItem where element // can be a string or a font shift. // current = POLYTEXT16_DATA_OFFSET; length = POLYTEXT16_DATA_OFFSET; do { #ifdef DUMP *logofs << name() << " Current " << current << ".\n" << logofs_flush; #endif item = GetUINT(buffer + length , bigEndian); if (item < 255) { // // Text element. Number represents // the 'Length of CHAR2B string' // field. // length += ((item * 2) + delta + 1); nitem++; } else if (item == 255) { // // Element is a font shift. // length += 5; nitem++; } #ifdef DUMP *logofs << name() << " Item " << item << ".\n" << logofs_flush; #endif current += length; } while(current < (int) size && item != 0); #ifdef DUMP *logofs << name() << " Final length " << length << ".\n" << logofs_flush; #endif end = ((unsigned char *) buffer) + size; pad = ((unsigned char *) buffer) + length; for (; pad < end && nitem >= 1; pad++) { #ifdef DUMP *logofs << name() << " Padding " << " .\n" << logofs_flush; #endif *pad = 0; } } #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int PolyText16Store::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { PolyText16Message *polyText16 = (PolyText16Message *) message; // // Fill all the message's fields. // PutULONG(polyText16 -> drawable, buffer + 4, bigEndian); PutULONG(polyText16 -> gcontext, buffer + 8, bigEndian); PutUINT(polyText16 -> x, buffer + 12, bigEndian); PutUINT(polyText16 -> y, buffer + 14, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void PolyText16Store::dumpIdentity(const Message *message) const { #ifdef DUMP PolyText16Message *polyText16 = (PolyText16Message *) message; *logofs << name() << ": Identity drawable " << polyText16 -> drawable << ", gcontext " << polyText16 -> gcontext << ", x " << polyText16 -> x << ", y " << polyText16 -> y << ", size " << polyText16 -> size_ << ".\n"; #endif } void PolyText16Store::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { } void PolyText16Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { PolyText16Message *polyText16 = (PolyText16Message *) message; PolyText16Message *cachedPolyText16 = (PolyText16Message *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << polyText16 -> drawable << " as " << "drawable" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyText16 -> drawable, clientCache -> drawableCache); cachedPolyText16 -> drawable = polyText16 -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << polyText16 -> gcontext << " as " << "gcontext" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyText16 -> gcontext, clientCache -> gcCache); cachedPolyText16 -> gcontext = polyText16 -> gcontext; #ifdef TEST *logofs << name() << ": Encoding value " << polyText16 -> x << " as " << "x" << " field.\n" << logofs_flush; #endif unsigned short int diff_x = polyText16 -> x - cachedPolyText16 -> x; encodeBuffer.encodeCachedValue(diff_x, 16, clientCache -> polyTextCacheX); cachedPolyText16 -> x = polyText16 -> x; #ifdef TEST *logofs << name() << ": Encoding value " << polyText16 -> y << " as " << "y" << " field.\n" << logofs_flush; #endif unsigned short int diff_y = polyText16 -> y - cachedPolyText16 -> y; encodeBuffer.encodeCachedValue(diff_y, 16, clientCache -> polyTextCacheY); cachedPolyText16 -> y = polyText16 -> y; } void PolyText16Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { PolyText16Message *polyText16 = (PolyText16Message *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); polyText16 -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyText16 -> drawable << " as " << "drawable" << " field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); polyText16 -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyText16 -> gcontext << " as gcontext field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> polyTextCacheX); polyText16 -> x += value; polyText16 -> x &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyText16 -> x << " as x field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> polyTextCacheY); polyText16 -> y += value; polyText16 -> y &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyText16 -> y << " as y field.\n" << logofs_flush; #endif } nxcomp/BlockCacheSet.h0000644000076400007640000000332411323113030015122 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef BlockCacheSet_H #define BlockCacheSet_H #include "BlockCache.h" class BlockCacheSet { public: BlockCacheSet(unsigned int numCaches); ~BlockCacheSet(); int lookup(unsigned int size, const unsigned char *data, unsigned int &index); void get(unsigned int index, unsigned int &size, const unsigned char *&data); void set(unsigned int size, const unsigned char *data); private: BlockCache ** caches_; unsigned int size_; unsigned int length_; }; #endif /* BlockCacheSet_H */ nxcomp/Types.h0000644000076400007640000001316411323113030013577 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Types_H #define Types_H using namespace std; #include #include #include #include #include "MD5.h" // // This is MD5 length. // #define MD5_LENGTH 16 // // Types of repositories. Replace the original // clear() methods from STL in order to actually // free the unused memory. // class Message; class T_data : public vector < unsigned char > { public: unsigned char *begin() { return &*(vector < unsigned char >::begin()); } const unsigned char *begin() const { return &*(vector < unsigned char >::begin()); } void clear() { #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) #if defined(__GLIBCPP_INTERNAL_VECTOR_H) _Destroy(_M_start, _M_finish); #else /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */ destroy(_M_start, _M_finish); #endif /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */ _M_deallocate(_M_start, _M_end_of_storage - _M_start); _M_start = _M_finish = _M_end_of_storage = 0; #else /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */ #if defined(_GLIBCXX_VECTOR) _Destroy(this->_M_impl._M_start, this->_M_impl._M_finish); _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = this->_M_impl._M_finish = this->_M_impl._M_end_of_storage = 0; #else /* #if defined(_GLIBCXX_VECTOR) */ destroy(start, finish); deallocate(); start = finish = end_of_storage = 0; #endif /* #if defined(_GLIBCXX_VECTOR) */ #endif /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */ } }; class T_messages : public vector < Message * > { public: void clear() { #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) #if defined(__GLIBCPP_INTERNAL_VECTOR_H) _Destroy(_M_start, _M_finish); #else /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */ destroy(_M_start, _M_finish); #endif /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */ _M_deallocate(_M_start, _M_end_of_storage - _M_start); _M_start = _M_finish = _M_end_of_storage = 0; #else /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */ #if defined(_GLIBCXX_VECTOR) _Destroy(this->_M_impl._M_start, this->_M_impl._M_finish); _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = this->_M_impl._M_finish = this->_M_impl._M_end_of_storage = 0; #else /* #if defined(_GLIBCXX_VECTOR) */ destroy(start, finish); deallocate(); start = finish = end_of_storage = 0; #endif /* #if defined(_GLIBCXX_VECTOR) */ #endif /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */ } }; typedef md5_byte_t * T_checksum; struct T_less { bool operator()(T_checksum a, T_checksum b) const { return (memcmp(a, b, MD5_LENGTH) < 0); } }; typedef map < T_checksum, int, T_less > T_checksums; class Split; typedef list < Split * > T_splits; class File; struct T_older { bool operator()(File *a, File *b) const; }; typedef set < File *, T_older > T_files; typedef list < int > T_list; // // Used to accomodate data to be read and // written to a socket. // typedef struct { T_data data_; int length_; int start_; } T_buffer; // // The message store operation that was // executed for the message. The channels // use these values to determine how to // handle the message after it has been // received at the decoding side. // enum T_store_action { is_hit, is_added, is_discarded, is_removed, is_added_compat = 0, is_hit_compat = 1 }; #define IS_HIT (control -> isProtoStep8() == 1 ? is_hit : is_hit_compat) #define IS_ADDED (control -> isProtoStep8() == 1 ? is_added : is_added_compat) enum T_checksum_action { use_checksum, discard_checksum }; enum T_data_action { use_data, discard_data }; // // Message is going to be weighted for // deletion at insert or cleanup time? // enum T_rating { rating_for_insert, rating_for_clean }; // // How to handle the writes to the X // and proxy connections. // enum T_write { write_immediate, write_delayed }; enum T_flush { flush_if_needed, flush_if_any }; // // This is the value to indicate an // invalid position in the message // store. // static const int nothing = -1; #endif /* Types_H */ nxcomp/Jpeg.cpp0000644000076400007640000004743311342776775013761 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #ifdef __cplusplus extern "C" { #include #include } #else #include #include #endif #include "Misc.h" #include "Jpeg.h" #include "Unpack.h" #define PANIC #define WARNING #undef TEST #undef DEBUG #define RGB24_TO_PIXEL(bpp,r,g,b) \ ((((CARD##bpp)(r) & 0xff) * srcRedMax + 127) / 255 \ << srcRedShift | \ (((CARD##bpp)(g) & 0xff) * srcGreenMax + 127) / 255 \ << srcGreenShift | \ (((CARD##bpp)(b) & 0xff) * srcBlueMax + 127) / 255 \ << srcBlueShift) #define RGB24_TO_PIXEL32(r,g,b) \ (((CARD32)(r) & 0xff) << srcRedShift | \ ((CARD32)(g) & 0xff) << srcGreenShift | \ ((CARD32)(b) & 0xff) << srcBlueShift) // // Functions from Unpack.cpp // extern int Unpack32To32(const T_colormask *colormask, const unsigned int *data, unsigned int *out, unsigned int *end); extern int Unpack24To24(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); extern int Unpack16To16(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); // // Local functions used for the jpeg decompression. // static void JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData, int compressedLen); static void JpegInitSource(j_decompress_ptr cinfo); static void JpegTermSource(j_decompress_ptr cinfo); static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes); static boolean JpegFillInputBuffer(j_decompress_ptr cinfo); static int DecompressJpeg16(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder); static int DecompressJpeg24(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder); static int DecompressJpeg32(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder); void UnpackJpegErrorHandler(j_common_ptr cinfo); // // Colormap stuff. // CARD16 srcRedMax, srcGreenMax, srcBlueMax; CARD8 srcRedShift, srcGreenShift, srcBlueShift; // // Error handler. // static bool jpegError; jmp_buf UnpackJpegContext; void UnpackJpegErrorHandler(j_common_ptr cinfo) { #ifdef PANIC *logofs << "UnpackJpegErrorHandler: PANIC! Detected error in JPEG decompression.\n" << logofs_flush; *logofs << "UnpackJpegErrorHandler: PANIC! Trying to revert to the previous context.\n" << logofs_flush; #endif jpegError = 1; longjmp(UnpackJpegContext, 1); } // // Attributes used for the jpeg decompression. // static struct jpeg_source_mgr jpegSrcManager; static JOCTET *jpegBufferPtr; static size_t jpegBufferLen; static char *tmpBuf; static int tmpBufSize = 0; int UnpackJpeg(T_geometry *geometry, unsigned char method, unsigned char *srcData, int srcSize, int dstBpp, int dstWidth, int dstHeight, unsigned char *dstData, int dstSize) { int byteOrder = geometry -> image_byte_order; // // Check if data is coming from a failed unsplit. // if (srcSize < 2 || (srcData[0] == SPLIT_PATTERN && srcData[1] == SPLIT_PATTERN)) { #ifdef WARNING *logofs << "UnpackJpeg: WARNING! Skipping unpack of dummy data.\n" << logofs_flush; #endif return -1; } #ifdef DEBUG *logofs << "UnpackJpeg: Decompression. Source size " << srcSize << " bits per plane " << dstBpp << ".\n" << logofs_flush; #endif srcRedShift = ffs(geometry -> red_mask) - 1; srcGreenShift = ffs(geometry -> green_mask) - 1; srcBlueShift = ffs(geometry -> blue_mask) - 1; #ifdef DEBUG *logofs << "UnpackJpeg: Red shift " << (int) srcRedShift << " green shift " << (int) srcGreenShift << " blue shift " << (int) srcBlueShift << ".\n" << logofs_flush; #endif srcRedMax = geometry -> red_mask >> srcRedShift; srcGreenMax = geometry -> green_mask >> srcGreenShift; srcBlueMax = geometry -> blue_mask >> srcBlueShift; #ifdef DEBUG *logofs << "UnpackJpeg: Red mask " << (void *) geometry -> red_mask << " green mask " << (void *) geometry -> green_mask << " blue mask " << (void *) geometry -> blue_mask << ".\n" << logofs_flush; #endif #ifdef DEBUG *logofs << "UnpackJpeg: Red max " << srcRedMax << " green max " << srcGreenMax << " blue max " << srcBlueMax << ".\n" << logofs_flush; #endif // // Make enough space in the temporary // buffer to have one complete row of // the image with 3 bytes for a pixel. // tmpBufSize = dstWidth * 3; tmpBuf = new char[tmpBufSize]; if (tmpBuf == NULL) { #ifdef PANIC *logofs << "UnpackJpeg: PANIC! Cannot allocate " << dstWidth * 3 << " bytes for Jpeg " << "decompressed data.\n" << logofs_flush; #endif delete [] tmpBuf; return -1; } int result = 1; switch(dstBpp) { case 8: { // // Simply move the data from srcData to dstData // taking into consideration the correct padding. // int row; unsigned char * dstBuff = dstData; unsigned char * srcBuff = srcData; for (row = 0; row < dstHeight; row++) { memcpy(dstBuff, srcBuff, dstWidth); dstBuff += RoundUp4(dstWidth); srcBuff += dstWidth; } break; } case 16: { result = DecompressJpeg16(srcData, srcSize, dstWidth, dstHeight, dstData, byteOrder); break; } case 24: { result = DecompressJpeg24(srcData, srcSize, dstWidth, dstHeight, dstData, byteOrder); break; } case 32: { result = DecompressJpeg32(srcData, srcSize, dstWidth, dstHeight, dstData, byteOrder); break; } default: { #ifdef PANIC *logofs << "UnpackJpeg: PANIC! Failed to decode Jpeg image. " << " Unsupported Bpp value " << dstBpp << " for the Jpeg compression" << ".\n" << logofs_flush; #endif delete [] tmpBuf; result = -1; } } #ifdef DEBUG *logofs << "UnpackJpeg: Decompression finished with result " << result << ".\n" << logofs_flush; #endif if (result == -1) { delete [] tmpBuf; #ifdef PANIC *logofs << "UnpackJpeg: PANIC! Failed to decode Jpeg image using " << dstBpp << " Bpp destination.\n" << logofs_flush; #endif return result; } // // Apply the correction for the brightness. // int maskMethod; switch(method) { case PACK_JPEG_8_COLORS: { maskMethod = MASK_8_COLORS; break; } case PACK_JPEG_64_COLORS: { maskMethod = MASK_64_COLORS; break; } case PACK_JPEG_256_COLORS: { maskMethod = MASK_256_COLORS; break; } case PACK_JPEG_512_COLORS: { maskMethod = MASK_512_COLORS; break; } case PACK_JPEG_4K_COLORS: { maskMethod = MASK_4K_COLORS; break; } case PACK_JPEG_32K_COLORS: { maskMethod = MASK_32K_COLORS; break; } case PACK_JPEG_64K_COLORS: { maskMethod = MASK_64K_COLORS; break; } case PACK_JPEG_256K_COLORS: { maskMethod = MASK_256K_COLORS; break; } case PACK_JPEG_2M_COLORS: { maskMethod = MASK_2M_COLORS; break; } case PACK_JPEG_16M_COLORS: { maskMethod = MASK_16M_COLORS; break; } default: { delete [] tmpBuf; return -1; } } const T_colormask *colorMask = MethodColorMask(maskMethod); unsigned char *dstBuff = dstData; switch (dstBpp) { case 16: { Unpack16To16(colorMask, dstBuff, dstBuff, dstBuff + dstSize); break; } case 24: { break; } case 32: { Unpack32To32(colorMask, (unsigned int *) dstBuff, (unsigned int *) dstBuff, (unsigned int *) (dstBuff + dstSize)); break; } default: { delete [] tmpBuf; return -1; } } delete [] tmpBuf; return 1; } // // Functions that actually do the Jpeg decompression. // int DecompressJpeg16(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; unsigned char *data; JSAMPROW rowPointer[1]; unsigned int dx = 0; unsigned int dy = 0; #ifdef DEBUG *logofs << "DecompressJpeg16: Decompressing with length " << compressedLen << " width " << w << " height " << h << ".\n" << logofs_flush; #endif jpegError = 0; cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = UnpackJpegErrorHandler; if (setjmp(UnpackJpegContext) == 1) { #ifdef TEST *logofs << "DecompressJpeg16: Out of the long jump with error '" << jpegError << "'.\n" << logofs_flush; #endif goto AbortDecompressJpeg16; } jpeg_create_decompress(&cinfo); if (jpegError) goto AbortDecompressJpeg16; JpegSetSrcManager(&cinfo, compressedData, compressedLen); jpeg_read_header(&cinfo, 1); if (jpegError) goto AbortDecompressJpeg16; cinfo.out_color_space = JCS_RGB; jpeg_start_decompress(&cinfo); if (jpegError) goto AbortDecompressJpeg16; if (cinfo.output_width != w || cinfo.output_height != h || cinfo.output_components != 3) { #ifdef PANIC *logofs << "DecompressJpeg16: PANIC! Wrong JPEG data received.\n" << logofs_flush; #endif jpeg_destroy_decompress(&cinfo); return -1; } // // PixelPtr points to dstBuf which is // already padded correctly for the final // image to put // data = dstBuf; rowPointer[0] = (JSAMPROW) tmpBuf; unsigned long pixel; while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, rowPointer, 1); if (jpegError) goto AbortDecompressJpeg16; for (dx = 0; dx < w; dx++) { pixel = RGB24_TO_PIXEL(16, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1], tmpBuf[dx * 3 + 2]); // // Follow the server byte order when arranging data. // if (byteOrder == LSBFirst) { data[0] = (unsigned char) (pixel & 0xff); data[1] = (unsigned char) ((pixel >> 8) & 0xff); } else { data[1] = (unsigned char) (pixel & 0xff); data[0] = (unsigned char) ((pixel >> 8) & 0xff); } data += 2; } // // Move data at the beginning of the // next line. // data = data + (RoundUp4(w * 2) - w * 2); dy++; } AbortDecompressJpeg16: if (jpegError == 0) { jpeg_finish_decompress(&cinfo); } jpeg_destroy_decompress(&cinfo); if (jpegError == 1) { #ifdef PANIC *logofs << "DecompressJpeg16: Failed to decompress JPEG image.\n" << logofs_flush; #endif return -1; } #ifdef TEST *logofs << "DecompressJpeg16: Decompression finished with " << dy << " lines handled.\n" << logofs_flush; #endif return 1; } int DecompressJpeg24(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; CARD8 *pixelPtr = NULL; JSAMPROW rowPointer[1]; unsigned int dx = 0; unsigned int dy = 0; #ifdef TEST *logofs << "DecompressJpeg24: Decompressing with length " << compressedLen << " width " << w << " height " << h << ".\n" << logofs_flush; #endif jpegError = 0; cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = UnpackJpegErrorHandler; if (setjmp(UnpackJpegContext) == 1) { #ifdef TEST *logofs << "DecompressJpeg24: Out of the long jump with error '" << jpegError << "'.\n" << logofs_flush; #endif goto AbortDecompressJpeg24; } jpeg_create_decompress(&cinfo); if (jpegError) goto AbortDecompressJpeg24; JpegSetSrcManager(&cinfo, compressedData, compressedLen); jpeg_read_header(&cinfo, 1); if (jpegError) goto AbortDecompressJpeg24; cinfo.out_color_space = JCS_RGB; jpeg_start_decompress(&cinfo); if (jpegError) goto AbortDecompressJpeg24; if (cinfo.output_width != w || cinfo.output_height != h || cinfo.output_components != 3) { #ifdef PANIC *logofs << "DecompressJpeg24: PANIC! Wrong JPEG data received.\n" << logofs_flush; #endif jpeg_destroy_decompress(&cinfo); return -1; } // // PixelPtr points to dstBuf which is // already padded correctly for the final // image to put. // pixelPtr = (CARD8 *) dstBuf; rowPointer[0] = (JSAMPROW) tmpBuf; while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, rowPointer, 1); if (jpegError) goto AbortDecompressJpeg24; for (dx = 0; dx < w; dx++) { // // Follow the server byte order when arranging data. // if (byteOrder == LSBFirst) { pixelPtr[0] = tmpBuf[dx * 3]; pixelPtr[1] = tmpBuf[dx * 3 + 1]; pixelPtr[2] = tmpBuf[dx * 3 + 2]; } else { pixelPtr[2] = tmpBuf[dx * 3]; pixelPtr[1] = tmpBuf[dx * 3 + 1]; pixelPtr[0] = tmpBuf[dx * 3 + 2]; } pixelPtr += 3; } // // Go to the next line. // pixelPtr = (CARD8 *) (((char *) pixelPtr) + (RoundUp4(w * 3) - w * 3)); dy++; } AbortDecompressJpeg24: if (jpegError == 0) { jpeg_finish_decompress(&cinfo); } jpeg_destroy_decompress(&cinfo); if (jpegError == 1) { #ifdef PANIC *logofs << "DecompressJpeg24: Failed to decompress JPEG image.\n" << logofs_flush; #endif return -1; } #ifdef TEST *logofs << "DecompressJpeg24: Decompression finished with " << dy << " lines handled.\n" << logofs_flush; #endif return 1; } int DecompressJpeg32(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; unsigned char *data; JSAMPROW rowPointer[1]; unsigned int dx = 0; unsigned int dy = 0; #ifdef TEST *logofs << "DecompressJpeg32: Decompressing with length " << compressedLen << " width " << w << " height " << h << ".\n" << logofs_flush; #endif jpegError = 0; cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = UnpackJpegErrorHandler; if (setjmp(UnpackJpegContext) == 1) { #ifdef TEST *logofs << "DecompressJpeg32: Out of the long jump with error '" << jpegError << "'.\n" << logofs_flush; #endif goto AbortDecompressJpeg32; } jpeg_create_decompress(&cinfo); if (jpegError) goto AbortDecompressJpeg32; JpegSetSrcManager(&cinfo, compressedData, compressedLen); jpeg_read_header(&cinfo, 1); if (jpegError) goto AbortDecompressJpeg32; cinfo.out_color_space = JCS_RGB; jpeg_start_decompress(&cinfo); if (jpegError) goto AbortDecompressJpeg32; if (cinfo.output_width != w || cinfo.output_height != h || cinfo.output_components != 3) { #ifdef PANIC *logofs << "DecompressJpeg32 : PANIC! Wrong JPEG data received.\n" << logofs_flush; #endif jpeg_destroy_decompress(&cinfo); return -1; } // // PixelPtr points to dstBuf which is // already padded correctly for the final // image to put // data = dstBuf; rowPointer[0] = (JSAMPROW) tmpBuf; unsigned long pixel; int i; while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, rowPointer, 1); if (jpegError) goto AbortDecompressJpeg32; for (dx = 0; dx < w; dx++) { pixel = RGB24_TO_PIXEL(32, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1], tmpBuf[dx * 3 + 2]); // // Follow the server byte order when arranging data. // if (byteOrder == LSBFirst) { for (i = 0; i < 4; i++) { data[i] = (unsigned char)(pixel & 0xff); pixel >>= 8; } } else { for (i = 3; i >= 0; i--) { data[i] = (unsigned char) (pixel & 0xff); pixel >>= 8; } } data += 4; } dy++; } AbortDecompressJpeg32: if (jpegError == 0) { jpeg_finish_decompress(&cinfo); } jpeg_destroy_decompress(&cinfo); if (jpegError == 1) { #ifdef PANIC *logofs << "DecompressJpeg32: Failed to decompress JPEG image.\n" << logofs_flush; #endif return -1; } #ifdef TEST *logofs << "DecompressJpeg32: Decompression finished with " << dy << " lines handled.\n" << logofs_flush; #endif return 1; } static void JpegInitSource(j_decompress_ptr cinfo) { jpegError = 0; } static boolean JpegFillInputBuffer(j_decompress_ptr cinfo) { jpegError = 1; jpegSrcManager.bytes_in_buffer = jpegBufferLen; jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; return 1; } static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes) { if (num_bytes < 0 || (unsigned long) num_bytes > jpegSrcManager.bytes_in_buffer) { jpegError = 1; jpegSrcManager.bytes_in_buffer = jpegBufferLen; jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; } else { jpegSrcManager.next_input_byte += (size_t) num_bytes; jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes; } } static void JpegTermSource(j_decompress_ptr cinfo) { } static void JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData, int compressedLen) { jpegBufferPtr = (JOCTET *) compressedData; jpegBufferLen = (size_t) compressedLen; jpegSrcManager.init_source = JpegInitSource; jpegSrcManager.fill_input_buffer = JpegFillInputBuffer; jpegSrcManager.skip_input_data = JpegSkipInputData; jpegSrcManager.resync_to_restart = jpeg_resync_to_restart; jpegSrcManager.term_source = JpegTermSource; jpegSrcManager.next_input_byte = jpegBufferPtr; jpegSrcManager.bytes_in_buffer = jpegBufferLen; cinfo->src = &jpegSrcManager; } nxcomp/ServerChannel.cpp0000644000076400007640000100370311342773403015605 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include #include #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; handleSplitEnable(); // // 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_++; // // Due to the way the loop was implemented // we can't encode multiple messages if we // are encoding the first request. // if (control -> isProtoStep7() == 0) { if (proxy -> handleAsyncInit() < 0) { return -1; } } } 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; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, nameLength); } else { serverCache_ -> getAtomNameTextCompressor.reset(); for (unsigned int i = 0; i < nameLength; i++) { serverCache_ -> getAtomNameTextCompressor. encodeChar(*nextSrc++, encodeBuffer); } } 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); if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, length); nextSrc += length; } else { serverCache_ -> getPropertyTextCompressor.reset(); for (; length; length--) { serverCache_ -> getPropertyTextCompressor.encodeChar( *nextSrc++, encodeBuffer); } } } 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); if (control -> isProtoStep8() == 0) { unsigned int compressedDataSize = 0; unsigned char *compressedData = NULL; int compressed = handleCompress(encodeBuffer, requestOpcode, messageStore -> dataOffset, inputMessage, inputLength, compressedData, compressedDataSize); if (compressed < 0) { return -1; } else if (compressed > 0) { // // Update size according to result of image compression. // handleUpdate(messageStore, inputLength - messageStore -> dataOffset, compressedDataSize); } } else { 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. // if (control -> isProtoStep7() == 1) { 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) { if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, dataLength); } else { clientCache_ -> changePropertyTextCompressor.reset(); for (unsigned int i = 0; i < dataLength; i++) { *nextDest++ = clientCache_ -> changePropertyTextCompressor. decodeChar(decodeBuffer); } } } 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); if (control -> isProtoStep7() == 1) { decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId, clientCache_ -> lastIdCache, clientCache_ -> gcCache, clientCache_ -> freeGCCache); } else { decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); } 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_); if (control -> isProtoStep7() == 1) { decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId, clientCache_ -> lastIdCache, clientCache_ -> windowCache, clientCache_ -> freeWindowCache); } else { decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); } 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; if (control -> isProtoStep10() == 1) { decodeBuffer.decodeCachedValue(numPoints, 16, clientCache_ -> fillPolyNumPointsCache, 4); } else { decodeBuffer.decodeCachedValue(numPoints, 14, 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); if (control -> isProtoStep7() == 1) { decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeGCCache); } else { decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); } PutULONG(value, outputMessage + 4, bigEndian_); } break; case X_FreePixmap: { outputLength = 8; outputMessage = writeBuffer_.addMessage(outputLength); if (control -> isProtoStep7() == 1) { decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeDrawableCache); PutULONG(value, outputMessage + 4, bigEndian_); } else { decodeBuffer.decodeBoolValue(value); if (!value) { decodeBuffer.decodeValue(value, 29, 4); clientCache_ -> createPixmapLastId += value; clientCache_ -> createPixmapLastId &= 0x1fffffff; } PutULONG(clientCache_ -> createPixmapLastId, 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; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, textLength); nextDest += textLength; } else { clientCache_ -> polyTextTextCompressor.reset(); while (textLength) { *nextDest++ = clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer); 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; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, textLength * 2); nextDest += textLength * 2; } else { clientCache_ -> polyTextTextCompressor.reset(); textLength <<= 1; while (textLength) { *nextDest++ = clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer); 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_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; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, textLength); } else { clientCache_ -> imageTextTextCompressor.reset(); for (unsigned int j = 0; j < textLength; j++) *nextDest++ = clientCache_ -> imageTextTextCompressor.decodeChar(decodeBuffer); } 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; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, textLength * 2); } else { clientCache_ -> imageTextTextCompressor.reset(); for (unsigned int j = 0; j < textLength * 2; j++) *nextDest++ = clientCache_ -> imageTextTextCompressor.decodeChar(decodeBuffer); } 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; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, nameLength); } else { clientCache_ -> internAtomTextCompressor.reset(); for (unsigned int i = 0; i < nameLength; i++) { *nextDest++ = clientCache_ -> internAtomTextCompressor.decodeChar(decodeBuffer); } } 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; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, textLength); } else { clientCache_ -> polyTextTextCompressor.reset(); for (unsigned int i = 0; i < textLength; i++) { *nextDest++ = clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer); } } 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; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, textLength); } else { clientCache_ -> polyTextTextCompressor.reset(); for (unsigned int i = 0; i < textLength; i++) { *nextDest++ = clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer); } } 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 && control -> isProtoStep7() == 1) { 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; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, nameLength); } else { clientCache_ -> openFontTextCompressor.reset(); for (; nameLength; nameLength--) { *nextDest++ = clientCache_ -> openFontTextCompressor. decodeChar(decodeBuffer); } } } 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; if (control -> isProtoStep9() == 1) { decodeBuffer.decodeValue(numRectangles, 15, 4); } else { decodeBuffer.decodeValue(numRectangles, 13, 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. // // 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) { if (control -> isProtoStep7() == 1) { 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 we are connected to an old proxy // version or the encoding side didn't // provide a checksum, then don't send // the split report. // if (control -> isProtoStep7() == 0 || 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 delete [] checksum; 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; if (control -> isProtoStep7() == 1) { 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. // if (control -> isProtoStep7() == 1) { 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 = 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 } else { unsigned int entries = GetULONG(buffer + 4, bigEndian_); if (size == entries * 4 + 8) { if (unpackState_[resource] -> colormap -> entries != entries && unpackState_[resource] -> colormap -> data != NULL) { #ifdef TEST *logofs << "handleColormap: Freeing previously " << "allocated unpack colormap.\n" << logofs_flush; #endif delete [] unpackState_[resource] -> colormap -> data; unpackState_[resource] -> colormap -> data = NULL; unpackState_[resource] -> colormap -> entries = 0; } if (entries > 0) { if (unpackState_[resource] -> colormap -> data == NULL) { unpackState_[resource] -> colormap -> data = new unsigned int[entries]; } if (unpackState_[resource] -> colormap -> data != NULL) { unpackState_[resource] -> colormap -> entries = entries; #ifdef DEBUG *logofs << "handleColormap: Size of new colormap " << "data is " << (entries << 2) << ".\n" << logofs_flush; #endif memcpy((unsigned char *) unpackState_[resource] -> colormap -> data, buffer + 8, entries << 2); #if defined(DEBUG) && defined(DUMP) *logofs << "handleColormap: Dumping colormap entries:\n" << logofs_flush; const unsigned int *p = (unsigned int *) buffer + 8; for (unsigned int i = 0; i < entries; i++) { *logofs << "handleColormap: [" << i << "] [" << (void *) p[i] << "].\n" << logofs_flush; } #endif } else { #ifdef PANIC *logofs << "handleColormap: PANIC! Can't allocate " << entries << " entries for unpack colormap " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif } } } else { #ifdef PANIC *logofs << "handleColormap: PANIC! Bad size " << size << " for set unpack colormap message for FD#" << fd_ << " with " << entries << " entries.\n" << logofs_flush; #endif } } 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. // if (control -> isProtoStep7() == 1) { 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 } else { unsigned int entries = GetULONG(buffer + 4, bigEndian_); if (size == RoundUp4(entries) + 8) { if (unpackState_[resource] -> alpha -> entries != entries && 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; } if (entries > 0) { if (unpackState_[resource] -> alpha -> data == NULL) { unpackState_[resource] -> alpha -> data = new unsigned char[entries]; } if (unpackState_[resource] -> alpha -> data != NULL) { unpackState_[resource] -> alpha -> entries = entries; #ifdef DEBUG *logofs << "handleAlpha: Size of new alpha data is " << entries << ".\n" << logofs_flush; #endif memcpy((unsigned char *) unpackState_[resource] -> alpha -> data, buffer + 8, entries); #if defined(DEBUG) && defined(DUMP) *logofs << "handleAlpha: Dumping alpha entries:\n" << logofs_flush; const unsigned char *p = buffer + 8; for (unsigned int i = 0; i < entries; i++) { *logofs << "handleAlpha: [" << i << "] [" << (void *) ((int) p[i]) << "].\n" << logofs_flush; } #endif } else { #ifdef PANIC *logofs << "handleAlpha: PANIC! Can't allocate " << entries << " entries for unpack alpha data " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif } } } #ifdef PANIC else { *logofs << "handleAlpha: PANIC! Bad size " << size << " for set unpack alpha message for FD#" << fd_ << " with " << entries << " entries.\n" << logofs_flush; } #endif } 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. The X_PutImage can // be handled here only if a split was // not requested. // if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) || (control -> isProtoStep7() == 1 && 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); shmemState_ -> present = *(buffer + 8); 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"; shmemState_ -> enabled = 1; 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. // if (control -> ShmemServer == 1 && control -> ShmemServerSize > 0 && enableServer == 1) { memcpy(buffer + 8, "MIT-SHM", 7); } else { memcpy(buffer + 8, "NO-MIT-", 7); } 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; shmemState_ -> id = shmget(IPC_PRIVATE, shmemState_ -> size, IPC_CREAT | permissions); 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 shmemState_ -> address = shmat(shmemState_ -> id, 0, 0); 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; } // // 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; // // Just to be sure. We should never // receive this request if connected // to an old proxy version. // handleSplitEnable(); #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 use // the split store at position 0. // if (control -> isProtoStep7() == 1) { 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 } #if defined(TEST) || defined(SPLIT) else { *logofs << "handleStartSplitRequest: SPLIT! Assuming fake id " << splitState_.current << " 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. // if (control -> isProtoStep7() == 1) { 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; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeBoolValue(receive); } else { receive = (control -> ImageCacheEnableLoad == 1 || control -> ImageCacheEnableSave == 1); } 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) { shmdt((char *) shmemState_ -> address); } if (shmemState_ -> id > 0) { shmctl(shmemState_ -> id, IPC_RMID, 0); } 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; } nxcomp/QueryFontReply.cpp0000644000076400007640000000766011323113027016010 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "QueryFontReply.h" #include "ServerCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP QueryFontReplyStore::QueryFontReplyStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = QUERYFONTREPLY_ENABLE_CACHE; enableData = QUERYFONTREPLY_ENABLE_DATA; enableSplit = QUERYFONTREPLY_ENABLE_SPLIT; enableCompress = QUERYFONTREPLY_ENABLE_COMPRESS; if (control -> isProtoStep7() == 1) { enableCompress = QUERYFONTREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7; } dataLimit = QUERYFONTREPLY_DATA_LIMIT; dataOffset = QUERYFONTREPLY_DATA_OFFSET; cacheSlots = QUERYFONTREPLY_CACHE_SLOTS; cacheThreshold = QUERYFONTREPLY_CACHE_THRESHOLD; cacheLowerThreshold = QUERYFONTREPLY_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } QueryFontReplyStore::~QueryFontReplyStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int QueryFontReplyStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // Clear the padding bytes. // unsigned char *pad = (unsigned char *) buffer; if (size >= 24) { PutULONG(0, pad + 20, bigEndian); } if (size >= 40) { PutULONG(0, pad + 36, bigEndian); } // // TODO: This doesn't work. Probably these // padding bytes are not padding anymore. // This is to be investigated. // // pad += 60; // // while (pad + 16 <= (buffer + size)) // { // PutULONG(0, pad + 12, bigEndian); // // pad += 16; // } // #ifdef DEBUG *logofs << name() << ": Cleaned padding bytes of " << "message at " << message << ".\n" << logofs_flush; #endif return 1; } int QueryFontReplyStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { return 1; } void QueryFontReplyStore::dumpIdentity(const Message *message) const { #ifdef DUMP QueryFontReplyMessage *queryFontReply = (QueryFontReplyMessage *) message; *logofs << name() << ": Identity size " << queryFontReply -> size_ << ".\n"; #endif } void QueryFontReplyStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { } nxcomp/ClientChannel.cpp0000644000076400007640000101057011342773403015555 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include "NXproto.h" #include "NXrender.h" #include "ClientChannel.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "StaticCompressor.h" #include "Statistics.h" #include "Proxy.h" #include "PutImage.h" #include "PutPackedImage.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 trace the invocations // of the agent's callbacks. // #undef CALLBACK // // By defining this, a simple procedure is activated at // startup which just allocates and deallocates plenty // of cache objects. This is used to help determine the // current memory requirements. // #undef MEMORY // // Inspects target of common X operations. // #undef TARGETS #ifdef TARGETS #include #include typedef set < unsigned int, less > T_windows; typedef set < unsigned int, less > T_pixmaps; typedef map < unsigned int, unsigned int, less > T_gcontexts; T_windows windows; T_pixmaps pixmaps; T_gcontexts gcontexts; #endif // // Define this to log when a channel // is created or destroyed. // #undef REFERENCES // // Here are the static members. // #ifdef REFERENCES int ClientChannel::references_ = 0; #endif ClientChannel::ClientChannel(Transport *transport, StaticCompressor *compressor) : Channel(transport, compressor), readBuffer_(transport_, this) { // // Sequence number of the next message // being encoded or decoded. // clientSequence_ = 0; serverSequence_ = 0; // // Current sequence known by NX agent. // lastSequence_ = 0; // // This is used to test the synchronous // flush in the proxy. // lastRequest_ = 0; // // Store information about the images // being streamed. // splitState_.resource = nothing; splitState_.pending = 0; splitState_.commit = 0; splitState_.mode = split_none; // // Disable image streaming if the remote // doesn't support our proxy version. // handleSplitEnable(); // // Number of outstanding tainted replies. // taintCounter_ = 0; #ifdef MEMORY *logofs << "ClientChannel: Created 1 ClientCache and 1 ServerCache. " << "You have 30 seconds to check the allocated size.\n" << logofs_flush; sleep(30); ClientCache *clientCacheTestArray[100]; ServerCache *serverCacheTestArray[100]; for (int i = 0; i < 100; i++) { clientCacheTestArray[i] = new ClientCache(); } *logofs << "ClientChannel: Created further 100 ClientCache. " << "You have 30 seconds to check the allocated size.\n" << logofs_flush; sleep(30); for (int i = 0; i < 100; i++) { serverCacheTestArray[i] = new ServerCache(); } *logofs << "ClientChannel: Created further 100 ServerCache. " << "You have 30 seconds to check the allocated size.\n" << logofs_flush; sleep(30); for (int i = 0; i < 100; i++) { delete clientCacheTestArray[i]; delete serverCacheTestArray[i]; } *logofs << "ClientChannel: Deleted 100 ClientCache and 100 ServerCache. " << "You have 30 seconds to check the allocated size.\n" << logofs_flush; sleep(30); #endif #ifdef REFERENCES *logofs << "ClientChannel: Created new object at " << this << " for FD#" << fd_ << " out of " << ++references_ << " allocated channels.\n" << logofs_flush; #endif } ClientChannel::~ClientChannel() { #ifdef REFERENCES *logofs << "ClientChannel: Deleted object at " << this << " for FD#" << fd_ << " out of " << --references_ << " allocated channels.\n" << logofs_flush; #endif } // // Beginning of handleRead(). // int ClientChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, unsigned int length) { #ifdef TEST *logofs << "handleRead: Called for FD#" << fd_ << " with " << encodeBuffer.getLength() << " bytes already encoded.\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; // // Check if we can borrow the buffer // from the caller. // if (message != NULL && length != 0) { readBuffer_.readMessage(message, length); } else { // // Get the data from the transport. // #if defined(TEST) || defined(INFO) *logofs << "handleRead: Trying to read from FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif int result = readBuffer_.readMessage(); #ifdef DEBUG *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) *logofs << "handleRead: PANIC! No data read from FD#" << fd_ << " while encoding messages.\n" << logofs_flush; HandleCleanup(); #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 (firstRequest_) { // // Need to add the length of the first // request as it was not present in // previous versions. // if (control -> isProtoStep7() == 1) { encodeBuffer.encodeValue(inputLength, 8); } for (unsigned int i = 0; i < inputLength; i++) { encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8); } firstRequest_ = 0; #if defined(TEST) || defined(OPCODES) int bits = encodeBuffer.diffBits(); *logofs << "handleRead: Handled first request. " << inputLength << " bytes in, " << bits << " bits (" << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; #endif priority_++; // // Due to the way the loop was implemented // we can't encode multiple messages if we // are encoding the first request. // if (control -> isProtoStep7() == 0) { if (proxy -> handleAsyncInit() < 0) { return -1; } } } else { // // First of all we get the opcode. // unsigned char inputOpcode = *inputMessage; #if defined(TEST) || defined(INFO) // // This is used to test the synchronous // flush in the parent proxy. // lastRequest_ = inputOpcode; #endif // // Check if the request is supported by the // remote. If not, only handle it locally and // taint the opcode as a X_NoOperation. Also // try to short-circuit some replies at this // side. XSync requests, for example, weight // for half of the total round-trips. // if (handleTaintRequest(inputOpcode, inputMessage, inputLength) < 0) { return -1; } encodeBuffer.encodeOpcodeValue(inputOpcode, clientCache_ -> opcodeCache); // // Update the current sequence. // clientSequence_++; clientSequence_ &= 0xffff; #ifdef DEBUG *logofs << "handleRead: Last client sequence number for FD#" << fd_ << " is " << clientSequence_ << ".\n" << logofs_flush; #endif // // If differential compression is disabled // then use the most simple encoding. // if (control -> LocalDeltaCompression == 0) { int result = handleFastReadRequest(encodeBuffer, inputOpcode, inputMessage, inputLength); if (result < 0) { return -1; } else if (result > 0) { continue; } } // // Go to the message's specific encoding. // switch (inputOpcode) { case X_AllocColor: { encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, clientCache_ -> colormapCache); const unsigned char *nextSrc = inputMessage + 8; unsigned int colorData[3]; for (unsigned int i = 0; i < 3; i++) { unsigned int value = GetUINT(nextSrc, bigEndian_); encodeBuffer.encodeCachedValue(value, 16, *(clientCache_ -> allocColorRGBCache[i]), 4); colorData[i] = value; nextSrc += 2; } sequenceQueue_.push(clientSequence_, inputOpcode, colorData[0], colorData[1], colorData[2]); priority_++; } break; case X_ReparentWindow: { encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> windowCache); encodeBuffer.encodeValue(GetUINT(inputMessage + 12, bigEndian_), 16, 11); encodeBuffer.encodeValue(GetUINT(inputMessage + 14, bigEndian_), 16, 11); } break; case X_ChangeProperty: { MessageStore *messageStore = clientStore_ -> getRequestStore(X_ChangeProperty); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } unsigned char format = inputMessage[16]; encodeBuffer.encodeCachedValue(format, 8, clientCache_ -> changePropertyFormatCache); unsigned int dataLength = GetULONG(inputMessage + 20, bigEndian_); encodeBuffer.encodeValue(dataLength, 32, 6); encodeBuffer.encodeValue(inputMessage[1], 2); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, clientCache_ -> changePropertyPropertyCache, 9); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29, clientCache_ -> changePropertyTypeCache, 9); const unsigned char *nextSrc = inputMessage + 24; if (format == 8) { if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, dataLength); } else { clientCache_ -> changePropertyTextCompressor.reset(); for (unsigned int i = 0; i < dataLength; i++) clientCache_ -> changePropertyTextCompressor. encodeChar(*nextSrc++, encodeBuffer); } } else if (format == 32) { for (unsigned int i = 0; i < dataLength; i++) { encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32, clientCache_ -> changePropertyData32Cache); nextSrc += 4; } } else { for (unsigned int i = 0; i < dataLength; i++) { encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16); nextSrc += 2; } } } break; case X_SendEvent: { // // TODO: This can be improved. In the worst // cases, it appears to provide a poor 1.6:1 // ratio. // MessageStore *messageStore = clientStore_ -> getRequestStore(X_SendEvent); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); unsigned int window = GetULONG(inputMessage + 4, bigEndian_); if (window == 0 || window == 1) { encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeBoolValue(window); } else { encodeBuffer.encodeBoolValue(0); encodeBuffer.encodeXidValue(window, clientCache_ -> windowCache); } encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 32, clientCache_ -> sendEventMaskCache, 9); encodeBuffer.encodeCachedValue(*(inputMessage + 12), 8, clientCache_ -> sendEventCodeCache); encodeBuffer.encodeCachedValue(*(inputMessage + 13), 8, clientCache_ -> sendEventByteDataCache); unsigned int newSeq = GetUINT(inputMessage + 14, bigEndian_); unsigned int diffSeq = newSeq - clientCache_ -> sendEventLastSequence; clientCache_ -> sendEventLastSequence = newSeq; encodeBuffer.encodeValue(diffSeq, 16, 4); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 32, clientCache_ -> sendEventIntDataCache); for (unsigned int i = 20; i < 44; i++) { encodeBuffer.encodeCachedValue((unsigned int) inputMessage[i], 8, clientCache_ -> sendEventEventCache); } } break; case X_ChangeWindowAttributes: { encodeBuffer.encodeValue((inputLength - 12) >> 2, 4); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); unsigned int bitmask = GetULONG(inputMessage + 8, bigEndian_); encodeBuffer.encodeCachedValue(bitmask, 15, clientCache_ -> createWindowBitmaskCache); const unsigned char *nextSrc = inputMessage + 12; unsigned int mask = 0x1; for (unsigned int j = 0; j < 15; j++) { if (bitmask & mask) { encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32, *clientCache_ -> createWindowAttrCache[j]); nextSrc += 4; } mask <<= 1; } } break; case X_ClearArea: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_ClearArea target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_ClearArea target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_ClearArea target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_ClearArea); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); const unsigned char *nextSrc = inputMessage + 8; for (unsigned int i = 0; i < 4; i++) { encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, *clientCache_ -> clearAreaGeomCache[i], 8); nextSrc += 2; } } break; case X_CloseFont: { unsigned int font = GetULONG(inputMessage + 4, bigEndian_); encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5); clientCache_ -> lastFont = font; } break; case X_ConfigureWindow: { MessageStore *messageStore = clientStore_ -> getRequestStore(X_ConfigureWindow); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); unsigned int bitmask = GetUINT(inputMessage + 8, bigEndian_); encodeBuffer.encodeCachedValue(bitmask, 7, clientCache_ -> configureWindowBitmaskCache); unsigned int mask = 0x1; const unsigned char *nextSrc = inputMessage + 12; for (unsigned int i = 0; i < 7; i++) { if (bitmask & mask) { encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), CONFIGUREWINDOW_FIELD_WIDTH[i], *clientCache_ -> configureWindowAttrCache[i], 8); nextSrc += 4; } mask <<= 1; } } break; case X_ConvertSelection: { encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, clientCache_ -> convertSelectionRequestorCache, 9); const unsigned char* nextSrc = inputMessage + 8; for (unsigned int i = 0; i < 3; i++) { encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, *(clientCache_ -> convertSelectionAtomCache[i]), 9); nextSrc += 4; } unsigned int timestamp = GetULONG(nextSrc, bigEndian_); encodeBuffer.encodeValue(timestamp - clientCache_ -> convertSelectionLastTimestamp, 32, 4); clientCache_ -> convertSelectionLastTimestamp = timestamp; } break; case X_CopyArea: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_CopyArea source id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_CopyArea source id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_CopyArea source id " << t_id << " is unrecognized.\n" << logofs_flush; } t_id = GetULONG(inputMessage + 8, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_CopyArea target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_CopyArea target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_CopyArea target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_CopyArea); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 12, bigEndian_), clientCache_ -> gcCache); const unsigned char *nextSrc = inputMessage + 16; for (unsigned int i = 0; i < 6; i++) { encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, *clientCache_ -> copyAreaGeomCache[i], 8); nextSrc += 2; } } break; case X_CopyGC: { #ifdef TARGETS unsigned int s_g_id = GetULONG(inputMessage + 4, bigEndian_); unsigned int d_g_id = GetULONG(inputMessage + 8, bigEndian_); *logofs << "handleRead: X_CopyGC source gcontext id is " << s_g_id << " destination gcontext id is " << d_g_id << ".\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> gcCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 23, clientCache_ -> createGCBitmaskCache); } break; case X_CopyPlane: { encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 12, bigEndian_), clientCache_ -> gcCache); const unsigned char *nextSrc = inputMessage + 16; for (unsigned int i = 0; i < 6; i++) { encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, *clientCache_ -> copyPlaneGeomCache[i], 8); nextSrc += 2; } encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 28, bigEndian_), 32, clientCache_ -> copyPlaneBitPlaneCache, 10); } break; case X_CreateGC: { #ifdef TARGETS unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_); unsigned int t_id = GetULONG(inputMessage + 8, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_CreateGC id " << g_id << " target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_CreateGC id " << g_id << " target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_CreateGC id " << g_id << " target id is unrecognized.\n" << logofs_flush; } gcontexts.insert(T_gcontexts::value_type(g_id, t_id)); #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_CreateGC); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } if (control -> isProtoStep7() == 1) { encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> lastId, clientCache_ -> lastIdCache, clientCache_ -> gcCache, clientCache_ -> freeGCCache); } else { encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> gcCache); } const unsigned char *nextSrc = inputMessage + 8; encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> drawableCache); nextSrc += 4; unsigned int bitmask = GetULONG(nextSrc, bigEndian_); nextSrc += 4; encodeBuffer.encodeCachedValue(bitmask, 23, clientCache_ -> createGCBitmaskCache); unsigned int mask = 0x1; for (unsigned int i = 0; i < 23; i++) { if (bitmask & mask) { unsigned int value = GetULONG(nextSrc, bigEndian_); nextSrc += 4; unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i]; if (fieldWidth <= 4) { encodeBuffer.encodeValue(value, fieldWidth); } else { encodeBuffer.encodeCachedValue(value, fieldWidth, *clientCache_ -> createGCAttrCache[i]); } } mask <<= 1; } } break; case X_ChangeGC: { #ifdef TARGETS unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_); T_gcontexts::iterator i = gcontexts.find(g_id); if (i != gcontexts.end()) { unsigned int t_id = i -> second; if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_ChangeGC gcontext id is " << g_id << " target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_ChangeGC gcontext id is " << g_id << " target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_ChangeGC gcontext is " << g_id << " target id is unrecognized.\n" << logofs_flush; } } else { *logofs << "handleRead: X_ChangeGC gcontext id " << g_id << " is unrecognized.\n" << logofs_flush; } gcontexts.erase(g_id); #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_ChangeGC); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> gcCache); const unsigned char *nextSrc = inputMessage + 8; unsigned int bitmask = GetULONG(nextSrc, bigEndian_); nextSrc += 4; encodeBuffer.encodeCachedValue(bitmask, 23, clientCache_ -> createGCBitmaskCache); unsigned int mask = 0x1; for (unsigned int i = 0; i < 23; i++) { if (bitmask & mask) { unsigned int value = GetULONG(nextSrc, bigEndian_); nextSrc += 4; unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i]; if (fieldWidth <= 4) { encodeBuffer.encodeValue(value, fieldWidth); } else { encodeBuffer.encodeCachedValue(value, fieldWidth, *clientCache_ -> createGCAttrCache[i]); } } mask <<= 1; } } break; case X_CreatePixmap: { #ifdef TARGETS *logofs << "handleRead: X_CreatePixmap depth " << (unsigned) inputMessage[1] << ", pixmap id " << GetULONG(inputMessage + 4, bigEndian_) << ", drawable " << GetULONG(inputMessage + 8, bigEndian_) << ", width " << GetUINT(inputMessage + 12, bigEndian_) << ", height " << GetUINT(inputMessage + 14, bigEndian_) << ", size " << GetUINT(inputMessage + 2, bigEndian_) << 2 << ".\n" << logofs_flush; unsigned int p_id = GetULONG(inputMessage + 4, bigEndian_); unsigned short p_sx = GetUINT(inputMessage + 12, bigEndian_); unsigned short p_sy = GetUINT(inputMessage + 14, bigEndian_); *logofs << "handleRead: X_CreatePixmap id is " << p_id << " width is " << p_sx << " height is " << p_sy << ".\n" << logofs_flush; if (p_sx * p_sy <= 64 * 64) { *logofs << "handleRead: X_CreatePixmap id " << p_id << " of size " << p_sx << "x" << p_sy << "=" << p_sx * p_sy << " will be painted at client side.\n" << logofs_flush; } else { *logofs << "handleRead: X_CreatePixmap id " << p_id << " of size " << p_sx << "x" << p_sy << "=" << p_sx * p_sy << " will be painted at server side.\n" << logofs_flush; } pixmaps.insert(p_id); #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_CreatePixmap); hit = handleEncode(encodeBuffer, clientCache_, messageStore, inputOpcode, inputMessage, inputLength); } break; case X_CreateWindow: { #ifdef TARGETS unsigned int w_id = GetULONG(inputMessage + 4, bigEndian_); *logofs << "handleRead: X_CreateWindow id is " << w_id << ".\n" << logofs_flush; windows.insert(w_id); #endif unsigned bitmask = GetULONG(inputMessage + 28, bigEndian_); encodeBuffer.encodeCachedValue((unsigned int) inputMessage[1], 8, clientCache_ -> depthCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> windowCache); if (control -> isProtoStep7() == 1) { encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> lastId, clientCache_ -> lastIdCache, clientCache_ -> windowCache, clientCache_ -> freeWindowCache); } else { encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); } const unsigned char *nextSrc = inputMessage + 12; for (unsigned int i = 0; i < 6; i++) { encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, *clientCache_ -> createWindowGeomCache[i], 8); nextSrc += 2; } encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 24, bigEndian_), 29, clientCache_ -> visualCache); encodeBuffer.encodeCachedValue(bitmask, 15, clientCache_ -> createWindowBitmaskCache); nextSrc = inputMessage + 32; unsigned int mask = 0x1; for (unsigned int j = 0; j < 15; j++) { if (bitmask & mask) { encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32, *clientCache_ -> createWindowAttrCache[j]); nextSrc += 4; } mask <<= 1; } } break; case X_DeleteProperty: { encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 29, 9); } break; case X_FillPoly: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_FillPoly target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_FillPoly target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_FillPoly target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_FillPoly); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } unsigned int numPoints = ((inputLength - 16) >> 2); if (control -> isProtoStep10() == 1) { encodeBuffer.encodeCachedValue(numPoints, 16, clientCache_ -> fillPolyNumPointsCache, 4); } else { encodeBuffer.encodeCachedValue(numPoints, 14, clientCache_ -> fillPolyNumPointsCache, 4); } encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); encodeBuffer.encodeValue((unsigned int) inputMessage[12], 2); encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]); int relativeCoordMode = (inputMessage[13] != 0); const unsigned char *nextSrc = inputMessage + 16; unsigned int pointIndex = 0; for (unsigned int i = 0; i < numPoints; i++) { if (relativeCoordMode) { encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, *clientCache_ -> fillPolyXRelCache[pointIndex], 8); nextSrc += 2; encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, *clientCache_ -> fillPolyYRelCache[pointIndex], 8); nextSrc += 2; } else { unsigned int x = GetUINT(nextSrc, bigEndian_); nextSrc += 2; unsigned int y = GetUINT(nextSrc, bigEndian_); nextSrc += 2; unsigned int j; for (j = 0; j < 8; j++) if ((x == clientCache_ -> fillPolyRecentX[j]) && (y == clientCache_ -> fillPolyRecentY[j])) break; if (j < 8) { encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeValue(j, 3); } else { encodeBuffer.encodeBoolValue(0); encodeBuffer.encodeCachedValue(x, 16, *clientCache_ -> fillPolyXAbsCache[pointIndex], 8); encodeBuffer.encodeCachedValue(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; } } if (++pointIndex == 10) pointIndex = 0; } } break; case X_FreeColors: { unsigned int numPixels = GetUINT(inputMessage + 2, bigEndian_) - 3; encodeBuffer.encodeValue(numPixels, 16, 4); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, clientCache_ -> colormapCache); encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 32, 4); const unsigned char *nextSrc = inputMessage + 12; while (numPixels) { encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 8); nextSrc += 4; numPixels--; } } break; case X_FreeCursor: { encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, clientCache_ -> cursorCache, 9); } break; case X_FreeGC: { #ifdef TARGETS unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_); T_gcontexts::iterator i = gcontexts.find(g_id); if (i != gcontexts.end()) { unsigned int t_id = i -> second; if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_FreeGC gcontext id is " << g_id << " target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_FreeGC gcontext id is " << g_id << " target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_FreeGC gcontext id is " << g_id << " target id is unrecognized.\n" << logofs_flush; } } else { *logofs << "handleRead: X_FreeGC gcontext id " << g_id << " is unrecognized.\n" << logofs_flush; } gcontexts.erase(g_id); #endif if (control -> isProtoStep7() == 1) { encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> freeGCCache); } else { encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> gcCache); } } break; case X_FreePixmap: { #ifdef TARGETS unsigned int p_id = GetULONG(inputMessage + 4, bigEndian_); *logofs << "handleRead: X_FreePixmap id is " << p_id << ".\n" << logofs_flush; pixmaps.erase(p_id); #endif if (control -> isProtoStep7() == 1) { encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> freeDrawableCache); } else { unsigned int pixmap = GetULONG(inputMessage + 4, bigEndian_); unsigned int diff = pixmap - clientCache_ -> createPixmapLastId; if (diff == 0) { encodeBuffer.encodeBoolValue(1); } else { encodeBuffer.encodeBoolValue(0); clientCache_ -> createPixmapLastId = pixmap; encodeBuffer.encodeValue(diff, 29, 4); } } } break; case X_GetAtomName: { encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 29, 9); sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GetGeometry: { encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GetInputFocus: { sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GetModifierMapping: { sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GetKeyboardMapping: { encodeBuffer.encodeValue((unsigned int) inputMessage[4], 8); encodeBuffer.encodeValue((unsigned int) inputMessage[5], 8); sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GetProperty: { MessageStore *messageStore = clientStore_ -> getRequestStore(X_GetProperty); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { unsigned int property = GetULONG(inputMessage + 8, bigEndian_); sequenceQueue_.push(clientSequence_, inputOpcode, property); priority_++; hit = 1; break; } encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); unsigned int property = GetULONG(inputMessage + 8, bigEndian_); encodeBuffer.encodeValue(property, 29, 9); encodeBuffer.encodeValue(GetULONG(inputMessage + 12, bigEndian_), 29, 9); encodeBuffer.encodeValue(GetULONG(inputMessage + 16, bigEndian_), 32, 2); encodeBuffer.encodeValue(GetULONG(inputMessage + 20, bigEndian_), 32, 8); sequenceQueue_.push(clientSequence_, inputOpcode, property); priority_++; } break; case X_GetSelectionOwner: { encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, clientCache_ -> getSelectionOwnerSelectionCache, 9); sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GrabButton: { encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, clientCache_ -> grabButtonEventMaskCache); encodeBuffer.encodeBoolValue((unsigned int) inputMessage[10]); encodeBuffer.encodeBoolValue((unsigned int) inputMessage[11]); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29, clientCache_ -> grabButtonConfineCache, 9); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 29, clientCache_ -> cursorCache, 9); encodeBuffer.encodeCachedValue(inputMessage[20], 8, clientCache_ -> grabButtonButtonCache); encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 22, bigEndian_), 16, clientCache_ -> grabButtonModifierCache); } break; case X_GrabPointer: { encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, clientCache_ -> grabButtonEventMaskCache); encodeBuffer.encodeBoolValue((unsigned int) inputMessage[10]); encodeBuffer.encodeBoolValue((unsigned int) inputMessage[11]); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29, clientCache_ -> grabButtonConfineCache, 9); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 29, clientCache_ -> cursorCache, 9); unsigned int timestamp = GetULONG(inputMessage + 20, bigEndian_); encodeBuffer.encodeValue(timestamp - clientCache_ -> grabKeyboardLastTimestamp, 32, 4); clientCache_ -> grabKeyboardLastTimestamp = timestamp; sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GrabKeyboard: { encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); unsigned int timestamp = GetULONG(inputMessage + 8, bigEndian_); encodeBuffer.encodeValue(timestamp - clientCache_ -> grabKeyboardLastTimestamp, 32, 4); clientCache_ -> grabKeyboardLastTimestamp = timestamp; encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]); encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]); sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GrabServer: case X_UngrabServer: case X_NoOperation: { } break; case X_PolyText8: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_PolyText8 target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_PolyText8 target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_PolyText8 target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_PolyText8); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); unsigned int x = GetUINT(inputMessage + 12, bigEndian_); int xDiff = x - clientCache_ -> polyTextLastX; clientCache_ -> polyTextLastX = x; encodeBuffer.encodeCachedValue(xDiff, 16, clientCache_ -> polyTextCacheX); unsigned int y = GetUINT(inputMessage + 14, bigEndian_); int yDiff = y - clientCache_ -> polyTextLastY; clientCache_ -> polyTextLastY = y; encodeBuffer.encodeCachedValue(yDiff, 16, clientCache_ -> polyTextCacheY); const unsigned char *end = inputMessage + inputLength - 1; const unsigned char *nextSrc = inputMessage + 16; while (nextSrc < end) { unsigned int textLength = (unsigned int) *nextSrc++; encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeValue(textLength, 8); if (textLength == 255) { encodeBuffer.encodeCachedValue(GetULONG(nextSrc, 1), 29, clientCache_ -> polyTextFontCache); nextSrc += 4; } else { encodeBuffer.encodeCachedValue(*nextSrc++, 8, clientCache_ -> polyTextDeltaCache); if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, textLength); nextSrc += textLength; } else { clientCache_ -> polyTextTextCompressor.reset(); for (unsigned int i = 0; i < textLength; i++) clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); } } } encodeBuffer.encodeBoolValue(0); } break; case X_PolyText16: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_PolyText16 target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_PolyText16 target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_PolyText16 target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_PolyText16); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); unsigned int x = GetUINT(inputMessage + 12, bigEndian_); int xDiff = x - clientCache_ -> polyTextLastX; clientCache_ -> polyTextLastX = x; encodeBuffer.encodeCachedValue(xDiff, 16, clientCache_ -> polyTextCacheX); unsigned int y = GetUINT(inputMessage + 14, bigEndian_); int yDiff = y - clientCache_ -> polyTextLastY; clientCache_ -> polyTextLastY = y; encodeBuffer.encodeCachedValue(yDiff, 16, clientCache_ -> polyTextCacheY); const unsigned char *end = inputMessage + inputLength - 1; const unsigned char *nextSrc = inputMessage + 16; while (nextSrc < end) { unsigned int textLength = (unsigned int) *nextSrc++; encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeValue(textLength, 8); if (textLength == 255) { encodeBuffer.encodeCachedValue(GetULONG(nextSrc, 1), 29, clientCache_ -> polyTextFontCache); nextSrc += 4; } else { encodeBuffer.encodeCachedValue(*nextSrc++, 8, clientCache_ -> polyTextDeltaCache); if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, textLength * 2); nextSrc += textLength * 2; } else { clientCache_ -> polyTextTextCompressor.reset(); for (unsigned int i = 0; i < textLength * 2; i++) clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); } } } encodeBuffer.encodeBoolValue(0); } break; case X_ImageText8: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_ImageText8 target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_ImageText8 target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_ImageText8 target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_ImageText8); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } unsigned int textLength = (unsigned int) inputMessage[1]; encodeBuffer.encodeCachedValue(textLength, 8, clientCache_ -> imageTextLengthCache, 4); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); unsigned int x = GetUINT(inputMessage + 12, bigEndian_); int xDiff = x - clientCache_ -> imageTextLastX; clientCache_ -> imageTextLastX = x; encodeBuffer.encodeCachedValue(xDiff, 16, clientCache_ -> imageTextCacheX); unsigned int y = GetUINT(inputMessage + 14, bigEndian_); int yDiff = y - clientCache_ -> imageTextLastY; clientCache_ -> imageTextLastY = y; encodeBuffer.encodeCachedValue(yDiff, 16, clientCache_ -> imageTextCacheY); const unsigned char *nextSrc = inputMessage + 16; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, textLength); } else { clientCache_ -> imageTextTextCompressor.reset(); for (unsigned int j = 0; j < textLength; j++) clientCache_ -> imageTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); } } break; case X_ImageText16: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_ImageText16 target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_ImageText16 target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_ImageText16 target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_ImageText16); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } unsigned int textLength = (unsigned int) inputMessage[1]; encodeBuffer.encodeCachedValue(textLength, 8, clientCache_ -> imageTextLengthCache, 4); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); unsigned int x = GetUINT(inputMessage + 12, bigEndian_); int xDiff = x - clientCache_ -> imageTextLastX; clientCache_ -> imageTextLastX = x; encodeBuffer.encodeCachedValue(xDiff, 16, clientCache_ -> imageTextCacheX); unsigned int y = GetUINT(inputMessage + 14, bigEndian_); int yDiff = y - clientCache_ -> imageTextLastY; clientCache_ -> imageTextLastY = y; encodeBuffer.encodeCachedValue(yDiff, 16, clientCache_ -> imageTextCacheY); const unsigned char *nextSrc = inputMessage + 16; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, textLength * 2); } else { clientCache_ -> imageTextTextCompressor.reset(); for (unsigned int j = 0; j < textLength * 2; j++) clientCache_ -> imageTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); } } break; case X_InternAtom: { MessageStore *messageStore = clientStore_ -> getRequestStore(X_InternAtom); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { sequenceQueue_.push(clientSequence_, inputOpcode); // // Set the priority, also if doing so will // penalize all the well written clients // using XInternAtoms() to pipeline multi- // ple replies. // priority_++; hit = 1; break; } unsigned int nameLength = GetUINT(inputMessage + 4, bigEndian_); encodeBuffer.encodeValue(nameLength, 16, 6); encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); const unsigned char *nextSrc = inputMessage + 8; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, nameLength); } else { clientCache_ -> internAtomTextCompressor.reset(); for (unsigned int i = 0; i < nameLength; i++) { clientCache_ -> internAtomTextCompressor.encodeChar(*nextSrc++, encodeBuffer); } } sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_ListExtensions: { sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_ListFonts: { unsigned int textLength = GetUINT(inputMessage + 6, bigEndian_); encodeBuffer.encodeValue(textLength, 16, 6); encodeBuffer.encodeValue(GetUINT(inputMessage + 4, bigEndian_), 16, 6); const unsigned char* nextSrc = inputMessage + 8; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, textLength); } else { clientCache_ -> polyTextTextCompressor.reset(); for (unsigned int i = 0; i < textLength; i++) { clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); } } sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_LookupColor: case X_AllocNamedColor: { unsigned int textLength = GetUINT(inputMessage + 8, bigEndian_); encodeBuffer.encodeValue(textLength, 16, 6); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, clientCache_ -> colormapCache); const unsigned char *nextSrc = inputMessage + 12; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, textLength); } else { clientCache_ -> polyTextTextCompressor.reset(); for (unsigned int i = 0; i < textLength; i++) { clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); } } sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } 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: { #ifdef TARGETS if (inputOpcode == X_DestroyWindow) { unsigned int w_id = GetULONG(inputMessage + 4, bigEndian_); *logofs << "handleRead: X_DestroyWindow id is " << w_id << ".\n" << logofs_flush; windows.erase(w_id); } #endif if (inputOpcode == X_DestroyWindow && control -> isProtoStep7() == 1) { encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> freeWindowCache); } else { encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> windowCache); } if ((inputOpcode == X_QueryPointer) || (inputOpcode == X_GetWindowAttributes) || (inputOpcode == X_QueryTree)) { sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } } break; case X_OpenFont: { unsigned int nameLength = GetUINT(inputMessage + 8, bigEndian_); encodeBuffer.encodeValue(nameLength, 16, 7); unsigned int font = GetULONG(inputMessage + 4, bigEndian_); encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5); clientCache_ -> lastFont = font; const unsigned char *nextSrc = inputMessage + 12; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(nextSrc, nameLength); } else { clientCache_ -> openFontTextCompressor.reset(); for (; nameLength; nameLength--) { clientCache_ -> openFontTextCompressor. encodeChar(*nextSrc++, encodeBuffer); } } } break; case X_PolyFillRectangle: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_PolyFillRectangle target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_PolyFillRectangle target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_PolyFillRectangle target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_PolyFillRectangle); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); unsigned int index = 0; unsigned int lastX = 0, lastY = 0; unsigned int lastWidth = 0, lastHeight = 0; // // TODO: Could send the size at the beginning // instead of a bool at each iteration. // for (unsigned int i = 12; i < inputLength;) { unsigned int x = GetUINT(inputMessage + i, bigEndian_); unsigned int newX = x; x -= lastX; lastX = newX; encodeBuffer.encodeCachedValue(x, 16, *clientCache_ -> polyFillRectangleCacheX[index], 8); i += 2; unsigned int y = GetUINT(inputMessage + i, bigEndian_); unsigned int newY = y; y -= lastY; lastY = newY; encodeBuffer.encodeCachedValue(y, 16, *clientCache_ -> polyFillRectangleCacheY[index], 8); i += 2; unsigned int width = GetUINT(inputMessage + i, bigEndian_); unsigned int newWidth = width; width -= lastWidth; lastWidth = newWidth; encodeBuffer.encodeCachedValue(width, 16, *clientCache_ -> polyFillRectangleCacheWidth[index], 8); i += 2; unsigned int height = GetUINT(inputMessage + i, bigEndian_); unsigned int newHeight = height; height -= lastHeight; lastHeight = newHeight; encodeBuffer.encodeCachedValue(height, 16, *clientCache_ -> polyFillRectangleCacheHeight[index], 8); i += 2; if (++index == 4) index = 0; encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0); } } break; case X_PolyFillArc: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_PolyFillArc target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_PolyFillArc target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_PolyFillArc target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_PolyFillArc); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); unsigned int index = 0; unsigned int lastX = 0, lastY = 0; unsigned int lastWidth = 0, lastHeight = 0; unsigned int lastAngle1 = 0, lastAngle2 = 0; // // TODO: Could send the size at the beginning // instead of a bool at each iteration. // for (unsigned int i = 12; i < inputLength;) { unsigned int x = GetUINT(inputMessage + i, bigEndian_); unsigned int newX = x; x -= lastX; lastX = newX; encodeBuffer.encodeCachedValue(x, 16, *clientCache_ -> polyFillArcCacheX[index], 8); i += 2; unsigned int y = GetUINT(inputMessage + i, bigEndian_); unsigned int newY = y; y -= lastY; lastY = newY; encodeBuffer.encodeCachedValue(y, 16, *clientCache_ -> polyFillArcCacheY[index], 8); i += 2; unsigned int width = GetUINT(inputMessage + i, bigEndian_); unsigned int newWidth = width; width -= lastWidth; lastWidth = newWidth; encodeBuffer.encodeCachedValue(width, 16, *clientCache_ -> polyFillArcCacheWidth[index], 8); i += 2; unsigned int height = GetUINT(inputMessage + i, bigEndian_); unsigned int newHeight = height; height -= lastHeight; lastHeight = newHeight; encodeBuffer.encodeCachedValue(height, 16, *clientCache_ -> polyFillArcCacheHeight[index], 8); i += 2; unsigned int angle1 = GetUINT(inputMessage + i, bigEndian_); unsigned int newAngle1 = angle1; angle1 -= lastAngle1; lastAngle1 = newAngle1; encodeBuffer.encodeCachedValue(angle1, 16, *clientCache_ -> polyFillArcCacheAngle1[index], 8); i += 2; unsigned int angle2 = GetUINT(inputMessage + i, bigEndian_); unsigned int newAngle2 = angle2; angle2 -= lastAngle2; lastAngle2 = newAngle2; encodeBuffer.encodeCachedValue(angle2, 16, *clientCache_ -> polyFillArcCacheAngle2[index], 8); i += 2; if (++index == 2) index = 0; encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0); } } break; case X_PolyArc: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_PolyArc target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_PolyArc target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_PolyArc target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_PolyArc); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); unsigned int index = 0; unsigned int lastX = 0, lastY = 0; unsigned int lastWidth = 0, lastHeight = 0; unsigned int lastAngle1 = 0, lastAngle2 = 0; // // TODO: Could send the size at the beginning // instead of a bool at each iteration. // for (unsigned int i = 12; i < inputLength;) { unsigned int x = GetUINT(inputMessage + i, bigEndian_); unsigned int newX = x; x -= lastX; lastX = newX; encodeBuffer.encodeCachedValue(x, 16, *clientCache_ -> polyArcCacheX[index], 8); i += 2; unsigned int y = GetUINT(inputMessage + i, bigEndian_); unsigned int newY = y; y -= lastY; lastY = newY; encodeBuffer.encodeCachedValue(y, 16, *clientCache_ -> polyArcCacheY[index], 8); i += 2; unsigned int width = GetUINT(inputMessage + i, bigEndian_); unsigned int newWidth = width; width -= lastWidth; lastWidth = newWidth; encodeBuffer.encodeCachedValue(width, 16, *clientCache_ -> polyArcCacheWidth[index], 8); i += 2; unsigned int height = GetUINT(inputMessage + i, bigEndian_); unsigned int newHeight = height; height -= lastHeight; lastHeight = newHeight; encodeBuffer.encodeCachedValue(height, 16, *clientCache_ -> polyArcCacheHeight[index], 8); i += 2; unsigned int angle1 = GetUINT(inputMessage + i, bigEndian_); unsigned int newAngle1 = angle1; angle1 -= lastAngle1; lastAngle1 = newAngle1; encodeBuffer.encodeCachedValue(angle1, 16, *clientCache_ -> polyArcCacheAngle1[index], 8); i += 2; unsigned int angle2 = GetUINT(inputMessage + i, bigEndian_); unsigned int newAngle2 = angle2; angle2 -= lastAngle2; lastAngle2 = newAngle2; encodeBuffer.encodeCachedValue(angle2, 16, *clientCache_ -> polyArcCacheAngle2[index], 8); i += 2; if (++index == 2) index = 0; encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0); } } break; case X_PolyPoint: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_PolyPoint target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_PolyPoint target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_PolyPoint target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_PolyPoint); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_) - 3, 16, 4); encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); const unsigned char *nextSrc = inputMessage + 12; unsigned int index = 0; unsigned int lastX = 0, lastY = 0; for (unsigned int i = 12; i < inputLength; i += 4) { unsigned int x = GetUINT(nextSrc, bigEndian_); nextSrc += 2; unsigned int tmp = x; x -= lastX; lastX = tmp; encodeBuffer.encodeCachedValue(x, 16, *clientCache_ -> polyPointCacheX[index], 8); unsigned int y = GetUINT(nextSrc, bigEndian_); nextSrc += 2; tmp = y; y -= lastY; lastY = tmp; encodeBuffer.encodeCachedValue(y, 16, *clientCache_ -> polyPointCacheY[index], 8); if (++index == 2) index = 0; } } break; case X_PolyLine: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_PolyLine target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_PolyLine target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_PolyLine target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_PolyLine); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_) - 3, 16, 4); encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); const unsigned char *nextSrc = inputMessage + 12; unsigned int index = 0; unsigned int lastX = 0, lastY = 0; for (unsigned int i = 12; i < inputLength; i += 4) { unsigned int x = GetUINT(nextSrc, bigEndian_); nextSrc += 2; unsigned int tmp = x; x -= lastX; lastX = tmp; encodeBuffer.encodeCachedValue(x, 16, *clientCache_ -> polyLineCacheX[index], 8); unsigned int y = GetUINT(nextSrc, bigEndian_); nextSrc += 2; tmp = y; y -= lastY; lastY = tmp; encodeBuffer.encodeCachedValue(y, 16, *clientCache_ -> polyLineCacheY[index], 8); if (++index == 2) index = 0; } } break; case X_PolyRectangle: { encodeBuffer.encodeValue((GetUINT(inputMessage + 2, bigEndian_) - 3) >> 1, 16, 3); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); const unsigned char *end = inputMessage + inputLength; const unsigned char *nextSrc = inputMessage + 12; while (nextSrc < end) { for (unsigned int i = 0; i < 4; i++) { encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, *clientCache_ -> polyRectangleGeomCache[i], 8); nextSrc += 2; } } } break; case X_PolySegment: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_PolySegment target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_PolySegment target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_PolySegment target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_PolySegment); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } encodeBuffer.encodeValue((GetUINT(inputMessage + 2, bigEndian_) - 3) >> 1, 16, 4); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), clientCache_ -> gcCache); const unsigned char *end = inputMessage + inputLength; const unsigned char *nextSrc = inputMessage + 12; // unsigned int index = 0; // unsigned int lastX1, lastY1, lastX2, lastY2; while (nextSrc < end) { unsigned int x = GetUINT(nextSrc, bigEndian_); nextSrc += 2; unsigned int xDiff0 = x - clientCache_ -> polySegmentLastX[0]; unsigned int xDiff1 = x - clientCache_ -> polySegmentLastX[1]; int xDiff0Abs = (int) xDiff0; if (xDiff0Abs < 0) xDiff0Abs = -xDiff0Abs; int xDiff1Abs = (int) xDiff1; if (xDiff1Abs < 0) xDiff1Abs = -xDiff1Abs; unsigned int y = GetUINT(nextSrc, bigEndian_); nextSrc += 2; unsigned int yDiff0 = y - clientCache_ -> polySegmentLastY[0]; unsigned int yDiff1 = y - clientCache_ -> polySegmentLastY[1]; int yDiff0Abs = (int) yDiff0; if (yDiff0Abs < 0) yDiff0Abs = -yDiff0Abs; int yDiff1Abs = (int) yDiff1; if (yDiff1Abs < 0) yDiff1Abs = -yDiff1Abs; int diff0 = xDiff0Abs + yDiff0Abs; int diff1 = xDiff1Abs + yDiff1Abs; if (diff0 < diff1) { encodeBuffer.encodeBoolValue(0); encodeBuffer.encodeCachedValue(xDiff0, 16, clientCache_ -> polySegmentCacheX, 6); encodeBuffer.encodeCachedValue(yDiff0, 16, clientCache_ -> polySegmentCacheY, 6); } else { encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeCachedValue(xDiff1, 16, clientCache_ -> polySegmentCacheX, 6); encodeBuffer.encodeCachedValue(yDiff1, 16, clientCache_ -> polySegmentCacheY, 6); } clientCache_ -> polySegmentLastX[clientCache_ -> polySegmentCacheIndex] = x; clientCache_ -> polySegmentLastY[clientCache_ -> polySegmentCacheIndex] = y; clientCache_ -> polySegmentCacheIndex = clientCache_ -> polySegmentCacheIndex == 1 ? 0 : 1; } } break; case X_PutImage: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_PutImage target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_PutImage target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_PutImage target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_PutImage); hit = handleEncode(encodeBuffer, clientCache_, messageStore, inputOpcode, inputMessage, inputLength); } break; case X_QueryBestSize: { encodeBuffer.encodeValue((unsigned int)inputMessage[1], 2); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); encodeBuffer.encodeValue(GetUINT(inputMessage + 8, bigEndian_), 16, 8); encodeBuffer.encodeValue(GetUINT(inputMessage + 10, bigEndian_), 16, 8); sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_QueryColors: { // Differential encoding. encodeBuffer.encodeBoolValue(1); unsigned int numColors = ((inputLength - 8) >> 2); encodeBuffer.encodeValue(numColors, 16, 5); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, clientCache_ -> colormapCache); const unsigned char *nextSrc = inputMessage + 8; unsigned int predictedPixel = clientCache_ -> queryColorsLastPixel; for (unsigned int i = 0; i < numColors; i++) { unsigned int pixel = GetULONG(nextSrc, bigEndian_); nextSrc += 4; if (pixel == predictedPixel) encodeBuffer.encodeBoolValue(1); else { encodeBuffer.encodeBoolValue(0); encodeBuffer.encodeValue(pixel, 32, 9); } if (i == 0) clientCache_ -> queryColorsLastPixel = pixel; predictedPixel = pixel + 1; } sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_QueryExtension: { #ifdef TEST char data[256]; int length = GetUINT(inputMessage + 4, bigEndian_); if (length > 256) { length = 256; } strncpy(data, (char *) inputMessage + 8, length); *(data + length) = '\0'; *logofs << "handleRead: Going to query extension '" << data << "' for FD#" << fd_ << ".\n" << logofs_flush; #endif unsigned int nameLength = GetUINT(inputMessage + 4, bigEndian_); encodeBuffer.encodeValue(nameLength, 16, 6); const unsigned char *nextSrc = inputMessage + 8; for (; nameLength; nameLength--) { encodeBuffer.encodeValue((unsigned int) *nextSrc++, 8); } unsigned int extension = 0; if (strncmp((char *) inputMessage + 8, "SHAPE", 5) == 0) { extension = X_NXInternalShapeExtension; } else if (strncmp((char *) inputMessage + 8, "RENDER", 6) == 0) { extension = X_NXInternalRenderExtension; } sequenceQueue_.push(clientSequence_, inputOpcode, extension); priority_++; } break; case X_QueryFont: { unsigned int font = GetULONG(inputMessage + 4, bigEndian_); encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5); clientCache_ -> lastFont = font; sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_SetClipRectangles: { MessageStore *messageStore = clientStore_ -> getRequestStore(X_SetClipRectangles); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { hit = 1; break; } unsigned int numRectangles = ((inputLength - 12) >> 3); if (control -> isProtoStep9() == 1) { encodeBuffer.encodeValue(numRectangles, 15, 4); } else { encodeBuffer.encodeValue(numRectangles, 13, 4); } encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> gcCache); encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, clientCache_ -> setClipRectanglesXCache, 8); encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 10, bigEndian_), 16, clientCache_ -> setClipRectanglesYCache, 8); const unsigned char *nextSrc = inputMessage + 12; for (unsigned int i = 0; i < numRectangles; i++) { for (unsigned int j = 0; j < 4; j++) { encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, *clientCache_ -> setClipRectanglesGeomCache[j], 8); nextSrc += 2; } } } break; case X_SetDashes: { unsigned int numDashes = GetUINT(inputMessage + 10, bigEndian_); encodeBuffer.encodeCachedValue(numDashes, 16, clientCache_ -> setDashesLengthCache, 5); encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> gcCache); encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, clientCache_ -> setDashesOffsetCache, 5); const unsigned char *nextSrc = inputMessage + 12; for (unsigned int i = 0; i < numDashes; i++) encodeBuffer.encodeCachedValue(*nextSrc++, 8, clientCache_ -> setDashesDashCache_[i & 1], 5); } break; case X_SetSelectionOwner: { encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, clientCache_ -> setSelectionOwnerCache, 9); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, clientCache_ -> getSelectionOwnerSelectionCache, 9); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 32, clientCache_ -> setSelectionOwnerTimestampCache, 9); } break; case X_TranslateCoords: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_TranslateCoords source id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_TranslateCoords source id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_TranslateCoords source id " << t_id << " is unrecognized.\n" << logofs_flush; } t_id = GetULONG(inputMessage + 8, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_TranslateCoords target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_TranslateCoords target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_TranslateCoords target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_TranslateCoords); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; hit = 1; break; } encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, clientCache_ -> translateCoordsSrcCache, 9); encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, clientCache_ -> translateCoordsDstCache, 9); encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), 16, clientCache_ -> translateCoordsXCache, 8); encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 14, bigEndian_), 16, clientCache_ -> translateCoordsYCache, 8); sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GetImage: { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_GetImage source id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_GetImage source id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_GetImage source id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_GetImage); if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, inputMessage, inputLength)) { sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; hit = 1; break; } // Format. encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2); // Drawable. encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), clientCache_ -> drawableCache); // X. unsigned int x = GetUINT(inputMessage + 8, bigEndian_); int xDiff = x - clientCache_ -> putImageLastX; clientCache_ -> putImageLastX = x; encodeBuffer.encodeCachedValue(xDiff, 16, clientCache_ -> putImageXCache, 8); // Y. unsigned int y = GetUINT(inputMessage + 10, bigEndian_); int yDiff = y - clientCache_ -> putImageLastY; clientCache_ -> putImageLastY = y; encodeBuffer.encodeCachedValue(yDiff, 16, clientCache_ -> putImageYCache, 8); // Width. unsigned int width = GetUINT(inputMessage + 12, bigEndian_); encodeBuffer.encodeCachedValue(width, 16, clientCache_ -> putImageWidthCache, 8); // Height. unsigned int height = GetUINT(inputMessage + 14, bigEndian_); encodeBuffer.encodeCachedValue(height, 16, clientCache_ -> putImageHeightCache, 8); // Plane mask. encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 32, clientCache_ -> getImagePlaneMaskCache, 5); sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GetPointerMapping: { sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; case X_GetKeyboardControl: { sequenceQueue_.push(clientSequence_, inputOpcode); priority_++; } break; default: { if (inputOpcode == opcodeStore_ -> renderExtension) { MessageStore *messageStore = clientStore_ -> getRequestStore(X_NXInternalRenderExtension); hit = handleEncode(encodeBuffer, clientCache_, messageStore, inputOpcode, inputMessage, inputLength); } else if (inputOpcode == opcodeStore_ -> shapeExtension) { MessageStore *messageStore = clientStore_ -> getRequestStore(X_NXInternalShapeExtension); hit = handleEncode(encodeBuffer, clientCache_, messageStore, inputOpcode, inputMessage, inputLength); } else if (inputOpcode == opcodeStore_ -> putPackedImage) { #ifdef TARGETS unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); if (pixmaps.find(t_id) != pixmaps.end()) { *logofs << "handleRead: X_NXPutPackedImage target id is pixmap " << t_id << ".\n" << logofs_flush; } else if (windows.find(t_id) != windows.end()) { *logofs << "handleRead: X_NXPutPackedImage target id is window " << t_id << ".\n" << logofs_flush; } else { *logofs << "handleRead: X_NXPutPackedImage target id " << t_id << " is unrecognized.\n" << logofs_flush; } #endif #ifdef DEBUG *logofs << "handleRead: Encoding packed image request for FD#" << fd_ << ".\n" << logofs_flush; #endif // // The field carries the destination data // length. We add the request's size of // the final X_PutImage. // unsigned int outputLength = GetULONG(inputMessage + 20, bigEndian_) + 24; statistics -> addPackedBytesIn(inputLength); statistics -> addPackedBytesOut(outputLength); MessageStore *messageStore = clientStore_ -> getRequestStore(X_NXPutPackedImage); hit = handleEncode(encodeBuffer, clientCache_, messageStore, inputOpcode, inputMessage, inputLength); } else if (inputOpcode == opcodeStore_ -> setUnpackColormap) { #ifdef DEBUG *logofs << "handleRead: Encoding set unpack colormap request " << "for FD#" << fd_ << " with size " << inputLength << ".\n" << logofs_flush; #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_NXSetUnpackColormap); hit = handleEncode(encodeBuffer, clientCache_, messageStore, inputOpcode, inputMessage, inputLength); } else if (inputOpcode == opcodeStore_ -> setUnpackAlpha) { #ifdef DEBUG *logofs << "handleRead: Encoding set unpack alpha request " << "for FD#" << fd_ << " with size " << inputLength << ".\n" << logofs_flush; #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_NXSetUnpackAlpha); hit = handleEncode(encodeBuffer, clientCache_, messageStore, inputOpcode, inputMessage, inputLength); } else if (inputOpcode == opcodeStore_ -> setUnpackGeometry) { #ifdef DEBUG *logofs << "handleRead: Encoding set unpack geometry request " << "for FD#" << fd_ << " with size " << inputLength << ".\n" << logofs_flush; #endif MessageStore *messageStore = clientStore_ -> getRequestStore(X_NXSetUnpackGeometry); hit = handleEncode(encodeBuffer, clientCache_, messageStore, inputOpcode, inputMessage, inputLength); } else if (inputOpcode == opcodeStore_ -> startSplit) { if (handleStartSplitRequest(encodeBuffer, inputOpcode, inputMessage, inputLength) < 0) { return -1; } } else if (inputOpcode == opcodeStore_ -> endSplit) { if (handleEndSplitRequest(encodeBuffer, inputOpcode, inputMessage, inputLength) < 0) { return -1; } } else if (inputOpcode == opcodeStore_ -> commitSplit) { if (handleCommitSplitRequest(encodeBuffer, inputOpcode, inputMessage, inputLength) < 0) { return -1; } } else if (inputOpcode == opcodeStore_ -> abortSplit) { if (handleAbortSplitRequest(encodeBuffer, inputOpcode, inputMessage, inputLength) < 0) { return -1; } } else if (inputOpcode == opcodeStore_ -> finishSplit) { if (handleFinishSplitRequest(encodeBuffer, inputOpcode, inputMessage, inputLength) < 0) { return -1; } } else if (inputOpcode == opcodeStore_ -> freeSplit) { #ifdef DEBUG *logofs << "handleRead: Encoding free split request " << "for FD#" << fd_ << " with size " << inputLength << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8, clientCache_ -> resourceCache); } else if (inputOpcode == opcodeStore_ -> freeUnpack) { #ifdef DEBUG *logofs << "handleRead: Encoding free unpack request " << "for FD#" << fd_ << " with size " << inputLength << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8, clientCache_ -> resourceCache); } else if (inputOpcode == opcodeStore_ -> getControlParameters) { #ifdef DEBUG *logofs << "handleRead: Encoding get control parameters " << "request for FD#" << fd_ << " with size " << inputLength << ".\n" << logofs_flush; #endif // // Add the reply to the write buffer. If found // to contain a message, it it will be flushed // to the X client before leaving the loop. // unsigned char *reply = writeBuffer_.addMessage(32); *(reply + 0) = X_Reply; PutUINT(clientSequence_, reply + 2, bigEndian_); PutULONG(0, reply + 4, bigEndian_); // // Save the sequence number we used // to auto-generate this reply. // lastSequence_ = clientSequence_; #ifdef TEST *logofs << "handleRead: Registered " << lastSequence_ << " as last auto-generated sequence number.\n" << logofs_flush; #endif *(reply + 1) = control -> LinkMode; *(reply + 8) = control -> LocalVersionMajor; *(reply + 9) = control -> LocalVersionMinor; *(reply + 10) = control -> LocalVersionPatch; *(reply + 11) = control -> RemoteVersionMajor; *(reply + 12) = control -> RemoteVersionMinor; *(reply + 13) = control -> RemoteVersionPatch; PutUINT(control -> SplitTimeout, reply + 14, bigEndian_); PutUINT(control -> MotionTimeout, reply + 16, bigEndian_); *(reply + 18) = control -> SplitMode; PutULONG(control -> SplitDataThreshold, reply + 20, bigEndian_); *(reply + 24) = control -> PackMethod; *(reply + 25) = control -> PackQuality; *(reply + 26) = control -> LocalDataCompressionLevel; *(reply + 27) = control -> LocalStreamCompressionLevel; *(reply + 28) = control -> LocalDeltaCompression; *(reply + 29) = (control -> LocalDeltaCompression == 1 && control -> PersistentCacheEnableLoad == 1); *(reply + 30) = (control -> LocalDeltaCompression == 1 && control -> PersistentCacheEnableSave == 1); *(reply + 31) = (control -> LocalDeltaCompression == 1 && control -> PersistentCacheEnableLoad == 1 && control -> PersistentCacheName != NULL); if (handleFlush(flush_if_any) < 0) { return -1; } } else if (inputOpcode == opcodeStore_ -> getCleanupParameters) { #ifdef WARNING *logofs << "handleRead: WARNING! Encoding fake get cleanup " << "parameters request for FD#" << fd_ << " with size " << inputLength << ".\n" << logofs_flush; #endif } else if (inputOpcode == opcodeStore_ -> getImageParameters) { #ifdef WARNING *logofs << "handleRead: WARNING! Encoding fake get cleanup " << "parameters request for FD#" << fd_ << " with size " << inputLength << ".\n" << logofs_flush; #endif } else if (inputOpcode == opcodeStore_ -> getUnpackParameters) { #ifdef DEBUG *logofs << "handleRead: Encoding get unpack parameters " << "request for FD#" << fd_ << " with size " << inputLength << ".\n" << logofs_flush; #endif sequenceQueue_.push(clientSequence_, inputOpcode); } else if (inputOpcode == opcodeStore_ -> getShmemParameters) { if (handleShmemRequest(encodeBuffer, inputOpcode, inputMessage, inputLength) < 0) { return -1; } } else if (inputOpcode == opcodeStore_ -> setExposeParameters) { // // Enable or disable expose events // coming from the real server. // encodeBuffer.encodeBoolValue(*(inputMessage + 4)); encodeBuffer.encodeBoolValue(*(inputMessage + 5)); encodeBuffer.encodeBoolValue(*(inputMessage + 6)); } else if (inputOpcode == opcodeStore_ -> setCacheParameters) { if (handleCacheRequest(encodeBuffer, inputOpcode, inputMessage, inputLength) < 0) { return -1; } } else if (inputOpcode == opcodeStore_ -> getFontParameters) { if (handleFontRequest(encodeBuffer, inputOpcode, inputMessage, inputLength) < 0) { return -1; } } else { MessageStore *messageStore = clientStore_ -> getRequestStore(X_NXInternalGenericRequest); hit = handleEncode(encodeBuffer, clientCache_, messageStore, inputOpcode, inputMessage, inputLength); // // Don't flush if the opcode is unrecognized. // We may optionally flush it is an extension // but would penalize the well written clients. // // if (inputOpcode > 127) // { // priority_++; // } // } } } // End of switch on opcode. int bits = encodeBuffer.diffBits(); #if defined(TEST) || defined(OPCODES) const char *cacheString = (hit ? "cached " : ""); *logofs << "handleRead: Handled " << cacheString << "request OPCODE#" << (unsigned int) inputOpcode << " (" << DumpOpcode(inputOpcode) << ")" << " for FD#" << fd_ << " sequence " << clientSequence_ << ". " << inputLength << " bytes in, " << bits << " bits (" << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; #endif if (hit) { statistics -> addCachedRequest(inputOpcode); } statistics -> addRequestBits(inputOpcode, inputLength << 3, bits); if (inputOpcode == opcodeStore_ -> renderExtension) { if (hit) { statistics -> addRenderCachedRequest(*(inputMessage + 1)); } statistics -> addRenderRequestBits(*(inputMessage + 1), inputLength << 3, bits); } } // End if (firstRequest_)... 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 exceeded the token length. // 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 ClientChannel::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 (firstReply_) { #ifdef TEST *logofs << "handleWrite: First reply detected.\n" << logofs_flush; #endif unsigned int outputOpcode; decodeBuffer.decodeValue(outputOpcode, 8); unsigned int secondByte; decodeBuffer.decodeValue(secondByte, 8); unsigned int major; decodeBuffer.decodeValue(major, 16); unsigned int minor; decodeBuffer.decodeValue(minor, 16); unsigned int extraLength; decodeBuffer.decodeValue(extraLength, 16); unsigned int outputLength = 8 + (extraLength << 2); unsigned char *outputMessage = writeBuffer_.addMessage(outputLength); *outputMessage = (unsigned char) outputOpcode; outputMessage[1] = (unsigned char) secondByte; PutUINT(major, outputMessage + 2, bigEndian_); PutUINT(minor, outputMessage + 4, bigEndian_); PutUINT(extraLength, outputMessage + 6, bigEndian_); unsigned char *nextDest = outputMessage + 8; unsigned int cached; decodeBuffer.decodeBoolValue(cached); if (cached) { memcpy(nextDest, ServerCache::lastInitReply.getData(), outputLength - 8); } else { for (unsigned i = 8; i < outputLength; i++) { unsigned int nextByte; decodeBuffer.decodeValue(nextByte, 8); *nextDest++ = (unsigned char) nextByte; } ServerCache::lastInitReply.set(outputLength - 8, outputMessage + 8); } imageByteOrder_ = outputMessage[30]; bitmapBitOrder_ = outputMessage[31]; scanlineUnit_ = outputMessage[32]; scanlinePad_ = outputMessage[33]; firstReply_ = 0; } // End of if (firstReply_) // // 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. #ifdef DEBUG *logofs << "handleWrite: Starting loop on opcodes.\n" << logofs_flush; #endif unsigned char outputOpcode; // // NX client needs this line to consider // the initialization phase successfully // completed. // if (firstClient_ == -1) { cerr << "Info" << ": Established X client connection.\n" ; firstClient_ = fd_; } while (decodeBuffer.decodeOpcodeValue(outputOpcode, serverCache_ -> opcodeCache, 1)) { #ifdef DEBUG *logofs << "handleWrite: Decoded a new OPCODE#" << (unsigned int) outputOpcode << ".\n" << logofs_flush; #endif unsigned char *outputMessage = NULL; unsigned int outputLength = 0; // // General-purpose temp variables // for decoding ints and chars. // unsigned int value = 0; unsigned char cValue = 0; // // Check first if we need to abort any split, // then if this is a reply, finally if it is // en event or error. // if (outputOpcode == opcodeStore_ -> splitEvent) { // // It's an abort split, not a normal // burst of proxy data. // handleSplitEvent(decodeBuffer); continue; } else if (outputOpcode == X_Reply) { #ifdef DEBUG *logofs << "handleWrite: Decoding sequence number of reply.\n" << logofs_flush; #endif unsigned int sequenceNum; unsigned int sequenceDiff; decodeBuffer.decodeCachedValue(sequenceDiff, 16, serverCache_ -> replySequenceCache, 7); sequenceNum = (serverSequence_ + sequenceDiff) & 0xffff; serverSequence_ = sequenceNum; #ifdef DEBUG *logofs << "handleWrite: Last server sequence number for FD#" << fd_ << " is " << serverSequence_ << " with " << "difference " << sequenceDiff << ".\n" << logofs_flush; #endif // // In case of reply we can follow the X server and // override any event's sequence number generated // by this side. // #ifdef TEST *logofs << "handleWrite: Updating last event's sequence " << lastSequence_ << " to reply's sequence number " << serverSequence_ << " for FD#" << fd_ << ".\n" << logofs_flush; #endif lastSequence_ = serverSequence_; unsigned short int requestSequenceNum; unsigned char requestOpcode; #ifdef DEBUG requestSequenceNum = 0; requestOpcode = 0; *logofs << "handleWrite: Peek of sequence number returns "; *logofs << sequenceQueue_.peek(requestSequenceNum, requestOpcode); *logofs << " with sequence " << requestSequenceNum << " and opcode " << (unsigned int) requestOpcode << ".\n" << logofs_flush; #endif if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) == 1 && (requestSequenceNum == sequenceNum)) { unsigned int requestData[3]; sequenceQueue_.pop(requestSequenceNum, requestOpcode, requestData[0], requestData[1], requestData[2]); #ifdef DEBUG *logofs << "handleWrite: Identified reply to OPCODE#" << (unsigned int) requestOpcode << ".\n" << logofs_flush; #endif // // Is differential encoding disabled? // if (control -> RemoteDeltaCompression == 0) { int result = handleFastWriteReply(decodeBuffer, requestOpcode, outputMessage, outputLength); if (result < 0) { return -1; } else if (result > 0) { continue; } } switch (requestOpcode) { case X_AllocColor: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); unsigned char *nextDest = outputMessage + 8; for (unsigned int i = 0; i < 3; i++) { decodeBuffer.decodeBoolValue(value); if (value) { PutUINT(requestData[i], nextDest, bigEndian_); } else { decodeBuffer.decodeValue(value, 16, 6); PutUINT(requestData[i] + value, nextDest, bigEndian_); } nextDest += 2; } decodeBuffer.decodeValue(value, 32, 9); PutULONG(value, outputMessage + 16, bigEndian_); } break; case X_GetAtomName: { unsigned int nameLength; decodeBuffer.decodeValue(nameLength, 16, 6); outputLength = RoundUp4(nameLength) + 32; outputMessage = writeBuffer_.addMessage(outputLength); PutUINT(nameLength, outputMessage + 8, bigEndian_); unsigned char* nextDest = outputMessage + 32; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, nameLength); } else { serverCache_ -> getAtomNameTextCompressor.reset(); for (unsigned int i = 0; i < nameLength; i++) { *nextDest++ = serverCache_ -> getAtomNameTextCompressor. decodeChar(decodeBuffer); } } } break; case X_GetGeometry: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); decodeBuffer.decodeCachedValue(cValue, 8, serverCache_ -> depthCache); outputMessage[1] = cValue; decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> getGeometryRootCache, 9); PutULONG(value, outputMessage + 8, bigEndian_); unsigned char *nextDest = outputMessage + 12; for (unsigned int i = 0; i < 5; i++) { decodeBuffer.decodeCachedValue(value, 16, *serverCache_ -> getGeometryGeomCache[i], 8); PutUINT(value, nextDest, bigEndian_); nextDest += 2; } } break; case X_GetInputFocus: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); decodeBuffer.decodeValue(value, 2); outputMessage[1] = (unsigned char) value; decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> getInputFocusWindowCache); PutULONG(value, outputMessage + 8, bigEndian_); } break; case X_GetKeyboardMapping: { decodeBuffer.decodeBoolValue(value); if (value) { unsigned int dataLength = ServerCache::getKeyboardMappingLastMap.getLength(); outputLength = 32 + dataLength; outputMessage = writeBuffer_.addMessage(outputLength); outputMessage[1] = ServerCache::getKeyboardMappingLastKeysymsPerKeycode; memcpy(outputMessage + 32, ServerCache::getKeyboardMappingLastMap.getData(), dataLength); break; } unsigned int numKeycodes; decodeBuffer.decodeValue(numKeycodes, 8); unsigned int keysymsPerKeycode; decodeBuffer.decodeValue(keysymsPerKeycode, 8, 4); ServerCache::getKeyboardMappingLastKeysymsPerKeycode = keysymsPerKeycode; outputLength = 32 + numKeycodes * keysymsPerKeycode * 4; outputMessage = writeBuffer_.addMessage(outputLength); outputMessage[1] = (unsigned char) keysymsPerKeycode; unsigned char *nextDest = outputMessage + 32; unsigned char previous = 0; for (unsigned int count = numKeycodes * keysymsPerKeycode; count; --count) { decodeBuffer.decodeBoolValue(value); if (value) PutULONG((unsigned int) NoSymbol, nextDest, bigEndian_); else { unsigned int keysym; decodeBuffer.decodeCachedValue(keysym, 24, serverCache_ -> getKeyboardMappingKeysymCache, 9); decodeBuffer.decodeCachedValue(cValue, 8, serverCache_ -> getKeyboardMappingLastByteCache, 5); previous += cValue; PutULONG((keysym << 8) | previous, nextDest, bigEndian_); } nextDest += 4; } ServerCache::getKeyboardMappingLastMap.set(outputLength - 32, outputMessage + 32); } break; case X_GetModifierMapping: { unsigned int keycodesPerModifier; decodeBuffer.decodeValue(keycodesPerModifier, 8); outputLength = 32 + (keycodesPerModifier << 3); outputMessage = writeBuffer_.addMessage(outputLength); outputMessage[1] = (unsigned char) keycodesPerModifier; unsigned char *nextDest = outputMessage + 32; decodeBuffer.decodeBoolValue(value); if (value) { memcpy(outputMessage + 32, ServerCache::getModifierMappingLastMap.getData(), ServerCache::getModifierMappingLastMap.getLength()); break; } for (unsigned int count = outputLength - 32; count; count--) { decodeBuffer.decodeBoolValue(value); if (value) *nextDest++ = 0; else { decodeBuffer.decodeValue(value, 8); *nextDest++ = value; } } ServerCache::getModifierMappingLastMap.set(outputLength - 32, outputMessage + 32); } break; case X_GetProperty: { MessageStore *messageStore = serverStore_ -> getReplyStore(X_GetProperty); handleDecode(decodeBuffer, serverCache_, messageStore, requestOpcode, outputMessage, outputLength); } break; case X_GetSelectionOwner: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> getSelectionOwnerCache, 9); PutULONG(value, outputMessage + 8, bigEndian_); } break; case X_GetWindowAttributes: { outputLength = 44; outputMessage = writeBuffer_.addMessage(outputLength); decodeBuffer.decodeValue(value, 2); outputMessage[1] = (unsigned char) value; decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> visualCache); PutULONG(value, outputMessage + 8, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> getWindowAttributesClassCache, 3); PutUINT(value, outputMessage + 12, bigEndian_); decodeBuffer.decodeCachedValue(cValue, 8, serverCache_ -> getWindowAttributesBitGravityCache); outputMessage[14] = cValue; decodeBuffer.decodeCachedValue(cValue, 8, serverCache_ -> getWindowAttributesWinGravityCache); outputMessage[15] = cValue; decodeBuffer.decodeCachedValue(value, 32, serverCache_ -> getWindowAttributesPlanesCache, 9); PutULONG(value, outputMessage + 16, bigEndian_); decodeBuffer.decodeCachedValue(value, 32, serverCache_ -> getWindowAttributesPixelCache, 9); PutULONG(value, outputMessage + 20, bigEndian_); decodeBuffer.decodeBoolValue(value); outputMessage[24] = (unsigned char) value; decodeBuffer.decodeBoolValue(value); outputMessage[25] = (unsigned char) value; decodeBuffer.decodeValue(value, 2); outputMessage[26] = (unsigned char) value; decodeBuffer.decodeBoolValue(value); outputMessage[27] = (unsigned char) value; decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> colormapCache, 9); PutULONG(value, outputMessage + 28, bigEndian_); decodeBuffer.decodeCachedValue(value, 32, serverCache_ -> getWindowAttributesAllEventsCache); PutULONG(value, outputMessage + 32, bigEndian_); decodeBuffer.decodeCachedValue(value, 32, serverCache_ -> getWindowAttributesYourEventsCache); PutULONG(value, outputMessage + 36, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> getWindowAttributesDontPropagateCache); PutUINT(value, outputMessage + 40, bigEndian_); } break; case X_GrabKeyboard: case X_GrabPointer: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); decodeBuffer.decodeValue(value, 3); outputMessage[1] = (unsigned char) value; } break; case X_InternAtom: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); decodeBuffer.decodeValue(value, 29, 9); PutULONG(value, outputMessage + 8, bigEndian_); } break; case X_ListExtensions: { decodeBuffer.decodeValue(value, 32, 8); outputLength = 32 + (value << 2); outputMessage = writeBuffer_.addMessage(outputLength); unsigned int numExtensions; decodeBuffer.decodeValue(numExtensions, 8); outputMessage[1] = (unsigned char) numExtensions; unsigned char *nextDest = outputMessage + 32; for (; numExtensions; numExtensions--) { unsigned int length; decodeBuffer.decodeValue(length, 8); *nextDest++ = (unsigned char) length; for (; length; length--) { decodeBuffer.decodeValue(value, 8); *nextDest++ = value; } } } break; case X_ListFonts: { // // Differential compression can achieve a 12:1 to 14:1 // ratio, while the best ZLIB compression can achieve // a mere 4:1 to 5:1. In the first case, though, the // huge amount of data constituting the message would // be stored uncompressed at the remote side. We need // to find a compromise. The solution is to use diffe- // rential compression at startup and ZLIB compression // later on. // MessageStore *messageStore = serverStore_ -> getReplyStore(X_ListFonts); if (handleDecodeCached(decodeBuffer, serverCache_, messageStore, outputMessage, outputLength)) { break; } decodeBuffer.decodeValue(value, 32, 8); outputLength = 32 + (value << 2); outputMessage = writeBuffer_.addMessage(outputLength); unsigned int numFonts; decodeBuffer.decodeValue(numFonts, 16, 6); PutUINT(numFonts, outputMessage + 8, bigEndian_); // Differential or plain data compression? decodeBuffer.decodeBoolValue(value); if (value) { unsigned char* nextDest = outputMessage + 32; for (; numFonts; numFonts--) { unsigned int length; decodeBuffer.decodeValue(length, 8); *nextDest++ = (unsigned char)length; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(nextDest, length); nextDest += length; } else { serverCache_ -> getPropertyTextCompressor.reset(); for (; length; length--) { *nextDest++ = serverCache_ -> getPropertyTextCompressor. decodeChar(decodeBuffer); } } } handleSave(messageStore, outputMessage, outputLength); } else { const unsigned char *compressedData = NULL; unsigned int compressedDataSize = 0; int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset, outputMessage, outputLength, compressedData, compressedDataSize); if (decompressed < 0) { return -1; } else if (decompressed > 0) { handleSave(messageStore, outputMessage, outputLength, compressedData, compressedDataSize); } else { handleSave(messageStore, outputMessage, outputLength); } } } break; case X_LookupColor: case X_AllocNamedColor: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); unsigned char *nextDest = outputMessage + 8; if (requestOpcode == X_AllocNamedColor) { decodeBuffer.decodeValue(value, 32, 9); PutULONG(value, nextDest, bigEndian_); nextDest += 4; } unsigned int count = 3; do { decodeBuffer.decodeValue(value, 16, 9); PutUINT(value, nextDest, bigEndian_); unsigned int visualColor; decodeBuffer.decodeValue(visualColor, 16, 5); visualColor += value; visualColor &= 0xffff; PutUINT(visualColor, nextDest + 6, bigEndian_); nextDest += 2; } while (--count); } break; case X_QueryBestSize: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); decodeBuffer.decodeValue(value, 16, 8); PutUINT(value, outputMessage + 8, bigEndian_); decodeBuffer.decodeValue(value, 16, 8); PutUINT(value, outputMessage + 10, bigEndian_); } break; case X_QueryColors: { // Differential or plain data compression? decodeBuffer.decodeBoolValue(value); if (value) { decodeBuffer.decodeBoolValue(value); if (value) { unsigned int numColors = serverCache_ -> queryColorsLastReply.getLength() / 6; outputLength = 32 + (numColors << 3); outputMessage = writeBuffer_.addMessage(outputLength); PutUINT(numColors, outputMessage + 8, bigEndian_); const unsigned char *nextSrc = serverCache_ -> queryColorsLastReply.getData(); unsigned char *nextDest = outputMessage + 32; for (; numColors; numColors--) { for (unsigned int i = 0; i < 6; i++) *nextDest++ = *nextSrc++; nextDest += 2; } } else { unsigned int numColors; decodeBuffer.decodeValue(numColors, 16, 5); outputLength = 32 + (numColors << 3); outputMessage = writeBuffer_.addMessage(outputLength); PutUINT(numColors, outputMessage + 8, bigEndian_); unsigned char *nextDest = outputMessage + 32; for (unsigned int c = 0; c < numColors; c++) { for (unsigned int i = 0; i < 3; i++) { decodeBuffer.decodeValue(value, 16); PutUINT(value, nextDest, bigEndian_); nextDest += 2; } } serverCache_ -> queryColorsLastReply.set(numColors * 6, outputMessage + 32); const unsigned char *nextSrc = nextDest - 1; nextDest = outputMessage + 32 + ((numColors - 1) << 3) + 5; for (; numColors > 1; numColors--) { for (unsigned int i = 0; i < 6; i++) *nextDest-- = *nextSrc--; nextDest -= 2; } } } else { // Reply length. unsigned int numColors; decodeBuffer.decodeValue(numColors, 16, 5); outputLength = 32 + (numColors << 3); outputMessage = writeBuffer_.addMessage(outputLength); PutUINT(numColors, outputMessage + 8, bigEndian_); const unsigned char *compressedData = NULL; unsigned int compressedDataSize = 0; int decompressed = handleDecompress(decodeBuffer, requestOpcode, 32, outputMessage, outputLength, compressedData, compressedDataSize); if (decompressed < 0) { return -1; } } } break; case X_QueryExtension: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); decodeBuffer.decodeBoolValue(value); outputMessage[8] = (unsigned char) value; decodeBuffer.decodeValue(value, 8); outputMessage[9] = (unsigned char) value; decodeBuffer.decodeValue(value, 8); outputMessage[10] = (unsigned char) value; decodeBuffer.decodeValue(value, 8); outputMessage[11] = (unsigned char) value; // // We use a predefined opcode to address // extensions' message stores, while real // opcodes are used for communication with // X server and clients. // if (requestData[0] == X_NXInternalShapeExtension) { opcodeStore_ -> shapeExtension = outputMessage[9]; #ifdef TEST *logofs << "handleWrite: Shape extension opcode for FD#" << fd_ << " is " << (unsigned int) opcodeStore_ -> shapeExtension << ".\n" << logofs_flush; #endif } else if (requestData[0] == X_NXInternalRenderExtension) { opcodeStore_ -> renderExtension = outputMessage[9]; #ifdef TEST *logofs << "handleWrite: Render extension opcode for FD#" << fd_ << " is " << (unsigned int) opcodeStore_ -> renderExtension << ".\n" << logofs_flush; #endif } } break; case X_QueryFont: { // // Use differential compression at startup and plain // data compression later. Check X_ListFonts message // for an explaination. // MessageStore *messageStore = serverStore_ -> getReplyStore(X_QueryFont); if (handleDecodeCached(decodeBuffer, serverCache_, messageStore, outputMessage, outputLength)) { break; } // Differential or plain data compression? decodeBuffer.decodeBoolValue(value); if (value) { unsigned int numProperties; unsigned int numCharInfos; decodeBuffer.decodeValue(numProperties, 16, 8); decodeBuffer.decodeValue(numCharInfos, 32, 10); outputLength = 60 + numProperties * 8 + numCharInfos * 12; outputMessage = writeBuffer_.addMessage(outputLength); PutUINT(numProperties, outputMessage + 46, bigEndian_); PutULONG(numCharInfos, outputMessage + 56, bigEndian_); handleDecodeCharInfo(decodeBuffer, outputMessage + 8); handleDecodeCharInfo(decodeBuffer, outputMessage + 24); decodeBuffer.decodeValue(value, 16, 9); PutUINT(value, outputMessage + 40, bigEndian_); decodeBuffer.decodeValue(value, 16, 9); PutUINT(value, outputMessage + 42, bigEndian_); decodeBuffer.decodeValue(value, 16, 9); PutUINT(value, outputMessage + 44, bigEndian_); decodeBuffer.decodeBoolValue(value); outputMessage[48] = (unsigned char) value; decodeBuffer.decodeValue(value, 8); outputMessage[49] = (unsigned char) value; decodeBuffer.decodeValue(value, 8); outputMessage[50] = (unsigned char) value; decodeBuffer.decodeBoolValue(value); outputMessage[51] = (unsigned char) value; decodeBuffer.decodeValue(value, 16, 9); PutUINT(value, outputMessage + 52, bigEndian_); decodeBuffer.decodeValue(value, 16, 9); PutUINT(value, outputMessage + 54, bigEndian_); unsigned char *nextDest = outputMessage + 60; decodeBuffer.decodeBoolValue(value); int end = 0; if (value == 1) { unsigned int index; decodeBuffer.decodeValue(index, 4); unsigned int length; const unsigned char *data; ServerCache::queryFontFontCache.get(index, length, data); memcpy(nextDest, data, length); end = 1; } if (end == 0) { unsigned char *saveDest = nextDest; unsigned int length = numProperties * 8 + numCharInfos * 12; for (; numProperties; numProperties--) { decodeBuffer.decodeValue(value, 32, 9); PutULONG(value, nextDest, bigEndian_); decodeBuffer.decodeValue(value, 32, 9); PutULONG(value, nextDest + 4, bigEndian_); nextDest += 8; } for (; numCharInfos; numCharInfos--) { handleDecodeCharInfo(decodeBuffer, nextDest); nextDest += 12; } ServerCache::queryFontFontCache.set(length, saveDest); } handleSave(messageStore, outputMessage, outputLength); } else { // Reply length. unsigned int replyLength; decodeBuffer.decodeValue(replyLength, 32, 16); outputLength = 32 + (replyLength << 2); outputMessage = writeBuffer_.addMessage(outputLength); const unsigned char *compressedData = NULL; unsigned int compressedDataSize = 0; int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset, outputMessage, outputLength, compressedData, compressedDataSize); if (decompressed < 0) { return -1; } else if (decompressed > 0) { handleSave(messageStore, outputMessage, outputLength, compressedData, compressedDataSize); } else { handleSave(messageStore, outputMessage, outputLength); } } } break; case X_QueryPointer: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); decodeBuffer.decodeBoolValue(value); outputMessage[1] = (unsigned char) value; decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> queryPointerRootCache, 9); PutULONG(value, outputMessage + 8, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> queryPointerChildCache, 9); PutULONG(value, outputMessage + 12, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> motionNotifyRootXCache, 8); serverCache_ -> motionNotifyLastRootX += value; PutUINT(serverCache_ -> motionNotifyLastRootX, outputMessage + 16, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> motionNotifyRootYCache, 8); serverCache_ -> motionNotifyLastRootY += value; PutUINT(serverCache_ -> motionNotifyLastRootY, outputMessage + 18, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> motionNotifyEventXCache, 8); PutUINT(serverCache_ -> motionNotifyLastRootX + value, outputMessage + 20, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> motionNotifyEventYCache, 8); PutUINT(serverCache_ -> motionNotifyLastRootY + value, outputMessage + 22, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> motionNotifyStateCache); PutUINT(value, outputMessage + 24, bigEndian_); } break; case X_QueryTree: { unsigned int children; decodeBuffer.decodeValue(children, 16, 8); outputLength = 32 + (children << 2); outputMessage = writeBuffer_.addMessage(outputLength); PutULONG(outputLength, outputMessage + 4, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> queryTreeWindowCache); PutULONG(value, outputMessage + 8, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> queryTreeWindowCache); PutULONG(value, outputMessage + 12, bigEndian_); unsigned char *next = outputMessage + 32; PutUINT(children, outputMessage + 16, bigEndian_); for (unsigned int i = 0; i < children; i++) { decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> queryTreeWindowCache); PutULONG(value, next + (i * 4), bigEndian_); } } break; case X_TranslateCoords: { outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); decodeBuffer.decodeBoolValue(value); outputMessage[1] = (unsigned char) value; decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> translateCoordsChildCache, 9); PutULONG(value, outputMessage + 8, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> translateCoordsXCache, 8); PutUINT(value, outputMessage + 12, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> translateCoordsYCache, 8); PutUINT(value, outputMessage + 14, bigEndian_); } break; case X_GetImage: { MessageStore *messageStore = serverStore_ -> getReplyStore(X_GetImage); if (handleDecodeCached(decodeBuffer, serverCache_, messageStore, outputMessage, outputLength)) { break; } // Depth. decodeBuffer.decodeCachedValue(cValue, 8, serverCache_ -> depthCache); // Reply length. unsigned int replyLength; decodeBuffer.decodeValue(replyLength, 32, 9); outputLength = 32 + (replyLength << 2); outputMessage = writeBuffer_.addMessage(outputLength); outputMessage[1] = (unsigned char) cValue; // Visual. unsigned int visual; decodeBuffer.decodeCachedValue(visual, 29, serverCache_ -> visualCache); PutULONG(visual, outputMessage + 8, bigEndian_); if (control -> isProtoStep8() == 0) { const unsigned char *compressedData = NULL; unsigned int compressedDataSize = 0; int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset, outputMessage, outputLength, compressedData, compressedDataSize); if (decompressed < 0) { return -1; } else if (decompressed > 0) { handleSave(messageStore, outputMessage, outputLength, compressedData, compressedDataSize); } else { handleSave(messageStore, outputMessage, outputLength); } } else { handleCopy(decodeBuffer, requestOpcode, messageStore -> dataOffset, outputMessage, outputLength); handleSave(messageStore, outputMessage, outputLength); } } break; case X_GetPointerMapping: { unsigned int nextByte; decodeBuffer.decodeValue(nextByte, 8, 4); unsigned int replyLength; decodeBuffer.decodeValue(replyLength, 32, 4); outputLength = 32 + (replyLength << 2); outputMessage = writeBuffer_.addMessage(outputLength); outputMessage[1] = (unsigned char) nextByte; unsigned char *nextDest = outputMessage + 32; for (unsigned int i = 32; i < outputLength; i++) { decodeBuffer.decodeValue(nextByte, 8, 4); *nextDest++ = (unsigned char) nextByte; } } break; case X_GetKeyboardControl: { unsigned int nextByte; decodeBuffer.decodeValue(nextByte, 8, 2); unsigned int replyLength; decodeBuffer.decodeValue(replyLength, 32, 8); outputLength = 32 + (replyLength << 2); outputMessage = writeBuffer_.addMessage(outputLength); outputMessage[1] = (unsigned char) nextByte; unsigned char *nextDest = outputMessage + 8; for (unsigned int i = 8; i < outputLength; i++) { decodeBuffer.decodeValue(nextByte, 8, 4); *nextDest++ = (unsigned char) nextByte; } } break; default: { if (requestOpcode == opcodeStore_ -> getUnpackParameters) { #ifdef TEST *logofs << "handleWrite: Received get unpack parameters reply " << "OPCODE#" << (unsigned int) opcodeStore_ -> getUnpackParameters << ".\n" << logofs_flush; #endif outputLength = 32 + PACK_METHOD_LIMIT; outputMessage = writeBuffer_.addMessage(outputLength); unsigned int method; // // Let agent use only the unpack methods // implemented at both sides. // for (int i = 0; i < PACK_METHOD_LIMIT; i++) { decodeBuffer.decodeBoolValue(method); control -> RemoteUnpackMethods[i] = method; *(outputMessage + 32 + i) = (control -> LocalUnpackMethods[i] == 1 && method == 1); } } else if (requestOpcode == opcodeStore_ -> getShmemParameters) { if (handleShmemReply(decodeBuffer, requestOpcode, outputMessage, outputLength) < 0) { return -1; } } else if (requestOpcode == opcodeStore_ -> getFontParameters) { if (handleFontReply(decodeBuffer, requestOpcode, outputMessage, outputLength) < 0) { return -1; } } else { #ifdef PANIC *logofs << "handleWrite: PANIC! No matching request for " << "reply with sequence number " << sequenceNum << ".\n" << logofs_flush; #endif cerr << "Error" << ": No matching request for " << "reply with sequence number " << sequenceNum << ".\n"; return -1; } } } #if defined(TEST) || defined(OPCODES) *logofs << "handleWrite: Handled reply to OPCODE#" << (unsigned) requestOpcode << " (" << DumpOpcode(requestOpcode) << ")" << " for FD#" << fd_ << " with sequence " << serverSequence_ << ". Output size is " << outputLength << ".\n" << logofs_flush; #endif statistics -> addRepliedRequest(requestOpcode); } else // End of if (sequenceQueue_.peek() && ...) { // // Reply didn't match any request opcode. // Check again if differential encoding // is disabled. // #ifdef DEBUG *logofs << "handleWrite: Identified generic reply.\n" << logofs_flush; #endif requestOpcode = X_Reply; if (control -> RemoteDeltaCompression == 0) { int result = handleFastWriteReply(decodeBuffer, requestOpcode, outputMessage, outputLength); if (result < 0) { return -1; } else if (result > 0) { continue; } } // // All replies whose opcode is not pushed in // sequence number queue are cached together. // Among such replies are those to extension // requests. // MessageStore *messageStore = serverStore_ -> getReplyStore(X_NXInternalGenericReply); handleDecode(decodeBuffer, serverCache_, messageStore, requestOpcode, outputMessage, outputLength); #if defined(TEST) || defined(OPCODES) *logofs << "handleWrite: Handled generic reply for FD#" << fd_ << " with sequence " << serverSequence_ << ". Output size is " << outputLength << ".\n" << logofs_flush; #endif statistics -> addRepliedRequest(requestOpcode); } // End of if (sequenceQueue_.peek() && ...) else ... // // If any output was produced then write opcode, // sequence number and size to the buffer. // if (outputLength > 0) { *outputMessage = outputOpcode; PutUINT(serverSequence_, outputMessage + 2, bigEndian_); PutULONG((outputLength - 32) >> 2, outputMessage + 4, bigEndian_); } } // End of if (outputOpcode == 1)... else { // // It's an event or error. // unsigned int sequenceNum; unsigned int sequenceDiff; decodeBuffer.decodeCachedValue(sequenceDiff, 16, serverCache_ -> eventSequenceCache, 7); sequenceNum = (serverSequence_ + sequenceDiff) & 0xffff; serverSequence_ = sequenceNum; #ifdef DEBUG *logofs << "handleWrite: Last server sequence number for FD#" << fd_ << " is " << serverSequence_ << " with " << "difference " << sequenceDiff << ".\n" << logofs_flush; #endif // // Check if this is an error that matches // a sequence number for which we were // expecting a reply. // if (outputOpcode == X_Error) { unsigned short int errorSequenceNum; unsigned char errorOpcode; if (sequenceQueue_.peek(errorSequenceNum, errorOpcode) && ((unsigned) errorSequenceNum == serverSequence_)) { // // Remove the queued sequence of the reply. // #ifdef TEST *logofs << "handleWrite: WARNING! Removing reply to OPCODE#" << (unsigned) errorOpcode << " sequence " << errorSequenceNum << " for FD#" << fd_ << " due to error.\n" << logofs_flush; #endif sequenceQueue_.pop(errorSequenceNum, errorOpcode); // // Send to the client the current sequence // number, not the number that matched the // reply. Because we are generating replies // at our side, Xlib can incur in a sequence // lost if the error comes after the auto- // generated reply. // if (control -> SessionMode == session_proxy) { #ifdef TEST *logofs << "handleWrite: Updating last event's sequence " << lastSequence_ << " to X server's error sequence " << "number " << serverSequence_ << " for FD#" << fd_ << ".\n" << logofs_flush; #endif lastSequence_ = serverSequence_; } } // // In case of errors always send to client the // original X server's sequence associated to // the failing request. // if (control -> SessionMode != session_proxy) { #ifdef TEST *logofs << "handleWrite: Updating last event's sequence " << lastSequence_ << " to X server's error sequence " << "number " << serverSequence_ << " for FD#" << fd_ << ".\n" << logofs_flush; #endif lastSequence_ = serverSequence_; } } // // Check if by producing events at client side we // have modified the events' sequence numbering. // In this case taint the original sequence to // comply with the last one known by client. // /* FIXME: Recover the sequence number if the proxy is not connected to an agent. */ if (serverSequence_ > lastSequence_ || control -> SessionMode != session_proxy) { #ifdef DEBUG *logofs << "handleWrite: Updating last event's sequence " << lastSequence_ << " to X server's sequence number " << serverSequence_ << " for FD#" << fd_ << ".\n" << logofs_flush; #endif lastSequence_ = serverSequence_; } #ifdef DEBUG else if (serverSequence_ < lastSequence_) { // // Use our last auto-generated sequence. // *logofs << "handleWrite: Tainting sequence number " << serverSequence_ << " to last event's sequence " << lastSequence_ << " for FD#" << fd_ << ".\n" << logofs_flush; } #endif // // Check if remote side used fast encoding. // if (control -> RemoteDeltaCompression == 0) { int result = handleFastWriteEvent(decodeBuffer, outputOpcode, outputMessage, outputLength); if (result < 0) { return -1; } else if (result > 0) { continue; } } // // Make space for message in the outgoing buffer // and write opcode and sequence number. // outputLength = 32; outputMessage = writeBuffer_.addMessage(outputLength); *outputMessage = outputOpcode; PutUINT(lastSequence_, outputMessage + 2, bigEndian_); #ifdef DEBUG *logofs << "handleWrite: Going to handle event or error OPCODE#" << (unsigned int) outputOpcode << " for FD#" << fd_ << " sequence " << lastSequence_ << " (real was " << serverSequence_ << ").\n" << logofs_flush; #endif switch (outputOpcode) { case X_Error: { unsigned char code; decodeBuffer.decodeCachedValue(code, 8, serverCache_ -> errorCodeCache); outputMessage[1] = code; #if defined(TEST) || defined(OPCODES) *logofs << "handleWrite: Handled error ERR_CODE#" << (unsigned int) code << " for FD#" << fd_; #endif if ((code != 11) && (code != 8) && (code != 15) && (code != 1)) { decodeBuffer.decodeValue(value, 32, 16); PutULONG(value, outputMessage + 4, bigEndian_); #if defined(TEST) || defined(OPCODES) *logofs << " RES_ID#" << value; #endif } if (code >= 18) { decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> errorMinorCache); PutUINT(value, outputMessage + 8, bigEndian_); #if defined(TEST) || defined(OPCODES) *logofs << " MIN_OP#" << value; #endif } decodeBuffer.decodeCachedValue(cValue, 8, serverCache_ -> errorMajorCache); outputMessage[10] = cValue; #if defined(TEST) || defined(OPCODES) *logofs << " MAJ_OP#" << (unsigned int) cValue; #endif if (code >= 18) { unsigned char *nextDest = outputMessage + 11; for (unsigned int i = 11; i < 32; i++) { decodeBuffer.decodeValue(value, 8); *nextDest++ = (unsigned char) cValue; } } #if defined(TEST) || defined(OPCODES) *logofs << " sequence " << lastSequence_ << " (real was " << serverSequence_ << ") . Size is " << (unsigned int) outputLength << ".\n" << logofs_flush; #endif } break; case ButtonPress: case ButtonRelease: case KeyPress: case KeyRelease: case MotionNotify: case EnterNotify: case LeaveNotify: { if (outputOpcode == MotionNotify) { decodeBuffer.decodeBoolValue(value); } else if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify) { decodeBuffer.decodeValue(value, 3); } else if (outputOpcode == KeyRelease) { decodeBuffer.decodeBoolValue(value); if (value) { value = serverCache_ -> keyPressLastKey; } else { decodeBuffer.decodeValue(value, 8); } } else if (outputOpcode == ButtonPress || outputOpcode == ButtonRelease) { decodeBuffer.decodeCachedValue(cValue, 8, serverCache_ -> buttonCache); value = (unsigned int) cValue; } else { decodeBuffer.decodeValue(value, 8); } outputMessage[1] = (unsigned char) value; decodeBuffer.decodeCachedValue(value, 32, serverCache_ -> motionNotifyTimestampCache, 9); serverCache_ -> lastTimestamp += value; PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4, bigEndian_); unsigned char *nextDest = outputMessage + 8; int skipRest = 0; if (outputOpcode == KeyRelease) { decodeBuffer.decodeBoolValue(value); if (value) { for (unsigned int i = 0; i < 23; i++) { *nextDest++ = serverCache_ -> keyPressCache[i]; } skipRest = 1; } } if (!skipRest) { for (unsigned int i = 0; i < 3; i++) { decodeBuffer.decodeCachedValue(value, 29, *serverCache_ -> motionNotifyWindowCache[i], 6); PutULONG(value, nextDest, bigEndian_); nextDest += 4; } decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> motionNotifyRootXCache, 6); serverCache_ -> motionNotifyLastRootX += value; PutUINT(serverCache_ -> motionNotifyLastRootX, outputMessage + 20, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> motionNotifyRootYCache, 6); serverCache_ -> motionNotifyLastRootY += value; PutUINT(serverCache_ -> motionNotifyLastRootY, outputMessage + 22, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> motionNotifyEventXCache, 6); PutUINT(serverCache_ -> motionNotifyLastRootX + value, outputMessage + 24, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> motionNotifyEventYCache, 6); PutUINT(serverCache_ -> motionNotifyLastRootY + value, outputMessage + 26, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> motionNotifyStateCache); PutUINT(value, outputMessage + 28, bigEndian_); if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify) { decodeBuffer.decodeValue(value, 2); } else { decodeBuffer.decodeBoolValue(value); } outputMessage[30] = (unsigned char) value; if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify) { decodeBuffer.decodeValue(value, 2); outputMessage[31] = (unsigned char) value; } else if (outputOpcode == KeyPress) { serverCache_ -> keyPressLastKey = outputMessage[1]; for (unsigned int i = 8; i < 31; i++) { serverCache_ -> keyPressCache[i - 8] = outputMessage[i]; } } } } break; case ColormapNotify: { decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> colormapNotifyWindowCache, 8); PutULONG(value, outputMessage + 4, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> colormapNotifyColormapCache, 8); PutULONG(value, outputMessage + 8, bigEndian_); decodeBuffer.decodeBoolValue(value); outputMessage[12] = (unsigned char) value; decodeBuffer.decodeBoolValue(value); outputMessage[13] = (unsigned char) value; } break; case ConfigureNotify: { unsigned char *nextDest = outputMessage + 4; for (unsigned int i = 0; i < 3; i++) { decodeBuffer.decodeCachedValue(value, 29, *serverCache_ -> configureNotifyWindowCache[i], 9); PutULONG(value, nextDest, bigEndian_); nextDest += 4; } for (unsigned int j = 0; j < 5; j++) { decodeBuffer.decodeCachedValue(value, 16, *serverCache_ -> configureNotifyGeomCache[j], 8); PutUINT(value, nextDest, bigEndian_); nextDest += 2; } decodeBuffer.decodeBoolValue(value); *nextDest = value; } break; case CreateNotify: { decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> createNotifyWindowCache, 9); PutULONG(value, outputMessage + 4, bigEndian_); decodeBuffer.decodeValue(value, 29, 5); serverCache_ -> createNotifyLastWindow += value; serverCache_ -> createNotifyLastWindow &= 0x1fffffff; PutULONG(serverCache_ -> createNotifyLastWindow, outputMessage + 8, bigEndian_); unsigned char* nextDest = outputMessage + 12; for (unsigned int i = 0; i < 5; i++) { decodeBuffer.decodeValue(value, 16, 9); PutUINT(value, nextDest, bigEndian_); nextDest += 2; } decodeBuffer.decodeBoolValue(value); *nextDest = (unsigned char) value; } break; case Expose: { decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> exposeWindowCache, 9); PutULONG(value, outputMessage + 4, bigEndian_); unsigned char *nextDest = outputMessage + 8; for (unsigned int i = 0; i < 5; i++) { decodeBuffer.decodeCachedValue(value, 16, *serverCache_ -> exposeGeomCache[i], 6); PutUINT(value, nextDest, bigEndian_); nextDest += 2; } } break; case FocusIn: case FocusOut: { decodeBuffer.decodeValue(value, 3); outputMessage[1] = (unsigned char) value; decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> focusInWindowCache, 9); PutULONG(value, outputMessage + 4, bigEndian_); decodeBuffer.decodeValue(value, 2); outputMessage[8] = (unsigned char) value; } break; case KeymapNotify: { decodeBuffer.decodeBoolValue(value); if (value) memcpy(outputMessage + 1, ServerCache::lastKeymap.getData(), 31); else { unsigned char *nextDest = outputMessage + 1; for (unsigned int i = 1; i < 32; i++) { decodeBuffer.decodeValue(value, 8); *nextDest++ = (unsigned char) value; } ServerCache::lastKeymap.set(31, outputMessage + 1); } } break; case MapNotify: case UnmapNotify: case DestroyNotify: { decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> mapNotifyEventCache, 9); PutULONG(value, outputMessage + 4, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> mapNotifyWindowCache, 9); PutULONG(value, outputMessage + 8, bigEndian_); if (outputOpcode == MapNotify || outputOpcode == UnmapNotify) { decodeBuffer.decodeBoolValue(value); outputMessage[12] = (unsigned char) value; } } break; case NoExpose: { decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> noExposeDrawableCache, 9); PutULONG(value, outputMessage + 4, bigEndian_); decodeBuffer.decodeCachedValue(value, 16, serverCache_ -> noExposeMinorCache); PutUINT(value, outputMessage + 8, bigEndian_); decodeBuffer.decodeCachedValue(cValue, 8, serverCache_ -> noExposeMajorCache); outputMessage[10] = cValue; } break; case PropertyNotify: { decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> propertyNotifyWindowCache, 9); PutULONG(value, outputMessage + 4, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> propertyNotifyAtomCache, 9); PutULONG(value, outputMessage + 8, bigEndian_); decodeBuffer.decodeValue(value, 32, 9); serverCache_ -> lastTimestamp += value; PutULONG(serverCache_ -> lastTimestamp, outputMessage + 12, bigEndian_); decodeBuffer.decodeBoolValue(value); outputMessage[16] = (unsigned char) value; } break; case ReparentNotify: { unsigned char* nextDest = outputMessage + 4; for (unsigned int i = 0; i < 3; i++) { decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> reparentNotifyWindowCache, 9); PutULONG(value, nextDest, bigEndian_); nextDest += 4; } decodeBuffer.decodeValue(value, 16, 6); PutUINT(value, nextDest, bigEndian_); decodeBuffer.decodeValue(value, 16, 6); PutUINT(value, nextDest + 2, bigEndian_); decodeBuffer.decodeBoolValue(value); outputMessage[20] = (unsigned char)value; } break; case SelectionClear: { decodeBuffer.decodeValue(value, 32, 9); serverCache_ -> lastTimestamp += value; PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> selectionClearWindowCache, 9); PutULONG(value, outputMessage + 8, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> selectionClearAtomCache, 9); PutULONG(value, outputMessage + 12, bigEndian_); } break; case SelectionRequest: { decodeBuffer.decodeValue(value, 32, 9); serverCache_ -> lastTimestamp += value; PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> selectionClearWindowCache, 9); PutULONG(value, outputMessage + 8, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> selectionClearWindowCache, 9); PutULONG(value, outputMessage + 12, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> selectionClearAtomCache, 9); PutULONG(value, outputMessage + 16, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> selectionClearAtomCache, 9); PutULONG(value, outputMessage + 20, bigEndian_); decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> selectionClearAtomCache, 9); PutULONG(value, outputMessage + 24, bigEndian_); } break; case VisibilityNotify: { decodeBuffer.decodeCachedValue(value, 29, serverCache_ -> visibilityNotifyWindowCache, 9); PutULONG(value, outputMessage + 4, bigEndian_); decodeBuffer.decodeValue(value, 2); outputMessage[8] = (unsigned char) value; } break; default: { #ifdef TEST *logofs << "handleWrite: Using generic event compression " << "for OPCODE#" << (unsigned int) outputOpcode << ".\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(*(outputMessage + 1), 8, serverCache_ -> genericEventCharCache); for (unsigned int i = 0; i < 14; i++) { decodeBuffer.decodeCachedValue(value, 16, *serverCache_ -> genericEventIntCache[i]); PutUINT(value, outputMessage + i * 2 + 4, bigEndian_); } } } // End of switch (outputOpcode)... #if defined(TEST) || defined(OPCODES) if (outputOpcode != X_Error) { *logofs << "handleWrite: Handled event OPCODE#" << (unsigned int) outputOpcode << " for FD#" << fd_ << " sequence " << lastSequence_ << " (real was " << serverSequence_ << "). Size is " << outputLength << ".\n" << logofs_flush; } #endif // // Check if we need to suppress the error. // if (outputOpcode == X_Error && handleTaintSyncError(*(outputMessage + 10)) > 0) { #if defined(TEST) || defined(OPCODES) *logofs << "handleWrite: WARNING! Suppressed error OPCODE#" << (unsigned int) outputOpcode << " for FD#" << fd_ << " sequence " << lastSequence_ << ".\n" << logofs_flush; #endif writeBuffer_.removeMessage(32); } } // End of if (outputOpcode == 1)... else ... // // Check if we produced enough data. We need to // decode all provided messages. 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; } return 1; } // // End of handleWrite(). // // // Other members. // int ClientChannel::handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store, T_store_action action, int position, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { #if defined(TEST) || defined(SPLIT) if (control -> isProtoStep8() == 1) { *logofs << "handleSplit: PANIC! SPLIT! Split should " << "not be enabled for message " << "OPCODE#" << (unsigned int) store -> opcode() << ".\n" << logofs_flush; HandleCleanup(); } #endif // // Never split the message if connected to // an old proxy version. Also refuse the // split if we it is not introduced by a // start split. // if (control -> isProtoStep7() == 0) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Ignoring the split with " << "an old proxy version.\n" << logofs_flush; #endif if (action == IS_ADDED || action == is_discarded) { encodeBuffer.encodeBoolValue(0); } return 0; } else if (splitState_.resource == nothing || enableSplit_ == 0) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Nothing to do for message " << "OPCODE#" << (unsigned int) store -> opcode() << " of size " << size << " position " << position << " with action [" << DumpAction(action) << "] at " << strMsTimestamp() << ".\n" << logofs_flush; #endif encodeBuffer.encodeBoolValue(0); return 0; } // // It's not advisable to allocate the store at // the time we receive the start-split because // we may process all the splits received and // deallocate the store even before we receive // the end split. Another message for the same // split sequence may then come and we would // have a null split store. // handleSplitStoreAlloc(&splitResources_, splitState_.resource); // // Check if the split was actually requested by // the agent and if the request was saved in the // message store. The split can also be refused // if the message is smaller than the threshold // or if the split store is already full. // if (mustSplitMessage(splitState_.resource) == 0) { if (action == IS_HIT || canSplitMessage(splitState_.mode, size) == 0) { #if defined(TEST) || defined(SPLIT) if (splitState_.mode == split_none) { #ifdef PANIC *logofs << "handleSplit: PANIC! SPLIT! Split state has " << "mode 'none'.\n" << logofs_flush; #endif HandleCleanup(); } if (action != IS_HIT && (int) size >= control -> SplitDataThreshold) { #ifdef WARNING *logofs << "handleSplit: WARNING! SPLIT! Split stores have " << clientStore_ -> getSplitTotalSize() << " messages " << "and " << clientStore_ -> getSplitTotalStorageSize() << " allocated bytes.\n" << logofs_flush; #endif } #endif #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Message OPCODE#" << (unsigned int) store -> opcode() << " of size " << size << " [not split] with resource " << splitState_.resource << " mode " << splitState_.mode << " position " << position << " and action [" << DumpAction(action) << "] at " << strMsTimestamp() << ".\n" << logofs_flush; #endif encodeBuffer.encodeBoolValue(0); return 0; } } #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Message OPCODE#" << (unsigned int) store -> opcode() << " of size " << size << " [split] with resource " << splitState_.resource << " mode " << splitState_.mode << " position " << position << " and action [" << DumpAction(action) << "] at " << strMsTimestamp() << ".\n" << logofs_flush; #endif encodeBuffer.encodeBoolValue(1); T_checksum checksum = NULL; if (action == IS_ADDED) { checksum = store -> getChecksum(position); } else if (action == is_discarded) { // // Generate the checksum on the fly. // checksum = store -> getChecksum(buffer, size, bigEndian_); } // // The method must abort the connection // if it can't allocate the split. // Split *splitMessage = clientStore_ -> getSplitStore(splitState_.resource) -> add(store, splitState_.resource, splitState_.mode, position, action, checksum, buffer, size); // // Send the checksum. By using the checksum, // the remote end will try to locate the // message and load it from disk. // if (action == IS_HIT) { splitMessage -> setState(split_loaded); } else if (handleSplitChecksum(encodeBuffer, checksum) == 0) { // // If the checksum is not sent, for example // because loading of messages from disk is // disabled, then mark the split as missed. // #ifdef WARNING *logofs << "handleSplit: WARNING! Checksum not sent. " << "Marking the split as [missed].\n" << logofs_flush; #endif splitMessage -> setState(split_missed); } if (action == is_discarded) { delete [] checksum; } // // Check if we are ready to send a new split // for this store. // handleSplitPending(splitState_.resource); #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_.resource); #endif return 1; } int ClientChannel::handleSplit(EncodeBuffer &encodeBuffer) { // // Determine the maximum amount of bytes // we can write in this iteration. // int total = control -> SplitDataPacketLimit; int bytes = total; int splits = 0; #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Handling splits " << "for FD#" << fd_ << " with " << clientStore_ -> getSplitTotalSize() << " elements and " << total << " bytes to write at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (proxy -> handleAsyncSwitch(fd_) < 0) { return -1; } #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Looping to find " << "if there is any split to send.\n" << logofs_flush; #endif SplitStore *splitStore; Split *splitMessage; // // Divide the available bandwidth among all the active // split stores by implementing a simple round-robin // mechanism. This can be extended by using an external // function returning the number of bytes to be written // based on the state of the split (splits which didn't // receive yet a confirmation event could be delayed), // the current bitrate, and by letting the agent asso- // ciate a priority to the resource in the start split // operation. // splitState_.pending = 0; splitResources_.rotate(); // // Copy the list since elements can be removed // in the middle of the loop. // T_list splitList = splitResources_.copyList(); for (T_list::iterator j = splitList.begin(); j != splitList.end(); j++) { int resource = *j; #ifdef DEBUG *logofs << "handleSplit: SPLIT! Looping with current " << "resource " << resource << ".\n" << logofs_flush; #endif splitStore = clientStore_ -> getSplitStore(resource); if (splitStore != NULL) { // // Don't send more than the the packet size // bytes but ensure that we abort any split // found in the disk cache. // for (;;) { #if defined(TEST) || defined(SPLIT) clientStore_ -> dumpSplitStore(resource); #endif splitMessage = splitStore -> getFirstSplit(); if (splitMessage == NULL) { // // We have created the store after a start // split but no message was added yet. // #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: WARNING! SPLIT! The split store " << "is still 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 << "handleSplit: PANIC! SPLIT! Found an " << "aborted split in store [" << resource << "].\n" << logofs_flush; HandleCleanup(); } #endif // // Check if there are more messages in the // store that can be aborted or if we have // exceeded the number of bytes we can send // for this iteration. // #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Checking closure " << "of the inner loop with " << bytes << " bytes to write and split state [" << DumpState(splitMessage -> getState()) << "].\n" << logofs_flush; #endif if ((splitMessage -> getMode() == split_sync && splitMessage -> getState() == split_added) || (bytes <= 0 && splitMessage -> getState() != split_loaded)) { break; } // // If the split was loaded at the remote // side abort it immediately. // if (splitMessage -> getState() == split_loaded) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Sending more data " << "for store [" << resource << "] with " << "a split to be aborted.\n" << logofs_flush; #endif if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0) { return -1; } } else if (bytes > 0) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Sending more data " << "for store [" << resource << "] with " << bytes << " bytes to send.\n" << logofs_flush; #endif if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0) { return -1; } } // // Check if the split store was deleted. // splitStore = clientStore_ -> getSplitStore(resource); if (splitStore == NULL) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Exiting from the " << "inner loop with split store [" << resource << "] destroyed.\n" << logofs_flush; #endif break; } } #if defined(TEST) || defined(SPLIT) *logofs << "handleSplit: SPLIT! Completed handling splits " << "for store [" << resource << "] with " << bytes << " bytes still to send.\n" << logofs_flush; #endif // // Check if there is still a split to // send for the store just processed. // handleSplitPending(resource); } } #if defined(TEST) || defined(SPLIT) if (splits == 0) { #ifdef PANIC *logofs << "handleSplit: PANIC! Function called but " << "no split message was sent.\n" << logofs_flush; #endif HandleCleanup(); } *logofs << "handleSplit: SPLIT! Sent " << splits << " splits and " << total - bytes << " bytes for FD#" << fd_ << " with " << clientStore_ -> getSplitTotalStorageSize() << " bytes and [" << clientStore_ -> getSplitTotalSize() << "] splits remaining.\n" << logofs_flush; *logofs << "handleSplit: SPLIT! The pending split flag is " << splitState_.pending << " with " << clientStore_ -> getSplitTotalSize() << " splits in the split stores.\n" << logofs_flush; clientStore_ -> dumpSplitStores(); #endif return 1; } int ClientChannel::handleSplitSend(EncodeBuffer &encodeBuffer, int resource, int &splits, int &bytes) { #if defined(TEST) || defined(SPLIT) SplitStore *splitStore = clientStore_ -> getSplitStore(resource); Split *splitMessage = splitStore -> getFirstSplit(); if (splitStore -> getResource() != resource || splitMessage -> getResource() != resource) { #ifdef PANIC *logofs << "handleSplitSend: PANIC! The resource doesn't " << "match the split store.\n" << logofs_flush; #endif HandleCleanup(); } *logofs << "handleSplitSend: SPLIT! Sending message " << "OPCODE#" << (unsigned) opcodeStore_ -> splitData << " for resource " << splitMessage -> getResource() << " with request " << splitMessage -> getRequest() << " position " << splitMessage -> getPosition() << " and " << bytes << " bytes to write.\n" << logofs_flush; #endif // // Use a special opcode to signal the other // side this is part of a split and not a // new message. // encodeBuffer.encodeOpcodeValue(opcodeStore_ -> splitData, clientCache_ -> opcodeCache); encodeBuffer.encodeCachedValue(resource, 8, clientCache_ -> resourceCache); int result = clientStore_ -> getSplitStore(resource) -> send(encodeBuffer, bytes); if (result < 0) { #ifdef PANIC *logofs << "handleSplit: PANIC! Error sending splits for FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Error sending splits for FD#" << fd_ << ".\n"; return -1; } // // Get the bits written and update the // statistics for this special opcode. // int bits = encodeBuffer.diffBits(); #if defined(TEST) || defined(SPLIT)|| defined(OPCODES) *logofs << "handleSplitSend: SPLIT! Handled request OPCODE#" << (unsigned int) opcodeStore_ -> splitData << " (" << DumpOpcode(opcodeStore_ -> splitData) << ")" << " for FD#" << fd_ << " sequence none. 0 bytes in, " << bits << " bits (" << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; #endif statistics -> addRequestBits(opcodeStore_ -> splitData, 0, bits); bytes -= bits >> 3; splits++; if (result == 1) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitSend: SPLIT! Split at the head " << "of the list was completely transferred.\n" << logofs_flush; #endif // // The split at the head of the list was // completely transferred. // handleRestart(sequence_deferred, resource); } #if defined(TEST) || defined(SPLIT) else { *logofs << "handleSplitSend: SPLIT! More data to send " << "for the split at the head of the list.\n" << logofs_flush; } #endif return result; } int ClientChannel::handleSplitChecksum(EncodeBuffer &encodeBuffer, T_checksum checksum) { // // Send the checksum only if the loading // or the saving of the message to the // persistent image cache is enabled. // if ((control -> ImageCacheEnableLoad == 1 || control -> ImageCacheEnableSave == 1) && (enableLoad_ == 1 || enableSave_ == 1)) { encodeBuffer.encodeBoolValue(1); for (unsigned int i = 0; i < MD5_LENGTH; i++) { encodeBuffer.encodeValue((unsigned int) checksum[i], 8); } #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitChecksum: SPLIT! Sent checksum " << "[" << DumpChecksum(checksum) << "].\n" << logofs_flush; #endif return 1; } else { encodeBuffer.encodeBoolValue(0); return 0; } } void ClientChannel::handleSplitPending() { #if defined(TEST) || defined(SPLIT) int previous = splitState_.pending; #endif if (clientStore_ -> getSplitTotalSize() == 0) { splitState_.pending = 0; #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitPending: SPLIT! Set the pending " << "split flag to " << splitState_.pending << " with split stores empty.\n" << logofs_flush; #endif } else { // // Loop through the stores to find if // there is any split that has become // ready. // #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitPending: WARNING! SPLIT! Looping to " << "find if there is any split pending.\n" << logofs_flush; #endif splitState_.pending = 0; T_list &splitList = splitResources_.getList(); for (T_list::iterator j = splitList.begin(); j != splitList.end(); j++) { int resource = *j; SplitStore *splitStore = clientStore_ -> getSplitStore(resource); if (splitStore != NULL) { #if defined(TEST) || defined(SPLIT) clientStore_ -> dumpSplitStore(resource); #endif Split *splitMessage = splitStore -> getFirstSplit(); if (splitMessage != NULL && canSendSplit(splitMessage) == 1) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitPending: SPLIT! Found a pending " << "split in store [" << resource << "].\n" << logofs_flush; #endif splitState_.pending = 1; #if defined(TEST) || defined(SPLIT) if (splitMessage -> getState() == split_loaded) { *logofs << "handleSplitPending: PANIC! SPLIT! Found a " << "loaded split in store [" << resource << "].\n" << logofs_flush; HandleCleanup(); } #endif break; } } } #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitPending: SPLIT! Set the pending " << "split flag to " << splitState_.pending << " with " << clientStore_ -> getSplitTotalSize() << " splits in the split stores.\n" << logofs_flush; #endif } #if defined(TEST) || defined(SPLIT) if (splitState_.pending != previous) { *logofs << "handleSplitPending: SPLIT! Pending state " << "changed from " << previous << " to " << splitState_.pending << ".\n" << logofs_flush; } #endif } int ClientChannel::handleSplitEvent(EncodeBuffer &encodeBuffer, Split *splitMessage) { SplitStore *splitStore; int resource = splitMessage -> getResource(); #if defined(TEST) || defined(INFO) splitStore = clientStore_ -> getSplitStore(resource); if (splitStore == NULL) { #ifdef PANIC *logofs << "handleSplitEvent: PANIC! The split store can't " << "be NULL handling abort splits.\n" << logofs_flush; #endif HandleCleanup(); } else if (splitMessage -> getState() != split_loaded) { *logofs << "handleSplitEvent: PANIC! Can't find the split " << "to be aborted.\n" << logofs_flush; HandleCleanup(); } #endif // // Send any split that it is possible to // abort until the store is either empty // or the next split can't be aborted. // if (proxy -> handleAsyncSwitch(fd_) < 0) { return -1; } while ((splitStore = clientStore_ -> getSplitStore(resource)) != NULL && (splitMessage = splitStore -> getFirstSplit()) != NULL && splitMessage -> getState() == split_loaded) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitEvent: SPLIT! Aborting split with " << "checksum [" << DumpChecksum(splitMessage -> getChecksum()) << "] for resource " << resource << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif int any = 0; if (handleSplitSend(encodeBuffer, resource, any, any) < 0) { return -1; } } #if defined(TEST) || defined(SPLIT) if ((splitStore = clientStore_ -> getSplitStore(resource)) == NULL) { *logofs << "handleSplitEvent: SPLIT! The split store [" << resource << "] has been destroyed.\n" << logofs_flush; } else if ((splitMessage = splitStore -> getFirstSplit()) == NULL) { *logofs << "handleSplitEvent: SPLIT! The split store [" << resource << "] is empty.\n" << logofs_flush; } else if (splitMessage -> getState() != split_loaded) { *logofs << "handleSplitEvent: SPLIT! The split at the " << "head of store [" << resource << "] doesn't " << "need to be aborted.\n" << logofs_flush; } #endif return 1; } int ClientChannel::handleSplitEvent(DecodeBuffer &decodeBuffer) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitEvent: SPLIT! Handling abort " << "split messages for FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (control -> isProtoStep7() == 0) { #ifdef PANIC *logofs << "handleSplitEvent: PANIC! The split can't " << "be aborted when connected to an old " << "proxy version.\n" << logofs_flush; #endif HandleCleanup(); } // // Decode the information about the // message to be updated. // unsigned char resource; decodeBuffer.decodeCachedValue(resource, 8, serverCache_ -> resourceCache); unsigned int loaded; decodeBuffer.decodeBoolValue(loaded); unsigned char request; unsigned int size; if (loaded == 1) { decodeBuffer.decodeOpcodeValue(request, serverCache_ -> abortOpcodeCache); decodeBuffer.decodeValue(size, 32, 14); } else { request = 0; size = 0; } unsigned int value; md5_byte_t checksum[MD5_LENGTH]; for (unsigned int i = 0; i < MD5_LENGTH; i++) { decodeBuffer.decodeValue(value, 8); checksum[i] = (unsigned char) value; } #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitEvent: SPLIT! Checking split " << "with checksum [" << DumpChecksum(checksum) << "] loaded " << loaded << " request " << (unsigned int) request << " compressed size " << size << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif Split *splitMessage = handleSplitFind(checksum, resource); if (splitMessage != NULL) { if (loaded == 1) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitEvent: SPLIT! Marked split with " << "checksum [" << DumpChecksum(checksum) << "] " << "as [loaded] at " << strMsTimestamp() << ".\n" << logofs_flush; #endif splitMessage -> setState(split_loaded); #if defined(TEST) || defined(SPLIT) if (splitMessage -> compressedSize() != (int) size) { *logofs << "handleSplitEvent: WARNING! SPLIT! Updating " << "compressed data size from " << splitMessage -> compressedSize() << " to " << size << ".\n" << logofs_flush; } #endif splitMessage -> compressedSize(size); // // The splits to be aborted are checked by the split // store at the time we are going to send a new chunk // of split data. The splits must be strictly handled // in the same order as they were added to the split // store and the split we want to abort here may be // not at the head of the list. // if (splitMessage == clientStore_ -> getSplitStore(resource) -> getFirstSplit()) { // // We don't need to flush this packet immediately. // The abort can be sent at any time to the remote // proxy. What's important is that we restart the // agent resource as soon as possible. // #if defined(TEST) || defined(SPLIT) T_timestamp startTs = getTimestamp(); *logofs << "handleSplitEvent: SPLIT! Encoding abort " << "split events for FD#" << fd_ << " with " << "resource " << (unsigned) resource << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0) { return -1; } #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitEvent: SPLIT! Spent " << diffTimestamp(startTs, getTimestamp()) << " Ms " << "handling abort split events for FD#" << fd_ << ".\n" << logofs_flush; #endif // // Check if we can clear the pending flag. // handleSplitPending(); } #if defined(TEST) || defined(SPLIT) else { *logofs << "handleSplitEvent: WARNING! SPLIT! Abort split " << "event not sent because not at the head " << "of the list.\n" << logofs_flush; } #endif } else { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitEvent: SPLIT! Marked split with " << "checksum [" << DumpChecksum(checksum) << "] " << "as [missed] at " << strMsTimestamp() << ".\n" << logofs_flush; #endif splitMessage -> setState(split_missed); // // Check if we can set the pending flag. // handleSplitPending(resource); } } else { // // The split report came after the split was already // sent or the split store deleted. If the message // had been loaded from disk by the remote side, we // need to update the compressed size in our message // store or the checksum will not match at the time // we will try to save the message store on disk. // if (loaded == 1 && size != 0) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitEvent: WARNING! SPLIT! Can't find " << "the split. Updating in the message store.\n" << logofs_flush; #endif MessageStore *store = clientStore_ -> getRequestStore(request); if (store != NULL) { store -> updateData(checksum, size); } #if defined(TEST) || defined(SPLIT) else { #ifdef PANIC *logofs << "handleSplitEvent: PANIC! The message store " << "can't be null.\n" << logofs_flush; #endif HandleCleanup(); } #endif } #if defined(TEST) || defined(SPLIT) else { *logofs << "handleSplitEvent: WARNING! SPLIT! No need to " << "update the store with loaded " << loaded << " and compressed size " << size << ".\n" << logofs_flush; } #endif } return 1; } Split *ClientChannel::handleSplitFind(T_checksum checksum, int resource) { // // It can be that we handled all the splits, // restarted the resource and deleted the // store before the event could even reach // our side. // SplitStore *splitStore = clientStore_ -> getSplitStore(resource); if (splitStore != NULL) { Split *splitMessage; T_splits *splitList = splitStore -> getSplits(); for (T_splits::iterator i = splitList -> begin(); i != splitList -> end(); i++) { splitMessage = (*i); if (splitMessage -> getChecksum() != NULL) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitFind: SPLIT! Comparing with message [" << DumpChecksum(splitMessage -> getChecksum()) << "].\n" << logofs_flush; #endif if (memcmp(checksum, splitMessage -> getChecksum(), MD5_LENGTH) == 0) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitFind: SPLIT! Located split for " << "checksum [" << DumpChecksum(checksum) << "] " << "in store [" << splitStore -> getResource() << "].\n" << logofs_flush; #endif return splitMessage; } } } } #if defined(TEST) || defined(SPLIT) else { *logofs << "handleSplitFind: WARNING! SPLIT! The split store " << "was already deleted.\n" << logofs_flush; } #endif #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitFind: WARNING! SPLIT! Can't find the " << "split for checksum [" << DumpChecksum(checksum) << "].\n" << logofs_flush; #endif return NULL; } int ClientChannel::handleRestart(T_sequence_mode mode, int resource) { // // The agent must send a start-split message, followed by the // X messages that may be optionally split by the proxy. Usu- // ally, in the middle of a start-split/end-split sequence is // a single PutImage() or PutPackedImage(), that, in turn, // can generate multiple partial requests, like a SetUnpack- // Colormap() and SetUnpackAlpha() followed by the image that // must be transferred. Multiple requests may be also genera- // ted because the maximum size of a X request has been exce- // eded, so that Xlib has divided the single image in multi- // ple sub-image requests. The agent doesn't need to take care // of that, except tracking the result of the split operation. // // By monitoring the notify events sent by the proxy, the // agent will have to implement its own strategy to deal with // its resources (for example its clients). For example: // // - It will issue a new image request and suspend a client // if the image was not entirely sent in the main X oputput // stream. // // - It will choose to commit or discard the messages after // they are recomposed at the remote side. The set of mes- // sages that will have to be committed will include all // messages that were part of the split (the colormap, the // alpha channel). // // - It will restart its own client, in the case it had been // suspended. // // A more useful strategy would be to replace the original im- // age with a tiny 'placeholder' if a split took place, and // synchronize the content of the drawable at later time. This // is generally referred as 'lazy encoding'. // // The agent will be able to identify the original split ope- // ration (the one marked with the start-spit) by the small // integer number (0-255) referred to as the 'resource' field. // // Before the proxy will be able to report the status of the // split, the agent will have to close the sequence by issueing // an end-split. The proxy will then report the result of the // operation, so that the agent will have the option of suspend- // ing the client or marking the drawable as dirty and take // care of synchronizing it at later time. // // One of the following cases may be encountered: // // notify_no_split: All messages were sent in the main out- // put stream, so that no split actually // took place. // // notify_start_split: One or more messages were split, so, // at discrection of the agent, the client // may be suspended until the transferral // is completed. // // notify_commit_split: One of the requests that made up the // split was recomposed. The agent should // either commit the given request or tell // the proxy to discard it. // // notify_end_split: The split was duly completed. The agent // can restart the client. // // notify_empty_split: No more split operation are pending. // The agent can use this information to // implement specific strategies requiring // that all messages have been recomposed // at the remote end, like updating the // drawables that were not synchronized // because of the lazy encoding. // // By checking the split and commit store we can determine if we // need to send a new notification event to the agent. There can // be four different cases: // // - If the split store is not null and not empty, we are still // in the middle of a split. // // - If the commit store is not empty, we completely recomposed // a full message and can send a new commit notify. // // - If the split store has become empty, we recomposed all the // messages added for the given resource, and so will be able // to restart the resource. // // - If no more messages are in the split stores, we can notify // an empty split event to the agent. // #if defined(TEST) || defined(SPLIT) *logofs << "handleRestart: SPLIT! Handling [" << (mode == sequence_immediate ? "immediate" : "deferred") << "] restart events for resource " << resource << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif SplitStore *splitStore = clientStore_ -> getSplitStore(resource); if (mode == sequence_immediate) { // // We have received an end-split request. If the store // was not deleted already, we mark the last split added // as the one ending the row for this resource. If the // commit() function returns 0 it means that the split // store is either empty or that we did not add any split // for this resource. This is because when connected to // an old proxy version we only have a single store for // all the resources. // // It can happen that all the split messages that were // originally appended to the list were completely sent // before our client had the chance of ending the split // sequence. In this case the split store will be empty // or already deleted and so we will be able to restart // the resource. // #if defined(TEST) || defined(SPLIT) if (splitStore == NULL) { *logofs << "handleRestart: WARNING! SPLIT! Split store [" << resource << "] was already deleted.\n" << logofs_flush; } else { clientStore_ -> dumpSplitStore(resource); } #endif if (splitStore == NULL || splitStore -> getSize() == 0) { #if defined(TEST) || defined(SPLIT) *logofs << "handleRestart: SPLIT! Immediate agent split event " << "TYPE#" << (unsigned) opcodeStore_ -> noSplitNotify << " [no split] with resource " << resource << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (handleNotify(notify_no_split, sequence_immediate, resource, nothing, nothing) < 0) { return -1; } } else { #if defined(TEST) || defined(SPLIT) *logofs << "handleRestart: SPLIT! Immediate agent split event " << "TYPE#" << (unsigned) opcodeStore_ -> startSplitNotify << " [start split] with resource " << resource << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (handleNotify(notify_start_split, sequence_immediate, resource, nothing, nothing) < 0) { return -1; } } } else { // // We have completely transferred a message // that was put in the split store. // // The id of the resource can be different // than the index of the store if we are // connected to an old proxy. // #if defined(TEST) || defined(SPLIT) if (splitStore == NULL) { #ifdef PANIC *logofs << "handleRestart: PANIC! The split store can't " << "be NULL handling deferred restart events.\n" << logofs_flush; #endif HandleCleanup(); } else { clientStore_ -> dumpSplitStore(resource); } #endif CommitStore *commitStore = clientStore_ -> getCommitStore(); #if defined(TEST) || defined(SPLIT) clientStore_ -> dumpCommitStore(); #endif // // Check if there is any commit to notify. // Split *split; T_splits *commitList = commitStore -> getSplits(); for (T_splits::iterator i = commitList -> begin(); i != commitList -> end(); i++) { split = *i; if (split -> getState() != split_notified) { #if defined(TEST) || defined(SPLIT) if (split -> getResource() != resource) { #ifdef PANIC *logofs << "handleSplitSend: PANIC! The resource doesn't " << "match the split store.\n" << logofs_flush; #endif HandleCleanup(); } #endif int request = split -> getRequest(); int position = split -> getPosition(); #if defined(TEST) || defined(SPLIT) *logofs << "handleRestart: SPLIT! Deferred agent split event " << "TYPE#" << (unsigned) opcodeStore_ -> commitSplitNotify << " [commit split] with resource " << resource << " request " << request << " position " << position << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (handleNotify(notify_commit_split, sequence_deferred, resource, request, position) < 0) { return -1; } // // Don't send the notification again. // split -> setState(split_notified); } #if defined(TEST) || defined(SPLIT) else { *logofs << "handleRestart: SPLIT! Split for request " << split -> getRequest() << " and position " << split -> getPosition() << " was already " << "notified.\n" << logofs_flush; } #endif } // // Don't send the end split if we are still // in the middle of a start-split/end-split // sequence. We'll send a no-split at the // time the end-split is received. // if (splitStore -> getSize() == 0 && splitStore -> getResource() != splitState_.resource) { #if defined(TEST) || defined(SPLIT) *logofs << "handleRestart: SPLIT! Deferred agent split event " << "TYPE#" << (unsigned) opcodeStore_ -> endSplitNotify << " [end split] with resource " << resource << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (handleNotify(notify_end_split, sequence_deferred, resource, nothing, nothing) < 0) { return -1; } } #if defined(TEST) || defined(SPLIT) else if (splitStore -> getSize() == 0 && splitStore -> getResource() == splitState_.resource) { *logofs << "handleRestart: SPLIT! WARNING! The split store " << "for resource " << resource << " was emptied in the " << "split sequence at " << strMsTimestamp() << ".\n" << logofs_flush; } #endif } // // Remove the split store if it's empty. // if (splitStore != NULL && splitStore -> getSize() == 0 && splitStore -> getResource() != splitState_.resource) { #if defined(TEST) || defined(SPLIT) *logofs << "handleRestart: SPLIT! Removing the split store [" << resource << "] at " << strMsTimestamp() << ".\n" << logofs_flush; #endif handleSplitStoreRemove(&splitResources_, resource); if (clientStore_ -> getSplitTotalSize() == 0) { #if defined(TEST) || defined(SPLIT) *logofs << "handleRestart: SPLIT! Deferred agent split event " << "TYPE#" << (unsigned) opcodeStore_ -> emptySplitNotify << " [empty split] for FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (handleNotify(notify_empty_split, sequence_deferred, nothing, nothing, nothing) < 0) { return -1; } } #if defined(TEST) || defined(SPLIT) *logofs << "handleRestart: SPLIT! There are " << clientStore_ -> getSplitTotalSize() << " messages and " << clientStore_ -> getSplitTotalStorageSize() << " bytes to send in " << "the split stores.\n" << logofs_flush; if ((clientStore_ -> getSplitTotalSize() != 0 && clientStore_ -> getSplitTotalStorageSize() == 0) || (clientStore_ -> getSplitTotalSize() == 0 && clientStore_ -> getSplitTotalStorageSize() != 0)) { #ifdef PANIC *logofs << "handleRestart: PANIC! Inconsistency detected " << "while handling the split stores.\n" << logofs_flush; #endif HandleCleanup(); } #endif } return 1; } int ClientChannel::handleTaintCacheRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size) { #ifdef TEST *logofs << "handleTaintCacheRequest: Tainting cache request " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif // // The save and load flags would affect // the decoding side but the decoding // side doesn't support the request. // enableCache_ = *(buffer + 4); enableSplit_ = *(buffer + 5); handleSplitEnable(); #ifdef TEST *logofs << "handleTaintCacheRequest: Set cache parameters to " << "cache " << enableCache_ << " split " << enableSplit_ << " load " << enableLoad_ << " save " << enableSave_ << ".\n" << logofs_flush; #endif // // Taint the request to a X_NoOperation. // opcode = X_NoOperation; return 0; } int ClientChannel::handleTaintFontRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size) { // // The remote end doesn't support this // request so generate an empty reply // at the local side. // #ifdef TEST *logofs << "handleTaintFontRequest: Suppressing font " << "request for FD#" << fd_ << ".\n" << logofs_flush; #endif // // The client sequence number has not // been incremented yet in the loop. // unsigned int sequence = (clientSequence_ + 1) & 0xffff; #ifdef TEST *logofs << "handleTaintFontRequest: Opcode is " << (unsigned) opcode << " expected client sequence is " << sequence << ".\n" << logofs_flush; #endif unsigned char *reply = writeBuffer_.addMessage(36); *(reply + 0) = X_Reply; PutUINT(sequence, reply + 2, bigEndian_); PutULONG(1, reply + 4, bigEndian_); // // Set the length of the returned // path to 0. // *(reply + 32) = 0; // // Save the sequence number, not incremented // yet, we used to auto-generate this reply. // lastSequence_ = clientSequence_ + 1; #ifdef TEST *logofs << "handleTaintFontRequest: Registered " << lastSequence_ << " as last auto-generated sequence number.\n" << logofs_flush; #endif // // Taint the request to a X_NoOperation. // opcode = X_NoOperation; if (handleFlush(flush_if_any) < 0) { return -1; } return 1; } int ClientChannel::handleTaintSplitRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size) { #ifdef TEST if (opcode == opcodeStore_ -> abortSplit) { *logofs << "handleTaintSplitRequest: Tainting abort split " << "request for FD#" << fd_ << ".\n" << logofs_flush; } else if (opcode == opcodeStore_ -> finishSplit) { *logofs << "handleTaintSplitRequest: Tainting finish split " << "request for FD#" << fd_ << ".\n" << logofs_flush; } else { *logofs << "handleTaintSplitRequest: Tainting free split " << "request for FD#" << fd_ << ".\n" << logofs_flush; } #endif // // Taint the request to a X_NoOperation. // opcode = X_NoOperation; return 1; } int ClientChannel::handleTaintLameRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size) { // // Test the efficiency of the encoding // without these RENDER requests. // if (opcode == opcodeStore_ -> renderExtension && (*(buffer + 1) == X_RenderCompositeGlyphs8 || *(buffer + 1) == X_RenderCompositeGlyphs16 || *(buffer + 1) == X_RenderCompositeGlyphs32 || *(buffer + 1) == X_RenderAddGlyphs || *(buffer + 1) == X_RenderTrapezoids)) { #ifdef TEST *logofs << "handleTaintLameRequest: Tainting request " << "OPCODE#" << (unsigned int) opcode << " MINOR#" << (unsigned int) *(buffer + 1) << " for FD#" << fd_ << ".\n" << logofs_flush; #endif opcode = X_NoOperation; return 1; } return 0; } int ClientChannel::handleTaintSyncRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size) { // // Should short-circuit other common replies // whose values could be queried only once. // Examples are X_InterAtom, X_ListExtension // and X_QueryExtension. // if (taintCounter_ >= control -> TaintThreshold) { #ifdef DEBUG *logofs << "handleTaintSyncRequest: Reset taint counter after " << taintCounter_ << " replies managed.\n" << logofs_flush; #endif taintCounter_ = 0; return 0; } // // Check if we are rolling the counter. // The client sequence number has not // been incremented yet in the loop. // unsigned int sequence = (clientSequence_ + 1) & 0xffff; #ifdef DEBUG *logofs << "handleTaintSyncRequest: Opcode is " << (unsigned) opcode << " expected client sequence is " << sequence << ".\n" << logofs_flush; #endif if (sequence == 0xffff) { return 0; } unsigned short t1; unsigned char t2; // // Check if there is a previous reply // pending. // if (sequenceQueue_.peek(t1, t2) != 0) { #ifdef DEBUG *logofs << "handleTaintSyncRequest: Skipping taint of reply due to " << "pending request OPCODE#" << t1 << " with sequence " << (unsigned int) t2 << ".\n" << logofs_flush; #endif return 0; } #ifdef DEBUG *logofs << "handleTaintSyncRequest: Suppressing get input focus " << "request for FD#" << fd_ << " with sequence " << sequence << ".\n" << logofs_flush; #endif unsigned char *reply = writeBuffer_.addMessage(32); *(reply + 0) = X_Reply; PutUINT(sequence, reply + 2, bigEndian_); PutULONG(0, reply + 4, bigEndian_); // // Set revert-to to none. // *(reply + 1) = 0; // // Set focus to none. // PutULONG(0, reply + 8, bigEndian_); // // Save the sequence number, not incremented // yet, we used to auto-generate this reply. // lastSequence_ = clientSequence_ + 1; #ifdef TEST *logofs << "handleTaintSyncRequest: Registered " << lastSequence_ << " as last auto-generated sequence number.\n" << logofs_flush; #endif // // Taint the request to a X_NoOperation. // opcode = X_NoOperation; // // We may assume that the client has finished // drawing and flush immediately, even if this // seems to perceively affect the performance. // // priority_++; // if (handleFlush(flush_if_any) < 0) { return -1; } taintCounter_++; return 1; } int ClientChannel::handleTaintSyncError(unsigned char opcode) { if (control -> TaintReplies > 0) { // // By enabling short-circuiting of replies // some window managers can get confused // by some otherwise innocuous X errors. // if (opcode == X_GrabKey || opcode == X_ReparentWindow || opcode == X_ConfigureWindow) { #if defined(TEST) || defined(OPCODES) *logofs << "handleTaintSyncError: WARNING! Suppressed error " << "on OPCODE#" << (unsigned int) opcode << " for FD#" << fd_ << " sequence " << lastSequence_ << " (real was " << serverSequence_ << ").\n" << logofs_flush; #endif return 1; } } return 0; } int ClientChannel::handleNotify(T_notification_type type, T_sequence_mode mode, int resource, int request, int position) { if (finish_ == 1) { #if defined(TEST) || defined(INFO) *logofs << "handleNotify: Discarding notification on " << "channel for FD#" << fd_ << ".\n" << logofs_flush; #endif return 0; } // // Add a new message to the write buffer. // unsigned char *event = writeBuffer_.addMessage(32); // // Event is ClientMessage, atom and // window are 0, format is 32. // *(event + 0) = ClientMessage; PutULONG(0, event + 4, bigEndian_); PutULONG(0, event + 8, bigEndian_); *(event + 1) = 32; // // If the event follows immediately the request (that is the // sequence mode is 'immediate') then the sequence number is // the one of the last request, else it should be the last // sequence number encoded by peer proxy but, as we are ins- // erting events in the stream, we must ensure that the se- // quence we send is not less than the last sequence we have // auto-generated. // if (mode == sequence_immediate) { // // Save the sequence number we used // to auto-generate this event. // lastSequence_ = clientSequence_; #if defined(TEST) || defined(INFO) *logofs << "handleNotify: Registered " << lastSequence_ << " as last auto-generated sequence number.\n" << logofs_flush; #endif } else { if (serverSequence_ > lastSequence_) { #ifdef DEBUG *logofs << "handleNotify: Updating last event's sequence " << lastSequence_ << " to X server's sequence number " << serverSequence_ << " for FD#" << fd_ << ".\n" << logofs_flush; #endif lastSequence_ = serverSequence_; } #ifdef DEBUG else if (serverSequence_ < lastSequence_) { // // Use our last auto-generated sequence. // *logofs << "handleNotify: Tainting sequence number " << serverSequence_ << " to last event's sequence " << lastSequence_ << " for FD#" << fd_ << ".\n" << logofs_flush; } #endif } PutUINT(lastSequence_, event + 2, bigEndian_); // // Be sure we set to void the fields that // are not significant for the specific // notification message. // PutULONG(nothing, event + 16, bigEndian_); PutULONG(nothing, event + 20, bigEndian_); PutULONG(nothing, event + 24, bigEndian_); switch (type) { case notify_no_split: { PutULONG(opcodeStore_ -> noSplitNotify, event + 12, bigEndian_); PutULONG(resource, event + 16, bigEndian_); break; } case notify_start_split: { PutULONG(opcodeStore_ -> startSplitNotify, event + 12, bigEndian_); PutULONG(resource, event + 16, bigEndian_); break; } case notify_commit_split: { PutULONG(opcodeStore_ -> commitSplitNotify, event + 12, bigEndian_); PutULONG(resource, event + 16, bigEndian_); PutULONG(request, event + 20, bigEndian_); PutULONG(position, event + 24, bigEndian_); break; } case notify_end_split: { PutULONG(opcodeStore_ -> endSplitNotify, event + 12, bigEndian_); PutULONG(resource, event + 16, bigEndian_); break; } case notify_empty_split: { PutULONG(opcodeStore_ -> emptySplitNotify, event + 12, bigEndian_); break; } default: { #ifdef PANIC *logofs << "handleNotify: PANIC! Unrecognized notify " << "TYPE#" << type << ".\n" << logofs_flush; #endif return -1; } } #if defined(TEST) || defined(INFO) || defined (SPLIT) *logofs << "handleNotify: Sending " << (mode == sequence_immediate ? "immediate " : "deferred ") << "agent notify event TYPE#" << GetULONG(event + 12, bigEndian_) << logofs_flush; if (resource != nothing) { *logofs << " with resource " << GetULONG(event + 16, bigEndian_) << logofs_flush; if (request != nothing && position != nothing) { *logofs << " request " << GetULONG(event + 20, bigEndian_) << " position " << GetULONG(event + 24, bigEndian_) << logofs_flush; } } *logofs << ".\n" << logofs_flush; #endif // // Send the notification now. // if (handleFlush(flush_if_any) < 0) { return -1; } return 1; } int ClientChannel::handleCommitSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { // // Get the data of the request to be // committed. // unsigned char request = *(buffer + 5); MessageStore *store = clientStore_ -> getRequestStore(request); if (store == NULL) { #ifdef PANIC *logofs << "handleCommitSplitRequest: PANIC! Can't commit split for " << "request OPCODE#" << (unsigned int) request << ". No message store found.\n" << logofs_flush; #endif cerr << "Error" << ": Can't commit split for request " << "OPCODE#" << (unsigned int) request << ". No message store found.\n"; return -1; } // // The position in cache of the message // to commit. Encode it as difference in // respect to the last encoded value. // unsigned int position = GetULONG(buffer + 8, bigEndian_); unsigned char resource = *(buffer + 1); unsigned int commit = *(buffer + 4); #if defined(TEST) || defined(SPLIT) if (commit == 1) { *logofs << "handleCommitSplitRequest: SPLIT! Committing request " << "OPCODE#" << (unsigned) request << " at position " << position << " for FD#" << fd_ << " with resource " << (unsigned) resource << ".\n" << logofs_flush; } else { *logofs << "handleCommitSplitRequest: SPLIT! Discarding request " << "OPCODE#" << (unsigned) request << " at position " << position << " for FD#" << fd_ << " with resource " << (unsigned) resource << ".\n" << logofs_flush; } #endif encodeBuffer.encodeOpcodeValue(request, clientCache_ -> opcodeCache); int diffCommit = position - splitState_.commit; splitState_.commit = position; encodeBuffer.encodeValue(diffCommit, 32, 5); // // Send the resource id and the commit // flag. // encodeBuffer.encodeCachedValue(resource, 8, clientCache_ -> resourceCache); encodeBuffer.encodeBoolValue(commit); // // Remove the split from the split queue. // Split *split = handleSplitCommitRemove(request, resource, splitState_.commit); if (split == NULL) { return -1; } clientStore_ -> getCommitStore() -> update(split); // // Free the split. // #if defined(TEST) || defined(SPLIT) *logofs << "handleCommitSplitRequest: SPLIT! Freeing up the " << "committed split.\n" << logofs_flush; #endif delete split; return 1; } int ClientChannel::handleAbortSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { unsigned char resource = *(buffer + 1); #if defined(TEST) || defined(SPLIT) *logofs << "handleAbortSplitRequest: SPLIT! Handling abort split " << "request for FD#"<< fd_ << " and resource " << (unsigned int) resource << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(resource, 8, clientCache_ -> resourceCache); SplitStore *splitStore = clientStore_ -> getSplitStore(resource); if (splitStore == NULL) { #ifdef WARNING *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The split " << "store [" << (unsigned int) resource << "] " << "is already empty.\n" << logofs_flush; #endif return 0; } // // Loop through the messages in the split // store and discard from the memory cache // the messages that are still incomplete. // Then remove the message from the split // store. // #if defined(TEST) || defined(SPLIT) clientStore_ -> dumpSplitStore(resource); #endif int splits = 0; 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 [" << (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(), use_checksum, discard_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++; } // // If the start-split/end-split sequence // was closed, send the notification now, // else wait for the end-split. // if (resource != splitState_.resource) { #if defined(TEST) || defined(SPLIT) *logofs << "handleAbortSplitRequest: SPLIT! Sending the " << "deferred [end split] event.\n" << logofs_flush; #endif handleRestart(sequence_deferred, resource); } #if defined(TEST) || defined(SPLIT) else { *logofs << "handleAbortSplitRequest: WARNING! SPLIT! Still " << "waiting for the closure of the split " << "sequence.\n" << logofs_flush; } #endif // // Check if there is any other store // having splits to send. // handleSplitPending(); return (splits > 0); } int ClientChannel::handleFinishSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { unsigned char resource = *(buffer + 1); #if defined(TEST) || defined(SPLIT) *logofs << "handleFinishSplitRequest: SPLIT! Handling finish split " << "request for FD#"<< fd_ << " and resource " << (unsigned int) resource << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(resource, 8, clientCache_ -> resourceCache); // // We need to get the protocol statistics // for the finish message we are handling // here because sending a new split will // reset the bits counter. // int bits = encodeBuffer.diffBits(); statistics -> addRequestBits(opcode, size << 3, bits); SplitStore *splitStore = clientStore_ -> getSplitStore(resource); if (splitStore == NULL) { #ifdef WARNING *logofs << "handleFinishSplitRequest: WARNING! SPLIT! The split " << "store [" << (unsigned int) resource << "] " << "is already empty.\n" << logofs_flush; #endif return 0; } // // Send all the split queued for the given // resource until the split store becomes // empty. // #if defined(TEST) || defined(SPLIT) clientStore_ -> dumpSplitStore(resource); #endif Split *splitMessage; int total = MESSAGE_DATA_LIMIT; int bytes = total; int splits = 0; for (;;) { splitMessage = splitStore -> getFirstSplit(); if (splitMessage == NULL) { // // We have presumably created the store // after a start split but no message // was added yet. // #ifdef WARNING *logofs << "handleFinishSplitRequest: WARNING! SPLIT! The " << "split store [" << (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 << "handleFinishSplitRequest: PANIC! SPLIT! Found an " << "aborted split in store [" << (unsigned int) resource << "].\n" << logofs_flush; HandleCleanup(); } *logofs << "handleFinishSplitRequest: SPLIT! Sending more " << "data for store [" << (unsigned int) resource << "].\n" << logofs_flush; #endif if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0) { return -1; } // // Check if the split store was deleted. // if (clientStore_ -> getSplitStore(resource) == NULL) { #if defined(TEST) || defined(SPLIT) *logofs << "handleFinishSplitRequest: SPLIT! Exiting " << "from the finish loop with split store [" << (unsigned int) resource << "] destroyed.\n" << logofs_flush; #endif break; } } // // Check if there is any other store // having splits to send. // handleSplitPending(); #if defined(TEST) || defined(SPLIT) *logofs << "handleFinishSplitRequest: SPLIT! Sent " << splits << " splits and " << total - bytes << " bytes for FD#" << fd_ << " with " << clientStore_ -> getSplitTotalStorageSize() << " bytes and [" << clientStore_ -> getSplitTotalSize() << "] splits remaining.\n" << logofs_flush; #endif return (splits > 0); } int ClientChannel::handleConfiguration() { #ifdef TEST *logofs << "ClientChannel: Setting new buffer parameters.\n" << logofs_flush; #endif readBuffer_.setSize(control -> ClientInitialReadSize, control -> ClientMaximumBufferSize); writeBuffer_.setSize(control -> TransportXBufferSize, control -> TransportXBufferThreshold, control -> TransportMaximumBufferSize); transport_ -> setSize(control -> TransportXBufferSize, control -> TransportXBufferThreshold, control -> TransportMaximumBufferSize); return 1; } int ClientChannel::handleFinish() { #ifdef TEST *logofs << "ClientChannel: Finishing channel for FD#" << fd_ << ".\n" << logofs_flush; #endif congestion_ = 0; priority_ = 0; finish_ = 1; taintCounter_ = 0; splitState_.resource = nothing; splitState_.pending = 0; splitState_.commit = 0; splitState_.mode = split_none; transport_ -> finish(); return 1; } // // If differential compression is disabled then use the // most simple encoding but handle the image requests // and the X_ListExtensions and X_QueryExtension messa- // ges (needed to detect the opcode of the shape or the // other extensions) in the usual way. // int ClientChannel::handleFastReadRequest(EncodeBuffer &encodeBuffer, const unsigned char &opcode, const unsigned char *&buffer, const unsigned int &size) { // // All the NX requests are handled in the // main message loop. The X_PutImage can // be handled here only if the split was // not requested. // if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) || (control -> isProtoStep7() == 1 && opcode == X_PutImage && splitState_.resource != nothing) || opcode == X_ListExtensions || opcode == X_QueryExtension) { return 0; } #ifdef DEBUG *logofs << "handleFastReadRequest: Encoding raw request OPCODE#" << (unsigned int) opcode << " for FD#" << fd_ << " with size " << size << ".\n" << logofs_flush; #endif encodeBuffer.encodeMemory(buffer, size); // // Put request on the fast track // if it needs a reply. // switch (opcode) { case X_GetAtomName: case X_GetGeometry: case X_GetInputFocus: case X_GetModifierMapping: case X_GetKeyboardMapping: case X_GetProperty: case X_GetSelectionOwner: case X_GrabPointer: case X_GrabKeyboard: case X_ListExtensions: case X_ListFonts: case X_LookupColor: case X_AllocNamedColor: case X_QueryPointer: case X_GetWindowAttributes: case X_QueryTree: case X_QueryBestSize: case X_QueryColors: case X_QueryFont: case X_TranslateCoords: case X_GetImage: case X_GetPointerMapping: case X_GetKeyboardControl: case X_InternAtom: case X_AllocColor: { sequenceQueue_.push(clientSequence_, opcode); priority_++; break; } default: { break; } } int bits = encodeBuffer.diffBits(); #if defined(TEST) || defined(OPCODES) *logofs << "handleFastReadRequest: Handled raw request OPCODE#" << (unsigned int) opcode << " (" << DumpOpcode(opcode) << ")" << " for FD#" << fd_ << " sequence " << clientSequence_ << ". " << size << " bytes in, " << bits << " bits (" << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; #endif statistics -> addRequestBits(opcode, size << 3, bits); if (opcode == opcodeStore_ -> renderExtension) { statistics -> addRenderRequestBits(*(buffer + 1), size << 3, bits); } return 1; } int ClientChannel::handleFastWriteReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size) { if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) || opcode == X_ListExtensions || opcode == X_QueryExtension) { return 0; } #ifdef DEBUG *logofs << "handleFastWriteReply: Decoding raw reply OPCODE#" << (unsigned int) opcode << " for FD#" << fd_ << ".\n" << logofs_flush; #endif buffer = writeBuffer_.addMessage(8); #ifndef __sun unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(8); *((unsigned int *) buffer) = *next++; *((unsigned int *) (buffer + 4)) = *next; #else /* #ifndef __sun */ memcpy(buffer, decodeBuffer.decodeMemory(8), 8); #endif /* #ifndef __sun */ size = 32 + (GetULONG(buffer + 4, bigEndian_) << 2); writeBuffer_.registerPointer(&buffer); if (writeBuffer_.getAvailable() < size - 8 || (int) size >= control -> TransportFlushBufferSize) { #ifdef DEBUG *logofs << "handleFastWriteReply: Using scratch buffer for OPCODE#" << (unsigned int) opcode << " with size " << size << " and " << writeBuffer_.getLength() << " bytes in buffer.\n" << logofs_flush; #endif writeBuffer_.removeMessage(8); buffer = writeBuffer_.addScratchMessage(((unsigned char *) decodeBuffer.decodeMemory(size - 8)) - 8, size); } else { writeBuffer_.addMessage(size - 8); #ifndef __sun if (size == 32) { next = (unsigned int *) decodeBuffer.decodeMemory(size - 8); for (int i = 8; i < 32; i += sizeof(unsigned int)) { *((unsigned int *) (buffer + i)) = *next++; } } else { memcpy(buffer + 8, decodeBuffer.decodeMemory(size - 8), size - 8); } #else /* #ifndef __sun */ memcpy(buffer + 8, decodeBuffer.decodeMemory(size - 8), size - 8); #endif /* #ifndef __sun */ } writeBuffer_.unregisterPointer(); // // We don't need to write our local sequence // number. Replies are always sent with the // original X server's sequence number. // #if defined(TEST) || defined(OPCODES) *logofs << "handleFastWriteReply: Handled raw reply OPCODE#" << (unsigned int) opcode << " for FD#" << fd_ << " with sequence " << serverSequence_ << ". Output size is " << size << ".\n" << logofs_flush; #endif #ifdef DEBUG *logofs << "handleFastWriteReply: Length of sequence queue is " << sequenceQueue_.length() << ".\n" << logofs_flush; #endif statistics -> addRepliedRequest(opcode); handleFlush(flush_if_needed); return 1; } int ClientChannel::handleFastWriteEvent(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size) { #ifdef DEBUG *logofs << "handleFastWriteEvent: Decoding raw " << (opcode == X_Error ? "error" : "event") << " OPCODE#" << (unsigned int) opcode << " for FD#" << fd_ << ".\n" << logofs_flush; #endif size = 32; buffer = writeBuffer_.addMessage(size); #ifndef __sun unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(size); for (int i = 0; i < 32; i += sizeof(unsigned int)) { *((unsigned int *) (buffer + i)) = *next++; } #else /* #ifndef __sun */ memcpy(buffer, decodeBuffer.decodeMemory(size), size); #endif /* #ifndef __sun */ // // Use our local sequence number. // PutUINT(lastSequence_, buffer + 2, bigEndian_); #if defined(TEST) || defined(OPCODES) *logofs << "handleFastWriteEvent: Handled raw " << (opcode == X_Error ? "error" : "event") << " OPCODE#" << (unsigned int) opcode << " for FD#" << fd_ << " with sequence " << lastSequence_ << ". Output size is " << size << ".\n" << logofs_flush; #endif // // Check if we need to suppress the error. // if (opcode == X_Error && handleTaintSyncError(*(buffer + 10)) > 0) { #if defined(TEST) || defined(OPCODES) *logofs << "handleFastWriteEvent: WARNING! Suppressed error OPCODE#" << (unsigned int) opcode << " for FD#" << fd_ << " with sequence " << lastSequence_ << ".\n" << logofs_flush; #endif writeBuffer_.removeMessage(32); } handleFlush(flush_if_needed); return 1; } int ClientChannel::handleShmemRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { // // Will push sequence and set // priority according to stage. // unsigned int stage = *(buffer + 1); #ifdef TEST *logofs << "handleShmemRequest: Encoding shmem request " << "OPCODE#" << (unsigned int) opcode << " for FD#" << fd_ << " with size " << size << " at stage " << stage << ".\n" << logofs_flush; #endif encodeBuffer.encodeValue(stage, 2); if (stage == 0) { unsigned int enableClient = 0; unsigned int enableServer = 0; if (control -> ShmemClient == 1) { enableClient = *(buffer + 4); } if (control -> ShmemServer == 1) { enableServer = *(buffer + 5); } encodeBuffer.encodeBoolValue(enableClient); encodeBuffer.encodeBoolValue(enableServer); unsigned int clientSegment = GetULONG(buffer + 8, bigEndian_); unsigned int serverSegment = GetULONG(buffer + 12, bigEndian_); encodeBuffer.encodeValue(clientSegment, 29, 9); encodeBuffer.encodeValue(serverSegment, 29, 9); #ifdef TEST *logofs << "handleShmemRequest: Enable client is " << enableClient << " enable server is " << enableServer << " client segment is " << (void *) clientSegment << " server segment is " << (void *) serverSegment << ".\n" << logofs_flush; #endif #ifdef TEST *logofs << "handleShmemRequest: Size of the shared memory " << "segment will be " << control -> ShmemServerSize << ".\n" << logofs_flush; #endif } if (stage != 1) { sequenceQueue_.push(clientSequence_, opcodeStore_ -> getShmemParameters); priority_++; } return 1; } int ClientChannel::handleShmemReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size) { #ifdef TEST *logofs << "handleShmemReply: Received shmem parameters " << "reply OPCODE#" << (unsigned int) opcode << ".\n" << logofs_flush; #endif size = 32; buffer = writeBuffer_.addMessage(size); unsigned int stage; decodeBuffer.decodeValue(stage, 2); *(buffer + 1) = stage; if (stage == 2) { unsigned int clientEnabled; unsigned int serverEnabled; decodeBuffer.decodeBoolValue(clientEnabled); decodeBuffer.decodeBoolValue(serverEnabled); // // Client support is not implemented // and not useful. It is here only // for compatibility. // clientEnabled = 0; *(buffer + 8) = clientEnabled; *(buffer + 9) = serverEnabled; PutULONG(0, buffer + 12, bigEndian_); if (serverEnabled == 1) { #ifdef TEST *logofs << "handleShmemReply: Enabled shared memory " << "support in X server with segment size " << control -> ShmemServerSize << ".\n" << logofs_flush; #endif PutULONG(control -> ShmemServerSize, buffer + 16, bigEndian_); } else { PutULONG(0, buffer + 16, bigEndian_); } } else { *(buffer + 8) = 0; *(buffer + 9) = 0; PutULONG(0, buffer + 12, bigEndian_); PutULONG(0, buffer + 16, bigEndian_); } return 1; } int ClientChannel::handleFontRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { #ifdef TEST *logofs << "handleFontRequest: Encoding font request " << "OPCODE#" << (unsigned int) opcode << " for FD#" << fd_ << " with size " << size << ".\n" << logofs_flush; #endif sequenceQueue_.push(clientSequence_, opcodeStore_ -> getFontParameters); return 1; } int ClientChannel::handleFontReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size) { #ifdef TEST *logofs << "handleFontReply: Received font operation " << "reply OPCODE#" << (unsigned int) opcode << ".\n" << logofs_flush; #endif unsigned int length; decodeBuffer.decodeValue(length, 8); size = 32 + RoundUp4(length + 1); buffer = writeBuffer_.addMessage(size); unsigned char *next = buffer + 32; *next++ = length; decodeBuffer.decodeTextData(next, length); #ifdef TEST *logofs << "handleFontReply: Received tunneled font server " << "path '"; for (unsigned int i = 0; i < length; i++) { *logofs << *(buffer + 32 + 1 + i); } *logofs << "' for FD#" << fd_ << ".\n" << logofs_flush; #endif if (fontPort_ == -1) { // // The local side is not going to forward // the font server connections. // #ifdef TEST *logofs << "handleFontReply: WARNING! Returning an empty " << "font server path.\n" << logofs_flush; #endif writeBuffer_.removeMessage(size); size = 36; buffer = writeBuffer_.addMessage(size); // // Set the length of the returned // path to 0. // *(buffer + 32) = 0; } #ifdef TEST else { *logofs << "handleFontReply: Returning the received " << "font server path.\n" << logofs_flush; } #endif return 1; } int ClientChannel::handleCacheRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { #ifdef TEST *logofs << "handleCacheRequest: Handling cache request " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif enableCache_ = *(buffer + 4); enableSplit_ = *(buffer + 5); enableSave_ = *(buffer + 6); enableLoad_ = *(buffer + 7); handleSplitEnable(); #ifdef TEST *logofs << "handleCacheRequest: Set cache parameters to " << " cache " << enableCache_ << " split " << enableSplit_ << " save " << enableSave_ << " load " << enableLoad_ << ".\n" << logofs_flush; #endif // // Encode all the parameters as a // single unsigned int so we can // use an int cache. // unsigned int mask = enableSave_ << 8 | enableLoad_; encodeBuffer.encodeCachedValue(mask, 32, clientCache_ -> setCacheParametersCache); return 0; } int ClientChannel::handleStartSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { #if defined(TEST) || defined(SPLIT) *logofs << "handleStartSplitRequest: SPLIT! Handling start split " << "request for FD#"<< fd_ << ".\n" << logofs_flush; #endif if (splitState_.resource != nothing) { #ifdef PANIC *logofs << "handleStartSplitRequest: PANIC! SPLIT! Split requested " << "for resource id " << (unsigned int) *(buffer + 1) << " while handling resource " << splitState_.resource << ".\n" << logofs_flush; #endif cerr << "Error" << ": Split requested for " << "resource id " << (unsigned int) *(buffer + 1) << " while handling resource " << splitState_.resource << ".\n"; return -1; } else if (fd_ != firstClient_) { // // It can be that an auxiliary channel is the // first to connect, then comes the agent that // is actually using the NX opcodes. // #ifdef WARNING *logofs << "handleStartSplitRequest: WARNING SPLIT! Split requested " << "on FD#" << fd_ << " while expecting FD#" << firstClient_ << ".\n" << logofs_flush; #endif firstClient_ = fd_; } // // Set the agent's resource for which we are // going to split the request. // splitState_.resource = *(buffer + 1); #if defined(TEST) || defined(SPLIT) *logofs << "handleStartSplitRequest: SPLIT! Registered id " << splitState_.resource << " as resource " << "waiting for a split.\n" << logofs_flush; if (clientStore_ -> getSplitStore(splitState_.resource) != NULL) { *logofs << "handleStartSplitRequest: WARNING! SPLIT! A split " << "store for resource id " << splitState_.resource << " already exists.\n" << logofs_flush; clientStore_ -> dumpSplitStore(splitState_.resource); } #endif // // Send the selected resource to the remote. // if (control -> isProtoStep7() == 1) { encodeBuffer.encodeCachedValue(splitState_.resource, 8, clientCache_ -> resourceCache); } splitState_.mode = (T_split_mode) *(buffer + 4); if (splitState_.mode != NXSplitModeAsync && splitState_.mode != NXSplitModeSync) { splitState_.mode = (T_split_mode) control -> SplitMode; #if defined(TEST) || defined(SPLIT) *logofs << "handleStartSplitRequest: SPLIT! Set split " << "mode to '" << splitState_.mode << "' with " << "provided value '" << (unsigned) *(buffer + 4) << "'.\n" << logofs_flush; #endif } #if defined(TEST) || defined(SPLIT) if (splitState_.mode == NXSplitModeAsync) { *logofs << "handleStartSplitRequest: SPLIT! Selected split " << "mode is [split_async].\n" << logofs_flush; } else if (splitState_.mode == NXSplitModeSync) { *logofs << "handleStartSplitRequest: SPLIT! Selected split " << "mode is [split_sync].\n" << logofs_flush; } clientStore_ -> dumpSplitStores(); #endif return 1; } int ClientChannel::handleEndSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { #if defined(TEST) || defined(SPLIT) *logofs << "handleEndSplitRequest: SPLIT! Handling end split " << "request for FD#"<< fd_ << ".\n" << logofs_flush; #endif // // Verify that the agent resource matches. // 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 cerr << "Error" << ": Received an end of split " << "for resource id " << (unsigned int) *(buffer + 1) << " without a previous start.\n"; return -1; } else if (splitState_.resource != *(buffer + 1)) { #ifdef PANIC *logofs << "handleEndSplitRequest: PANIC! SPLIT! Invalid resource id " << (unsigned int) *(buffer + 1) << " received while " << "waiting for resource id " << splitState_.resource << ".\n" << logofs_flush; #endif cerr << "Error" << ": Invalid resource id " << (unsigned int) *(buffer + 1) << " received while " << "waiting for resource id " << splitState_.resource << ".\n"; return -1; } // // Send the selected resource to the remote. // if (control -> isProtoStep7() == 1) { encodeBuffer.encodeCachedValue(splitState_.resource, 8, clientCache_ -> resourceCache); } // // Send the split notification events // to the agent. // handleRestart(sequence_immediate, splitState_.resource); // // Check if we still have splits to send. // handleSplitPending(); #if defined(TEST) || defined(SPLIT) *logofs << "handleEndSplitRequest: SPLIT! Reset id " << splitState_.resource << " as resource " << "selected for splits.\n" << logofs_flush; #endif splitState_.resource = nothing; splitState_.mode = split_none; #if defined(TEST) || defined(SPLIT) clientStore_ -> dumpSplitStores(); #endif return 1; } void ClientChannel::handleDecodeCharInfo(DecodeBuffer &decodeBuffer, unsigned char *nextDest) { unsigned int value; decodeBuffer.decodeCachedValue(value, 32, *serverCache_ -> queryFontCharInfoCache[0], 6); PutUINT(value & 0xffff, nextDest, bigEndian_); PutUINT(value >> 16, nextDest + 10, bigEndian_); nextDest += 2; for (unsigned int i = 1; i < 5; i++) { unsigned int value; decodeBuffer.decodeCachedValue(value, 16, *serverCache_ -> queryFontCharInfoCache[i], 6); PutUINT(value, nextDest, bigEndian_); nextDest += 2; } } int ClientChannel::setBigEndian(int flag) { bigEndian_ = flag; return 1; } int ClientChannel::setReferences() { #ifdef TEST *logofs << "ClientChannel: Initializing the static " << "members for the client channels.\n" << logofs_flush; #endif #ifdef REFERENCES references_ = 0; #endif return 1; } nxcomp/PolyLine.h0000644000076400007640000001105611323113027014232 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PolyLine_H #define PolyLine_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define POLYLINE_ENABLE_CACHE 1 #define POLYLINE_ENABLE_DATA 0 #define POLYLINE_ENABLE_SPLIT 0 #define POLYLINE_ENABLE_COMPRESS 0 #define POLYLINE_DATA_LIMIT 144 #define POLYLINE_DATA_OFFSET 12 #define POLYLINE_CACHE_SLOTS 3000 #define POLYLINE_CACHE_THRESHOLD 3 #define POLYLINE_CACHE_LOWER_THRESHOLD 1 // // The message class. // class PolyLineMessage : public Message { friend class PolyLineStore; public: PolyLineMessage() { } ~PolyLineMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char mode; unsigned int drawable; unsigned int gcontext; }; class PolyLineStore : public MessageStore { // // Constructors and destructors. // public: PolyLineStore() : MessageStore() { enableCache = POLYLINE_ENABLE_CACHE; enableData = POLYLINE_ENABLE_DATA; enableSplit = POLYLINE_ENABLE_SPLIT; enableCompress = POLYLINE_ENABLE_COMPRESS; dataLimit = POLYLINE_DATA_LIMIT; dataOffset = POLYLINE_DATA_OFFSET; cacheSlots = POLYLINE_CACHE_SLOTS; cacheThreshold = POLYLINE_CACHE_THRESHOLD; cacheLowerThreshold = POLYLINE_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~PolyLineStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "PolyLine"; } virtual unsigned char opcode() const { return X_PolyLine; } virtual unsigned int storage() const { return sizeof(PolyLineMessage); } // // Message handling methods. // public: virtual Message *create() const { return new PolyLineMessage(); } virtual Message *create(const Message &message) const { return new PolyLineMessage((const PolyLineMessage &) message); } virtual void destroy(Message *message) const { delete (PolyLineMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* PolyLine_H */ nxcomp/CopyArea.cpp0000644000076400007640000001465411323113030014536 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "CopyArea.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP int CopyAreaStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { CopyAreaMessage *copyArea = (CopyAreaMessage *) message; // // Here is the fingerprint. // copyArea -> src_drawable = GetULONG(buffer + 4, bigEndian); copyArea -> dst_drawable = GetULONG(buffer + 8, bigEndian); copyArea -> gcontext = GetULONG(buffer + 12, bigEndian); copyArea -> src_x = GetUINT(buffer + 16, bigEndian); copyArea -> src_y = GetUINT(buffer + 18, bigEndian); copyArea -> dst_x = GetUINT(buffer + 20, bigEndian); copyArea -> dst_y = GetUINT(buffer + 22, bigEndian); copyArea -> width = GetUINT(buffer + 24, bigEndian); copyArea -> height = GetUINT(buffer + 26, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int CopyAreaStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { CopyAreaMessage *copyArea = (CopyAreaMessage *) message; // // Fill all the message's fields. // PutULONG(copyArea -> src_drawable, buffer + 4, bigEndian); PutULONG(copyArea -> dst_drawable, buffer + 8, bigEndian); PutULONG(copyArea -> gcontext, buffer + 12, bigEndian); PutUINT(copyArea -> src_x, buffer + 16, bigEndian); PutUINT(copyArea -> src_y, buffer + 18, bigEndian); PutUINT(copyArea -> dst_x, buffer + 20, bigEndian); PutUINT(copyArea -> dst_y, buffer + 22, bigEndian); PutUINT(copyArea -> width, buffer + 24, bigEndian); PutUINT(copyArea -> height, buffer + 26, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void CopyAreaStore::dumpIdentity(const Message *message) const { #ifdef DUMP CopyAreaMessage *copyArea = (CopyAreaMessage *) message; *logofs << name() << ": Identity src_drawable " << copyArea -> src_drawable << ", dst_drawable " << copyArea -> dst_drawable << ", gcontext " << copyArea -> gcontext << ", src_x " << copyArea -> src_x << ", src_y " << copyArea -> src_y << ", dst_x " << copyArea -> dst_x << ", dst_y " << copyArea -> dst_y << ", width " << copyArea -> width << ", height " << copyArea -> height << ", size " << copyArea -> size_ << ".\n"; #endif } void CopyAreaStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 16, 12); } void CopyAreaStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { CopyAreaMessage *copyArea = (CopyAreaMessage *) message; CopyAreaMessage *cachedCopyArea = (CopyAreaMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << copyArea -> src_drawable << " as " << "src_drawable" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(copyArea -> src_drawable, clientCache -> drawableCache); cachedCopyArea -> src_drawable = copyArea -> src_drawable; #ifdef TEST *logofs << name() << ": Encoding value " << copyArea -> dst_drawable << " as " << "dst_drawable" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(copyArea -> dst_drawable, clientCache -> drawableCache); cachedCopyArea -> dst_drawable = copyArea -> dst_drawable; #ifdef TEST *logofs << name() << ": Encoding value " << copyArea -> gcontext << " as " << "gcontext" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(copyArea -> gcontext, clientCache -> gcCache); cachedCopyArea -> gcontext = copyArea -> gcontext; } void CopyAreaStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { CopyAreaMessage *copyArea = (CopyAreaMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); copyArea -> src_drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << copyArea -> src_drawable << " as " << "src_drawable" << " field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); copyArea -> dst_drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << copyArea -> dst_drawable << " as " << "dst_drawable" << " field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); copyArea -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << copyArea -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } nxcomp/COPYING0000644000076400007640000004310311323113030013351 0ustar svetonisvetoni GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. nxcomp/MD5.h0000644000076400007640000000650011323113030013054 0ustar svetonisvetoni/* Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ nxcomp/ClientCache.cpp0000644000076400007640000002143011323113030015163 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ClientCache.h" ClientCache::ClientCache() : freeGCCache(16), freeDrawableCache(16), freeWindowCache(16), cursorCache(16), colormapCache(16), visualCache(16), lastFont(0), changePropertyPropertyCache(16), changePropertyTypeCache(16), changePropertyData32Cache(16), changePropertyTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), configureWindowBitmaskCache(4), convertSelectionRequestorCache(16), convertSelectionLastTimestamp(0), copyPlaneBitPlaneCache(8), createGCBitmaskCache(8), createPixmapIdCache(16), createPixmapLastId(0), createPixmapXCache(8), createPixmapYCache(8), createWindowBitmaskCache(8), fillPolyNumPointsCache(8), fillPolyIndex(0), getSelectionOwnerSelectionCache(8), grabButtonEventMaskCache(8), grabButtonConfineCache(8), grabButtonModifierCache(8), grabKeyboardLastTimestamp(0), imageTextLengthCache(8), imageTextLastX(0), imageTextLastY(0), imageTextCacheX(8), imageTextCacheY(8), imageTextTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), internAtomTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), openFontTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), polySegmentCacheX(8), polySegmentCacheY(8), polySegmentCacheIndex(0), polyTextLastX(0), polyTextLastY(0), polyTextCacheX(8), polyTextCacheY(8), polyTextFontCache(8), polyTextTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), putImageWidthCache(8), putImageHeightCache(8), putImageLastX(0), putImageLastY(0), putImageXCache(8), putImageYCache(8), getImagePlaneMaskCache(8), queryColorsLastPixel(0), setClipRectanglesXCache(8), setClipRectanglesYCache(8), setDashesLengthCache(8), setDashesOffsetCache(8), setSelectionOwnerCache(8), setSelectionOwnerTimestampCache(8), translateCoordsSrcCache(8), translateCoordsDstCache(8), translateCoordsXCache(8), translateCoordsYCache(8), sendEventMaskCache(16), sendEventLastSequence(0), sendEventIntDataCache(16), putPackedImageSrcLengthCache(16), putPackedImageDstLengthCache(16), // // RenderExtension requests. // renderFreePictureCache(16), renderGlyphSetCache(16), renderFreeGlyphSetCache(16), renderIdCache(8), renderLengthCache(16), renderFormatCache(16), renderValueMaskCache(8), renderNumGlyphsCache(8), renderXCache(16), renderYCache(16), renderLastX(0), renderLastY(0), renderWidthCache(16), renderHeightCache(16), renderLastId(0), renderTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), renderGlyphXCache(16), renderGlyphYCache(16), renderGlyphX(0), renderGlyphY(0), renderLastCompositeGlyphsData(0), setCacheParametersCache(8), lastIdCache(16), lastId(0) { unsigned int i; for (i = 0; i < 3; i++) { allocColorRGBCache[i] = new IntCache(8); convertSelectionAtomCache[i] = new IntCache(8); } for (i = 0; i < 4; i++) { clearAreaGeomCache[i] = new IntCache(8); } for (i = 0; i < 7; i++) { configureWindowAttrCache[i] = new IntCache(8); } for (i = 0; i < 6; i++) { copyAreaGeomCache[i] = new IntCache(8); copyPlaneGeomCache[i] = new IntCache(8); } for (i = 0; i < 23; i++) { if (CREATEGC_FIELD_WIDTH[i] > 16) { createGCAttrCache[i] = new IntCache(16); } else { createGCAttrCache[i] = new IntCache(CREATEGC_FIELD_WIDTH[i]); } } for (i = 0; i < 6; i++) { createWindowGeomCache[i] = new IntCache(8); } for (i = 0; i < 15; i++) { createWindowAttrCache[i] = new IntCache(8); } for (i = 0; i < 10; i++) { fillPolyXRelCache[i] = new IntCache(8); fillPolyXAbsCache[i] = new IntCache(8); fillPolyYRelCache[i] = new IntCache(8); fillPolyYAbsCache[i] = new IntCache(8); } for (i = 0; i < 8; i++) { fillPolyRecentX[i] = 0; fillPolyRecentY[i] = 0; } for (i = 0; i < 4; i++) { polyFillRectangleCacheX[i] = new IntCache(8); polyFillRectangleCacheY[i] = new IntCache(8); polyFillRectangleCacheWidth[i] = new IntCache(8); polyFillRectangleCacheHeight[i] = new IntCache(8); } for (i = 0; i < 2; i++) { polyLineCacheX[i] = new IntCache(8); polyLineCacheY[i] = new IntCache(8); } for (i = 0; i < 2; i++) { polyPointCacheX[i] = new IntCache(8); polyPointCacheY[i] = new IntCache(8); } for (i = 0; i < 4; i++) { polyRectangleGeomCache[i] = new IntCache(8); } for (i = 0; i < 2; i++) { polySegmentLastX[i] = 0; polySegmentLastY[i] = 0; } for (i = 0; i < 4; i++) { setClipRectanglesGeomCache[i] = new IntCache(8); } for (i = 0; i < 2; i++) { polyFillArcCacheX[i] = new IntCache(8); polyFillArcCacheY[i] = new IntCache(8); polyFillArcCacheWidth[i] = new IntCache(8); polyFillArcCacheHeight[i] = new IntCache(8); polyFillArcCacheAngle1[i] = new IntCache(8); polyFillArcCacheAngle2[i] = new IntCache(8); } for (i = 0; i < 2; i++) { polyArcCacheX[i] = new IntCache(8); polyArcCacheY[i] = new IntCache(8); polyArcCacheWidth[i] = new IntCache(8); polyArcCacheHeight[i] = new IntCache(8); polyArcCacheAngle1[i] = new IntCache(8); polyArcCacheAngle2[i] = new IntCache(8); } for (i = 0; i < 8; i++) { shapeDataCache[i] = new IntCache(8); } for (i = 0; i < 8; i++) { genericRequestDataCache[i] = new IntCache(8); } for (i = 0; i < 16; i++) { renderDataCache[i] = new IntCache(16); } for (i = 0; i < 16; i++) { renderCompositeGlyphsDataCache[i] = new IntCache(16); } for (i = 0; i < 3; i++) { renderCompositeDataCache[i] = new IntCache(16); } } ClientCache::~ClientCache() { unsigned int i; for (i = 0; i < 3; i++) { delete allocColorRGBCache[i]; delete convertSelectionAtomCache[i]; } for (i = 0; i < 4; i++) { delete clearAreaGeomCache[i]; } for (i = 0; i < 7; i++) { delete configureWindowAttrCache[i]; } for (i = 0; i < 6; i++) { delete copyAreaGeomCache[i]; delete copyPlaneGeomCache[i]; } for (i = 0; i < 23; i++) { delete createGCAttrCache[i]; } for (i = 0; i < 6; i++) { delete createWindowGeomCache[i]; } for (i = 0; i < 15; i++) { delete createWindowAttrCache[i]; } for (i = 0; i < 10; i++) { delete fillPolyXRelCache[i]; delete fillPolyXAbsCache[i]; delete fillPolyYRelCache[i]; delete fillPolyYAbsCache[i]; } for (i = 0; i < 4; i++) { delete polyFillRectangleCacheX[i]; delete polyFillRectangleCacheY[i]; delete polyFillRectangleCacheWidth[i]; delete polyFillRectangleCacheHeight[i]; } for (i = 0; i < 2; i++) { delete polyLineCacheX[i]; delete polyLineCacheY[i]; } for (i = 0; i < 2; i++) { delete polyPointCacheX[i]; delete polyPointCacheY[i]; } for (i = 0; i < 4; i++) { delete polyRectangleGeomCache[i]; } for (i = 0; i < 4; i++) { delete setClipRectanglesGeomCache[i]; } for (i = 0; i < 2; i++) { delete polyFillArcCacheX[i]; delete polyFillArcCacheY[i]; delete polyFillArcCacheWidth[i]; delete polyFillArcCacheHeight[i]; delete polyFillArcCacheAngle1[i]; delete polyFillArcCacheAngle2[i]; } for (i = 0; i < 2; i++) { delete polyArcCacheX[i]; delete polyArcCacheY[i]; delete polyArcCacheWidth[i]; delete polyArcCacheHeight[i]; delete polyArcCacheAngle1[i]; delete polyArcCacheAngle2[i]; } for (i = 0; i < 8; i++) { delete shapeDataCache[i]; } for (i = 0; i < 8; i++) { delete genericRequestDataCache[i]; } for (i = 0; i < 16; i++) { delete renderDataCache[i]; } for (i = 0; i < 16; i++) { delete renderCompositeGlyphsDataCache[i]; } for (i = 0; i < 3; i++) { delete renderCompositeDataCache[i]; } } nxcomp/RenderCompositeGlyphsCompat.cpp0000644000076400007640000004447411323113027020501 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderCompositeGlyphsCompat.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; // // The offset points 8 bytes after // the beginning of the data part. // #ifdef DEBUG *logofs << name() << ": Encoding value " << ((size - (MESSAGE_OFFSET - 8)) >> 2) << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue((size - (MESSAGE_OFFSET - 8)) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); #ifdef DEBUG *logofs << name() << ": Decoded value " << size << ".\n" << logofs_flush; #endif size = (MESSAGE_OFFSET - 8) + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, clientCache -> renderFormatCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 29, clientCache -> renderGlyphSetCache); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 24, bigEndian), clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 26, bigEndian), clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); // // Try to save as many bits as possible by // encoding the information about the first // set of glyphs. // if (size >= MESSAGE_OFFSET) { unsigned int numGlyphs = *(buffer + 28); encodeBuffer.encodeCachedValue(numGlyphs, 8, clientCache -> renderNumGlyphsCache); encodeBuffer.encodeCachedValue(GetUINT(buffer + 32, bigEndian), 16, clientCache -> renderWidthCache, 11); encodeBuffer.encodeCachedValue(GetUINT(buffer + 34, bigEndian), 16, clientCache -> renderHeightCache, 11); // // Only manage the first set of glyphs, // that is in most cases the only one. // switch (*(buffer + 1)) { case X_RenderCompositeGlyphs8: { if (numGlyphs & 0x03) { memset((unsigned char *) buffer + MESSAGE_OFFSET + numGlyphs, '\0', RoundUp4(numGlyphs) - numGlyphs); } break; } case X_RenderCompositeGlyphs16: { if (numGlyphs & 0x01) { memset((unsigned char *) buffer + MESSAGE_OFFSET + (numGlyphs * 2), '\0', RoundUp4(numGlyphs * 2) - numGlyphs * 2); } break; } } #ifdef TEST if (*(buffer + (size - 1)) != '\0') { *logofs << name() << ": WARNING! Final byte is non-zero with size " << size << " and " << (unsigned int) *(buffer + 28) << " glyphs.\n" << logofs_flush; } else { *logofs << name() << ": Final byte is zero with size " << size << " and " << (unsigned int) *(buffer + 28) << " glyphs.\n" << logofs_flush; } #endif } #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 12, bigEndian); decodeBuffer.decodeCachedValue(value, 32, clientCache -> renderFormatCache); PutULONG(value, buffer + 16, bigEndian); decodeBuffer.decodeCachedValue(value, 29, clientCache -> renderGlyphSetCache); PutULONG(value, buffer + 20, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); PutUINT(clientCache -> renderLastX, buffer + 24, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); PutUINT(clientCache -> renderLastY, buffer + 26, bigEndian); if (size >= MESSAGE_OFFSET) { decodeBuffer.decodeCachedValue(value, 8, clientCache -> renderNumGlyphsCache); *(buffer + 28) = value; decodeBuffer.decodeCachedValue(value, 16, clientCache -> renderWidthCache, 11); PutUINT(value, buffer + 32, bigEndian); decodeBuffer.decodeCachedValue(value, 16, clientCache -> renderHeightCache, 11); PutUINT(value, buffer + 34, bigEndian); } #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; switch (*(buffer + 1)) { case X_RenderCompositeGlyphs8: { clientCache -> renderTextCompressor.reset(); const unsigned char *next = buffer + MESSAGE_OFFSET; for (unsigned int i = MESSAGE_OFFSET; i < size; i++) { #ifdef DEBUG *logofs << name() << ": Encoding char with i = " << i << ".\n" << logofs_flush; #endif clientCache -> renderTextCompressor. encodeChar(*next++, encodeBuffer); } break; } case X_RenderCompositeGlyphs16: { for (unsigned int i = MESSAGE_OFFSET; i < size; i += 2) { value = GetUINT(buffer + i, bigEndian); #ifdef DEBUG *logofs << name() << ": Encoding short with i = " << i << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(value, 16, *clientCache -> renderCompositeGlyphsDataCache[clientCache -> renderLastCompositeGlyphsData]); clientCache -> renderLastCompositeGlyphsData = value % 16; } break; } default: { for (unsigned int i = MESSAGE_OFFSET; i < size; i += 4) { value = GetULONG(buffer + i, bigEndian); #ifdef DEBUG *logofs << name() << ": Encoding long with i = " << i << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(value, 32, *clientCache -> renderCompositeGlyphsDataCache[clientCache -> renderLastCompositeGlyphsData]); clientCache -> renderLastCompositeGlyphsData = value % 16; } break; } } #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; switch (*(buffer + 1)) { case X_RenderCompositeGlyphs8: { clientCache -> renderTextCompressor.reset(); unsigned char *next = buffer + MESSAGE_OFFSET; for (unsigned int i = MESSAGE_OFFSET; i < size; i++) { #ifdef DEBUG *logofs << name() << ": Decoding char with i = " << i << ".\n" << logofs_flush; #endif *next++ = clientCache -> renderTextCompressor. decodeChar(decodeBuffer); } break; } case X_RenderCompositeGlyphs16: { for (unsigned int i = MESSAGE_OFFSET; i < size; i += 2) { #ifdef DEBUG *logofs << name() << ": Decoding short with i = " << i << ".\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, *clientCache -> renderCompositeGlyphsDataCache[clientCache -> renderLastCompositeGlyphsData]); PutUINT(value, buffer + i, bigEndian); clientCache -> renderLastCompositeGlyphsData = value % 16; } break; } default: { for (unsigned int i = MESSAGE_OFFSET; i < size; i += 4) { #ifdef DEBUG *logofs << name() << ": Decoding long with i = " << i << ".\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 32, *clientCache -> renderCompositeGlyphsDataCache[clientCache -> renderLastCompositeGlyphsData]); PutULONG(value, buffer + i, bigEndian); clientCache -> renderLastCompositeGlyphsData = value % 16; } break; } } #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.composite_glyphs_compat.type = *(buffer + 1); renderExtension -> data.composite_glyphs_compat.op = *(buffer + 4); renderExtension -> data.composite_glyphs_compat.src_id = GetULONG(buffer + 8, bigEndian); renderExtension -> data.composite_glyphs_compat.dst_id = GetULONG(buffer + 12, bigEndian); renderExtension -> data.composite_glyphs_compat.format = GetULONG(buffer + 16, bigEndian); renderExtension -> data.composite_glyphs_compat.set_id = GetULONG(buffer + 20, bigEndian); renderExtension -> data.composite_glyphs_compat.src_x = GetUINT(buffer + 24, bigEndian); renderExtension -> data.composite_glyphs_compat.src_y = GetUINT(buffer + 26, bigEndian); if (size >= MESSAGE_OFFSET) { renderExtension -> data.composite_glyphs_compat.num_elm = *(buffer + 28); renderExtension -> data.composite_glyphs_compat.delta_x = GetUINT(buffer + 32, bigEndian); renderExtension -> data.composite_glyphs_compat.delta_y = GetUINT(buffer + 34, bigEndian); } #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.composite_glyphs_compat.type << " size is " << renderExtension -> size_ << " identity size " << renderExtension -> i_size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.composite_glyphs_compat.type; *(buffer + 4) = renderExtension -> data.composite_glyphs_compat.op; PutULONG(renderExtension -> data.composite_glyphs_compat.src_id, buffer + 8, bigEndian); PutULONG(renderExtension -> data.composite_glyphs_compat.dst_id, buffer + 12, bigEndian); PutULONG(renderExtension -> data.composite_glyphs_compat.format, buffer + 16, bigEndian); PutULONG(renderExtension -> data.composite_glyphs_compat.set_id, buffer + 20, bigEndian); PutUINT(renderExtension -> data.composite_glyphs_compat.src_x, buffer + 24, bigEndian); PutUINT(renderExtension -> data.composite_glyphs_compat.src_y, buffer + 26, bigEndian); if (size >= MESSAGE_OFFSET) { *(buffer + 28) = renderExtension -> data.composite_glyphs_compat.num_elm; PutUINT(renderExtension -> data.composite_glyphs_compat.delta_x, buffer + 32, bigEndian); PutUINT(renderExtension -> data.composite_glyphs_compat.delta_y, buffer + 34, bigEndian); #ifdef DEBUG *logofs << name() << ": Len is " << (unsigned int) *(buffer + 28) << " delta X is " << GetUINT(buffer + 32, bigEndian) << " delta Y is " << GetUINT(buffer + 34, bigEndian) << ".\n" << logofs_flush; *logofs << name() << ": Pad 1 is " << (unsigned int) *(buffer + 29) << " pad 2 and 3 are " << GetUINT(buffer + 30, bigEndian) << ".\n" << logofs_flush; #endif } #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.composite_glyphs_compat.type << " size is " << renderExtension -> size_ << " identity size " << renderExtension -> i_size_ << ".\n" << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { // // Include minor opcode, size and // the composite operator in the // identity. // md5_append(md5_state, buffer + 1, 4); // // Include the format and the source // x and y fields. // md5_append(md5_state, buffer + 16, 4); md5_append(md5_state, buffer + 24, 4); // // Include the number of glyphs. // if (size >= MESSAGE_OFFSET) { md5_append(md5_state, buffer + 28, 1); } } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs_compat.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.composite_glyphs_compat.src_id = renderExtension -> data.composite_glyphs_compat.src_id; encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs_compat.dst_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.composite_glyphs_compat.dst_id = renderExtension -> data.composite_glyphs_compat.dst_id; encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs_compat.set_id, 29, clientCache -> renderGlyphSetCache); cachedRenderExtension -> data.composite_glyphs_compat.set_id = renderExtension -> data.composite_glyphs_compat.set_id; if (renderExtension -> size_ >= MESSAGE_OFFSET) { encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs_compat.delta_x, 16, clientCache -> renderWidthCache, 11); cachedRenderExtension -> data.composite_glyphs_compat.delta_x = renderExtension -> data.composite_glyphs_compat.delta_x; encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs_compat.delta_y, 16, clientCache -> renderHeightCache, 11); cachedRenderExtension -> data.composite_glyphs_compat.delta_y = renderExtension -> data.composite_glyphs_compat.delta_y; } #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.composite_glyphs_compat.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs_compat.src_id, clientCache -> renderSrcPictureCache); decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs_compat.dst_id, clientCache -> renderSrcPictureCache); decodeBuffer.decodeCachedValue(renderExtension -> data.composite_glyphs_compat.set_id, 29, clientCache -> renderGlyphSetCache); if (renderExtension -> size_ >= MESSAGE_OFFSET) { unsigned int value; decodeBuffer.decodeCachedValue(value, 16, clientCache -> renderWidthCache, 11); renderExtension -> data.composite_glyphs_compat.delta_x = value; decodeBuffer.decodeCachedValue(value, 16, clientCache -> renderHeightCache, 11); renderExtension -> data.composite_glyphs_compat.delta_y = value; } #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.composite_glyphs_compat.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/RenderFreePicture.h0000644000076400007640000000464211323113031016052 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderFreePicture_H #define RenderFreePicture_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderFreePicture" #undef MESSAGE_STORE #define MESSAGE_STORE RenderFreePictureStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 8 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 0 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 0 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderFreePicture_H */ nxcomp/XidCache.h0000644000076400007640000000276311323113031014147 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef XidCache_H #define XidCache_H #include "IntCache.h" class XidCache { friend class EncodeBuffer; friend class DecodeBuffer; public: XidCache(); ~XidCache(); private: IntCache *base_[256]; unsigned int slot_; unsigned int last_; }; #endif /* XidCache_H */ nxcomp/GetImageReply.cpp0000644000076400007640000001246511323113030015527 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "GetImageReply.h" #include "ServerCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // GetImageReplyStore::GetImageReplyStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = GETIMAGEREPLY_ENABLE_CACHE; enableData = GETIMAGEREPLY_ENABLE_DATA; enableSplit = GETIMAGEREPLY_ENABLE_SPLIT; enableCompress = GETIMAGEREPLY_ENABLE_COMPRESS; if (control -> isProtoStep7() == 1) { enableCompress = GETIMAGEREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7; } dataLimit = GETIMAGEREPLY_DATA_LIMIT; dataOffset = GETIMAGEREPLY_DATA_OFFSET; cacheSlots = GETIMAGEREPLY_CACHE_SLOTS; cacheThreshold = GETIMAGEREPLY_CACHE_THRESHOLD; cacheLowerThreshold = GETIMAGEREPLY_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } GetImageReplyStore::~GetImageReplyStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int GetImageReplyStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message; // // Here is the fingerprint. // getImageReply -> depth = *(buffer + 1); getImageReply -> visual = GetULONG(buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int GetImageReplyStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = getImageReply -> depth; PutULONG(getImageReply -> visual, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void GetImageReplyStore::dumpIdentity(const Message *message) const { #ifdef DUMP GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message; *logofs << name() << ": Identity depth " << (unsigned) getImageReply -> depth << ", visual " << getImageReply -> visual << ", size " << getImageReply -> size_ << ".\n"; #endif } void GetImageReplyStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // Field depth. // md5_append(md5_state_, buffer + 1, 1); } void GetImageReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { // // Encode the variant part. // GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message; ServerCache *serverCache = (ServerCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << getImageReply -> visual << " as visual field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(getImageReply -> visual, 29, serverCache -> visualCache); } void GetImageReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { // // Decode the variant part. // GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message; ServerCache *serverCache = (ServerCache *) channelCache; decodeBuffer.decodeCachedValue(getImageReply -> visual, 29, serverCache -> visualCache); } nxcomp/CharCache.h0000644000076400007640000000423711323113030014275 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef CharCache_H #define CharCache_H // // CharCache is a counterpart of IntCache that is // optimized for use in compressing text composed // of 8-bit characters. // class CharCache { public: CharCache() : length_(0) { } ~CharCache() { } unsigned int getSize() const { return (unsigned int) length_; } int lookup(unsigned char value, unsigned int &index); // // This can be inlined as it is only // called by decodeCachedValue(). // unsigned int get(unsigned int index) { unsigned char result = buffer_[index]; if (index != 0) { unsigned int i = index; unsigned int target = (i >> 1); do { buffer_[i] = buffer_[i - 1]; i--; } while (i > target); buffer_[target] = result; } return (unsigned int) result; } void insert(unsigned char value); private: unsigned char length_; unsigned char buffer_[7]; }; #endif /* CharCache_H */ nxcomp/PolySegment.cpp0000644000076400007640000001162211323113027015277 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "PolySegment.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int PolySegmentStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { PolySegmentMessage *polySegment = (PolySegmentMessage *) message; // // Here is the fingerprint. // polySegment -> drawable = GetULONG(buffer + 4, bigEndian); polySegment -> gcontext = GetULONG(buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int PolySegmentStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { PolySegmentMessage *polySegment = (PolySegmentMessage *) message; // // Fill all the message's fields. // PutULONG(polySegment -> drawable, buffer + 4, bigEndian); PutULONG(polySegment -> gcontext, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void PolySegmentStore::dumpIdentity(const Message *message) const { #ifdef DUMP PolySegmentMessage *polySegment = (PolySegmentMessage *) message; *logofs << name() << ": Identity drawable " << polySegment -> drawable << ", gcontext " << polySegment -> gcontext << ", size " << polySegment -> size_ << ".\n" << logofs_flush; #endif } void PolySegmentStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { } void PolySegmentStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { PolySegmentMessage *polySegment = (PolySegmentMessage *) message; PolySegmentMessage *cachedPolySegment = (PolySegmentMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << polySegment -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polySegment -> drawable, clientCache -> drawableCache); cachedPolySegment -> drawable = polySegment -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << polySegment -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polySegment -> gcontext, clientCache -> gcCache); cachedPolySegment -> gcontext = polySegment -> gcontext; } void PolySegmentStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { PolySegmentMessage *polySegment = (PolySegmentMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); polySegment -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polySegment -> drawable << " as drawable field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); polySegment -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polySegment -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } nxcomp/RenderGenericRequest.h0000644000076400007640000000676111323113030016565 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderGenericRequest_H #define RenderGenericRequest_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP class RenderGenericRequestStore : public RenderMinorExtensionStore { public: virtual const char *name() const { return "RenderGenericRequest"; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return RENDEREXTENSION_DATA_OFFSET; } virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, unsigned char type, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, md5_state_t *md5_state, int bigEndian) const; }; #endif /* RenderGenericRequest_H */ nxcomp/Unpack.h0000644000076400007640000001055511323113031013716 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Unpack_H #define Unpack_H #include "NXpack.h" #include "Z.h" #define LSBFirst 0 #define MSBFirst 1 #define SPLIT_PATTERN 0x88 typedef ColorMask T_colormask; // // Pixel geometry of channel's display. // typedef struct { unsigned int depth1_bpp; unsigned int depth4_bpp; unsigned int depth8_bpp; unsigned int depth16_bpp; unsigned int depth24_bpp; unsigned int depth32_bpp; unsigned int red_mask; unsigned int green_mask; unsigned int blue_mask; unsigned int image_byte_order; unsigned int bitmap_bit_order; unsigned int scanline_unit; unsigned int scanline_pad; } T_geometry; // // Colormap is used to remap colors // from source to destination depth. // typedef struct { unsigned int entries; unsigned int *data; } T_colormap; // // Alpha channel data is added to 32 // bits images at the time they are // unpacked. // typedef struct { unsigned int entries; unsigned char *data; } T_alpha; // // The ZLIB stream structure used for // the decompression. // extern z_stream unpackStream; // // Initialize the ZLIB stream used for // decompression. // void UnpackInit(); // // Free the ZLIB stream. // void UnpackDestroy(); // // Get the destination bits per pixel // based on the drawable depth. // int UnpackBitsPerPixel(T_geometry *geometry, unsigned int depth); // // Unpack the source data into the X // bitmap. // int Unpack8(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size); int Unpack16(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size); int Unpack24(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size); int Unpack8(T_geometry *geometry, T_colormap *colormap, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size); int Unpack15(T_geometry *geometry, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size); int Unpack16(T_geometry *geometry, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size); int Unpack24(T_geometry *geometry, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size); #endif /* Unpack_H */ nxcomp/CreatePixmap.cpp0000644000076400007640000002100711323113027015411 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "CreatePixmap.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Constructors and destructors. // CreatePixmapStore::CreatePixmapStore() : MessageStore() { enableCache = CREATEPIXMAP_ENABLE_CACHE; enableData = CREATEPIXMAP_ENABLE_DATA; enableSplit = CREATEPIXMAP_ENABLE_SPLIT; enableCompress = CREATEPIXMAP_ENABLE_COMPRESS; dataLimit = CREATEPIXMAP_DATA_LIMIT; dataOffset = CREATEPIXMAP_DATA_OFFSET; cacheSlots = CREATEPIXMAP_CACHE_SLOTS; cacheThreshold = CREATEPIXMAP_CACHE_THRESHOLD; cacheLowerThreshold = CREATEPIXMAP_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } CreatePixmapStore::~CreatePixmapStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int CreatePixmapStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(*(buffer + 1), 8, clientCache -> depthCache); encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> lastId, clientCache -> lastIdCache, clientCache -> drawableCache, clientCache -> freeDrawableCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> windowCache); encodeBuffer.encodeCachedValue(GetUINT(buffer + 12, bigEndian), 16, clientCache -> createPixmapXCache, 8); encodeBuffer.encodeCachedValue(GetUINT(buffer + 14, bigEndian), 16, clientCache -> createPixmapYCache, 8); #ifdef TEST *logofs << name() << ": Encoded message. Size is " << size << ".\n" << logofs_flush; #endif return 1; } int CreatePixmapStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; unsigned char cValue; unsigned int value; size = 16; buffer = writeBuffer -> addMessage(size); decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); *(buffer + 1) = cValue; decodeBuffer.decodeNewXidValue(value, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> drawableCache, clientCache -> freeDrawableCache); PutULONG(value, buffer + 4, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> windowCache); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeCachedValue(value, 16, clientCache -> createPixmapXCache, 8); PutUINT(value, buffer + 12, bigEndian); decodeBuffer.decodeCachedValue(value, 16, clientCache -> createPixmapYCache, 8); PutUINT(value, buffer + 14, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Size is " << size << ".\n" << logofs_flush; #endif return 1; } int CreatePixmapStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message; createPixmap -> depth = *(buffer + 1); createPixmap -> id = GetULONG(buffer + 4, bigEndian); createPixmap -> drawable = GetULONG(buffer + 8, bigEndian); createPixmap -> width = GetUINT(buffer + 12, bigEndian); createPixmap -> height = GetUINT(buffer + 14, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Size is " << createPixmap -> size_ << " identity is " << createPixmap -> i_size_ << ".\n" << logofs_flush; #endif return 1; } int CreatePixmapStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message; *(buffer + 1) = createPixmap -> depth; PutULONG(createPixmap -> id, buffer + 4, bigEndian); PutULONG(createPixmap -> drawable, buffer + 8, bigEndian); PutUINT(createPixmap -> width, buffer + 12, bigEndian); PutUINT(createPixmap -> height, buffer + 14, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Size is " << createPixmap -> size_ << " identity is " << createPixmap -> i_size_ << ".\n" << logofs_flush; #endif return 1; } void CreatePixmapStore::dumpIdentity(const Message *message) const { #ifdef DUMP #ifdef WARNING *logofs << name() << ": WARNING! Dump of identity not implemented.\n" << logofs_flush; #endif #endif } void CreatePixmapStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 1, 1); md5_append(md5_state_, buffer + 8, 8); } void CreatePixmapStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message; CreatePixmapMessage *cachedCreatePixmap = (CreatePixmapMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeNewXidValue(createPixmap -> id, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> drawableCache, clientCache -> freeDrawableCache); cachedCreatePixmap -> id = createPixmap -> id; #ifdef TEST *logofs << name() << ": Encoded update. Size is " << createPixmap -> size_ << " identity is " << createPixmap -> i_size_ << ".\n" << logofs_flush; #endif } void CreatePixmapStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeNewXidValue(createPixmap -> id, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> drawableCache, clientCache -> freeDrawableCache); #ifdef TEST *logofs << name() << ": Decoded update. Size is " << createPixmap -> size_ << " identity is " << createPixmap -> i_size_ << ".\n" << logofs_flush; #endif } nxcomp/Proxy.h0000644000076400007640000006126611323113027013630 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Proxy_H #define Proxy_H #include #ifdef _AIX #include #endif #include "Misc.h" #include "Timestamp.h" #include "List.h" #include "Channel.h" #include "Transport.h" #include "EncodeBuffer.h" #include "ProxyReadBuffer.h" // // Forward declaration as we // need a pointer. // class Agent; // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Log the important tracepoints related // to writing packets to the peer proxy. // #undef FLUSH // // Codes used for control messages in // proxy-to-proxy protocol. // // The following codes are currently // unused. // // code_alert_reply, // code_reset_request, // code_reset_reply, // code_load_reply, // code_save_reply, // code_shutdown_reply, // code_configuration_request, // code_configuration_reply. // // These are for compatibility with // old versions. // // code_sync_request, // code_sync_reply, // // The code_new_aux_connection should not // be used anymore. Auxiliary X connections // are treated as normal X channels since // version 1.5.0. // typedef enum { code_new_x_connection, code_new_cups_connection, code_new_aux_connection, code_new_smb_connection, code_new_media_connection, code_switch_connection, code_drop_connection, code_finish_connection, code_begin_congestion, code_end_congestion, code_alert_request, code_alert_reply, code_reset_request, code_reset_reply, code_load_request, code_load_reply, code_save_request, code_save_reply, code_shutdown_request, code_shutdown_reply, code_control_token_request, code_control_token_reply, code_configuration_request, code_configuration_reply, code_statistics_request, code_statistics_reply, code_new_http_connection, code_sync_request, code_sync_reply, code_new_font_connection, code_new_slave_connection, code_finish_listeners, code_split_token_request, code_split_token_reply, code_data_token_request, code_data_token_reply, code_last_tag } T_proxy_code; typedef enum { operation_in_negotiation, operation_in_messages, operation_in_configuration, operation_in_statistics, operation_last_tag } T_proxy_operation; typedef enum { frame_ping, frame_data, } T_frame_type; typedef enum { token_control, token_split, token_data } T_token_type; typedef enum { load_if_any, load_if_first } T_load_type; class Proxy { public: // // Maximum number of supported channels. // static const int CONNECTIONS_LIMIT = 256; // // Numboer of token types. // static const int TOKEN_TYPES = 3; // // Lenght of buffer we use to add our // control messages plus the length of // the data frame. // static const int CONTROL_CODES_LENGTH = ENCODE_BUFFER_PREFIX_SIZE - 5; static const int CONTROL_CODES_THRESHOLD = CONTROL_CODES_LENGTH - 9; Proxy(int fd); virtual ~Proxy(); // // Inform the proxy that the negotiation phase is // completed and that it can start handling binary // messages. // int setOperational(); int getOperational() { return (operation_ != operation_in_negotiation); } int setReadDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax); int setWriteDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax); // // Perform the operation on the proxy // link or its own channels. // int handleRead(int &resultFds, fd_set &fdSet); int handleFlush(int &resultFds, fd_set &fdSet); int handleRead(); int handleRead(int fd, const char *data = NULL, int size = 0); int handleEvents(); int handleFlush(); int handleFlush(int fd); int handlePing(); int handleFinish(); int handleShutdown(); int handleStatistics(int type, ostream *statofs); int handleAlert(int alert); int handleRotate() { activeChannels_.rotate(); return 1; } int handleChannelConfiguration(); int handleSocketConfiguration(); int handleLinkConfiguration(); int handleCacheConfiguration(); // // These must be called just after initialization to // tell to the proxy where the network connections // have to be forwarded. // virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily, sockaddr * xServerAddr, unsigned int xServerAddrLength) = 0; virtual void handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort, int httpServerPort, const char *fontServerPort) = 0; // // Create new tunneled channels. // virtual int handleNewConnection(T_channel_type type, int clientFd) = 0; virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId) = 0; virtual int handleNewAgentConnection(Agent *agent) = 0; virtual int handleNewXConnection(int clientFd) = 0; virtual int handleNewXConnectionFromProxy(int channelId) = 0; int handleNewGenericConnection(int clientFd, T_channel_type type, const char *label); int handleNewGenericConnectionFromProxy(int channelId, T_channel_type type, const char *hostname, int port, const char *label); int handleNewGenericConnectionFromProxy(int channelId, T_channel_type type, const char *hostname, const char *path, const char *label); int handleNewSlaveConnection(int clientFd); int handleNewSlaveConnectionFromProxy(int channelId); // // Force closure of channels. // int handleCloseConnection(int clientFd); int handleCloseAllXConnections(); int handleCloseAllListeners(); // // Called when the loop has replaced // or closed a previous alert. // void handleResetAlert(); // // Handle the persistent cache. // virtual int handleLoad(T_load_type type) = 0; virtual int handleSave() = 0; protected: // // Timeout related data: // // flush // split // motion // // Timeouts in milliseconds after which the // proxy will have to perform the operation. // // readTs, writeTs // // Timestamp of last packet received or sent // to remote proxy. Used to detect lost con- // nection. // // loopTs // // Timestamp of last loop completed by the // proxy // // pingTs // // Timestamp of last ping request sent to the // remote peer. // // alertTs // // Timestamp of last 'no data received' alert // dialog shown to the user. // // loadTs // // Were message stores populated from data on // disk. // // splitTs // motionTs // // Timestamps of the last operation of this // kind handled by the proxy. // typedef struct { int split; int motion; T_timestamp readTs; T_timestamp writeTs; T_timestamp loopTs; T_timestamp pingTs; T_timestamp alertTs; T_timestamp loadTs; T_timestamp splitTs; T_timestamp motionTs; } T_proxy_timeouts; // // Bytes accumulated so far while waiting // to send the next token, number of tokens // remaining for each token type and other // token related information. // typedef struct { int size; int limit; int bytes; int remaining; T_proxy_code request; T_proxy_code reply; T_token_type type; } T_proxy_token; int handlePostConnectionFromProxy(int channelId, int serverFd, T_channel_type type, const char *label); int handleDrain(); int handleFrame(T_frame_type type); int handleFinish(int channelId); int handleDrop(int channelId); int handleFinishFromProxy(int channelId); int handleDropFromProxy(int channelId); int handleStatisticsFromProxy(int type); int handleStatisticsFromProxy(const unsigned char *message, unsigned int length); int handleNegotiation(const unsigned char *message, unsigned int length); int handleNegotiationFromProxy(const unsigned char *message, unsigned int length); int handleToken(T_frame_type type); int handleTokenFromProxy(T_proxy_token &token, int count); int handleTokenReplyFromProxy(T_proxy_token &token, int count); int handleSyncFromProxy(int channelId); int handleSwitch(int channelId); int handleControl(T_proxy_code code, int data = -1); int handleControlFromProxy(const unsigned char *message); // // Interleave reads of the X server // events while writing data to the // remote proxy. // virtual int handleAsyncEvents() = 0; // // Timer related functions. // protected: void setTimer(int value) { SetTimer(value); } void resetTimer() { ResetTimer(); timer_ = 0; } public: void handleTimer() { timer_ = 1; } int getTimer() { return timer_; } // // Can the channel consume data and the // proxy produce more output? // int canRead(int fd) const { return (isTimeToRead() == 1 && isTimeToRead(getChannel(fd)) == 1); } // // Can the proxy read more data from its // peer? // int canRead() const { return (transport_ -> readable() != 0); } int canFlush() const { return (encodeBuffer_.getLength() + controlLength_ + transport_ -> length() + transport_ -> flushable() > 0); } int needFlush(int channelId) const { return (outputChannel_ == channelId && encodeBuffer_.getLength() > 0); } int needFlush() const { return (encodeBuffer_.getLength() > 0); } int shouldFlush() const { if ((int) ((encodeBuffer_.getLength() + controlLength_) / statistics -> getStreamRatio()) >= control -> TokenSize) { #if defined(TEST) || defined(INFO) || defined(FLUSH) *logofs << "Proxy: FLUSH! Requesting a flush with " << (encodeBuffer_.getLength() + controlLength_) << " bytes and stream ratio " << (double) statistics -> getStreamRatio() << ".\n" << logofs_flush; #endif return 1; } #if defined(TEST) || defined(INFO) || defined(FLUSH) *logofs << "Proxy: Not requesting a flush with " << (encodeBuffer_.getLength() + controlLength_) << " bytes and stream ratio " << (double) statistics -> getStreamRatio() << ".\n" << logofs_flush; #endif return 0; } int needDrain() const { return (congestion_ == 1 || transport_ -> length() > control -> TransportProxyBufferThreshold); } int getFd() const { return fd_; } int getFlushable(int fd) const { if (fd == fd_) { return (encodeBuffer_.getLength() + controlLength_ + transport_ -> flushable()); } return 0; } int getSplitSize() { return (clientStore_ != NULL ? clientStore_ -> getSplitTotalSize() : 0); } int getSplitStorageSize() { return (clientStore_ != NULL ? clientStore_ -> getSplitTotalStorageSize() : 0); } // // Returns the number of active channels // that currently managed by this proxy. // int getChannels(T_channel_type type = channel_none); // // If descriptor corresponds to a valid // channel, returns the type of traffic // carried by it. // T_channel_type getType(int fd); // // Given a channel type, returns the // literal name. // const char *getTypeName(T_channel_type type); // // Get a convenient name for 'localhost'. // const char *getComputerName(); // // Set if we have initiated the shutdown // procedure and if the shutdown request // has been received from the remote. // int getFinish() const { return finish_; } int getShutdown() const { return shutdown_; } // // Interfaces to the transport buffers. // int getLength(int fd) const { if (fd == fd_) { return transport_ -> length(); } int channelId = getChannel(fd); if (channelId < 0 || channels_[channelId] == NULL) { return 0; } return transports_[channelId] -> length(); } int getPending(int fd) const { if (fd == fd_) { return transport_ -> pending(); } int channelId = getChannel(fd); if (channelId < 0 || channels_[channelId] == NULL) { return 0; } return transports_[channelId] -> pending(); } // // Check if the proxy or the given channel // has data in the buffer because of a // blocking write. // int getBlocked(int fd) const { if (fd == fd_) { return transport_ -> blocked(); } int channelId = getChannel(fd); if (channelId < 0 || channels_[channelId] == NULL) { return 0; } return transports_[channelId] -> blocked(); } // // Check if the proxy or the given channel has // data to read. // int getReadable(int fd) const { if (fd == fd_) { return transport_ -> readable(); } int channelId = getChannel(fd); if (channelId < 0 || channels_[channelId] == NULL) { return 0; } return transports_[channelId] -> readable(); } // // Return a vale between 0 and 9 in the case // of the proxy descriptor. // int getCongestion(int fd) const { if (fd == fd_) { return (agent_ != nothing && congestions_[agent_] == 1 ? 9 : (int) statistics -> getCongestionInFrame()); } int channelId = getChannel(fd); if (channelId < 0 || channels_[channelId] == NULL) { return 0; } return channels_[channelId] -> getCongestion(); } // // Let the statistics class get info // from the message stores. // const ClientStore * const getClientStore() const { return clientStore_; } const ServerStore * const getServerStore() const { return serverStore_; } // // These can be called asynchronously by // channels during their read or write // loop. // int handleAsyncRead(int fd) { return handleRead(fd); } int handleAsyncCongestion(int fd) { int channelId = getChannel(fd); return handleControl(code_begin_congestion, channelId); } int handleAsyncDecongestion(int fd) { int channelId = getChannel(fd); return handleControl(code_end_congestion, channelId); } int handleAsyncSplit(int fd, Split *split) { return channels_[getChannel(fd)] -> handleSplitEvent(encodeBuffer_, split); } int handleAsyncInit() { return handleFlush(); } int handleAsyncPriority() { if (control -> FlushPriority == 1) { return handleFlush(); } return 0; } int canAsyncFlush() const { return shouldFlush(); } int handleAsyncFlush() { return handleFlush(); } int handleAsyncSwitch(int fd) { return handleSwitch(getChannel(fd)); } int handleAsyncKeeperCallback() { KeeperCallback(); return 1; } // // Internal interfaces used to verify the // availability of channels and the proxy // link. // protected: int isTimeToRead() const { if (congestion_ == 0 && transport_ -> blocked() == 0) { return 1; } else { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Can't read from channels " << "with congestion " << congestion_ << " and blocked " << transport_ -> blocked() << ".\n" << logofs_flush; #endif return 0; } } int isTimeToRead(int channelId) const { if (channelId >= 0 && channelId < CONNECTIONS_LIMIT && channels_[channelId] != NULL && congestions_[channelId] == 0) { if (channels_[channelId] -> getType() == channel_x11 || tokens_[token_data].remaining > 0 || channels_[channelId] -> getFinish() == 1) { return 1; } #if defined(TEST) || defined(INFO) *logofs << "Proxy: Can't read from generic " << "descriptor FD#" << getFd(channelId) << " channel ID#" << channelId << " with " << tokens_[token_data].remaining << " data tokens remaining.\n" << logofs_flush; #endif return 0; } #if defined(TEST) || defined(INFO) if (channelId < 0 || channelId >= CONNECTIONS_LIMIT || channels_[channelId] == NULL) { *logofs << "Proxy: WARNING! No valid channel for ID#" << channelId << ".\n" << logofs_flush; } else if (channels_[channelId] -> getFinish()) { *logofs << "Proxy: Can't read from finishing " << "descriptor FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; } else if (congestions_[channelId] == 1) { *logofs << "Proxy: Can't read from congested " << "descriptor FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; } #endif return 0; } // // Handle the flush and split timeouts. // All these functions should round up // to the value of the latency timeout // to save a further loop. // protected: int isTimeToSplit() const { if (isTimestamp(timeouts_.splitTs) && getTimeToNextSplit() <= control -> LatencyTimeout) { if (tokens_[token_split].remaining > 0) { return 1; } #if defined(TEST) || defined(INFO) *logofs << "Proxy: WARNING! Can't encode splits " << "with " << tokens_[token_split].remaining << " split tokens remaining.\n" << logofs_flush; #endif } return 0; } int isTimeToMotion() const { return (isTimestamp(timeouts_.motionTs) && getTimeToNextMotion() <= control -> LatencyTimeout); } int getTimeToNextSplit() const { #if defined(TEST) || defined(INFO) || defined(FLUSH) if (isTimestamp(timeouts_.splitTs) == 0) { #ifdef PANIC *logofs << "Proxy: PANIC! No split timeout was set " << "for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": No split timeout was set " << "for proxy FD#" << fd_ << ".\n"; HandleCleanup(); } #endif int diffTs = timeouts_.split - diffTimestamp(timeouts_.splitTs, getTimestamp()); return (diffTs > 0 ? diffTs : 0); } int getTimeToNextMotion() const { #if defined(TEST) || defined(INFO) || defined(FLUSH) if (isTimestamp(timeouts_.motionTs) == 0) { #ifdef PANIC *logofs << "Proxy: PANIC! No motion timeout was set " << "for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": No motion timeout was set " << "for proxy FD#" << fd_ << ".\n"; HandleCleanup(); } #endif int diffTs = timeouts_.motion - diffTimestamp(timeouts_.motionTs, getTimestamp()); return (diffTs > 0 ? diffTs : 0); } protected: // // Implement persistence of cache on disk. // virtual int handleLoadFromProxy() = 0; virtual int handleSaveFromProxy() = 0; int handleLoadStores(); int handleSaveStores(); char *handleSaveAllStores(const char *savePath) const; virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient) const = 0; int handleSaveVersion(unsigned char *buffer, int &major, int &minor, int &patch) const; void handleFailOnSave(const char *fullName, const char *failContext) const; const char *handleLoadAllStores(const char *loadPath, const char *loadName) const; virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const = 0; int handleLoadVersion(const unsigned char *buffer, int &major, int &minor, int &patch) const; void handleFailOnLoad(const char *fullName, const char *failContext) const; // // Prepare for a new persistent cache. // int handleResetPersistentCache(); // // Reset the stores in the case of a // failure loading the cache. // int handleResetStores(); // // Reset the transport buffer and the // other counters. // void handleResetFlush(); // // Utility functions used to map file // descriptors to channel ids. // protected: int allocateChannelMap(int fd); int checkChannelMap(int channelId); int assignChannelMap(int channelId, int fd); void cleanupChannelMap(int channelId); virtual int checkLocalChannelMap(int channelId) = 0; int addControlCodes(T_proxy_code code, int data); int addTokenCodes(T_proxy_token &token); int getFd(int channelId) const { if (channelId >= 0 && channelId < CONNECTIONS_LIMIT) { return fdMap_[channelId]; } return -1; } int getChannel(int fd) const { if (fd >= 0 && fd < CONNECTIONS_LIMIT) { return channelMap_[fd]; } return -1; } protected: void setSplitTimeout(int channelId); void setMotionTimeout(int channelId); void increaseChannels(int channelId); void decreaseChannels(int channelId); int allocateTransport(int channelFd, int channelId); int deallocateTransport(int channelId); // // The proxy has its own transport. // ProxyTransport *transport_; // // The static compressor is shared among // channels and all the message stores. // StaticCompressor *compressor_; // // Map NX specific opcodes. // OpcodeStore *opcodeStore_; // // Stores are shared between channels. // ClientStore *clientStore_; ServerStore *serverStore_; // // Client and server caches are shared // between channels. // ClientCache *clientCache_; ServerCache *serverCache_; // // The proxy's file descriptor. // int fd_; // // Channels currently selected for I/O // operations. // int inputChannel_; int outputChannel_; // // List of active channels. // List activeChannels_; // // Used to read data sent from peer proxy. // ProxyReadBuffer readBuffer_; // // Used to send data to peer proxy. // EncodeBuffer encodeBuffer_; // // Control messages' array. // int controlLength_; unsigned char controlCodes_[CONTROL_CODES_LENGTH]; // // Table of channel classes taking // care of open X connections. // Channel *channels_[CONNECTIONS_LIMIT]; // // Table of open sockets. // Transport *transports_[CONNECTIONS_LIMIT]; // // Timeout related data. // T_proxy_timeouts timeouts_; // // Proxy can be decoding messages, // handling a link reconfiguration, // or decoding statistics. // int operation_; // // True if we are currently draining // the proxy link. // int draining_; // // Force flush because of prioritized // control messages to send. // int priority_; // // Set if we have initiated the close // down procedure. // int finish_; // // Remote peer requested the shutdown. // int shutdown_; // // We are in the middle of a network // congestion in the path to remote // proxy. // int congestion_; // // Channels at the remote end that // are not consuming their data. // int congestions_[CONNECTIONS_LIMIT]; // // Is the timer expired? // int timer_; // // Did the proxy request an alert? // int alert_; // // The channel id of the agent. // int agent_; // // Token related data. // T_proxy_token tokens_[TOKEN_TYPES]; // // Pointer to stream descriptor where // proxy is producing statistics. // ostream *currentStatistics_; private: // // Map channel ids to file descriptors. // int channelMap_[CONNECTIONS_LIMIT]; int fdMap_[CONNECTIONS_LIMIT]; }; #endif /* Proxy_H */ nxcomp/Pgn.h0000644000076400007640000000316011323113027013220 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Pgn_H #define Pgn_H // // This file obviously supports PNG // decompression. It was renamed to // avoid name clashes on Windows. // #include "Misc.h" #include "Unpack.h" int UnpackPng(T_geometry *geometry, unsigned char method, unsigned char *srcData, int srcSize, int dstBpp, int dstWidth, int dstHeight, unsigned char *dstData, int dstSize); #endif /* Pgn_H */ nxcomp/Transport.h0000644000076400007640000002362711323113031014475 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Transport_H #define Transport_H #include #include #include #include #include #include "Misc.h" #include "Control.h" #include "Types.h" #include "Timestamp.h" #include "Socket.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Define this to lock and unlock the // memory-to-memory transport buffers // before they are accessed. The code // is outdated and doesn't work with // the current pthread library. // #undef THREADS // // Define this to know when a socket // is created or destroyed. // #undef REFERENCES // // Size of buffer if not set by user. // #define TRANSPORT_BUFFER_DEFAULT_SIZE 16384 // // Type of transport. // typedef enum { transport_base, transport_proxy, transport_agent, transport_last_tag } T_transport_type; // // This class handles the buffered I/O on // the network sockets. // // // TODO: This class is useful but adds a lot of // overhead. There are many improvements we can // make here: // // - There should be a generic Buffer class, ac- // comodating a list of memory buffers. This // would enable the use of the readv() and // writev() functions to perform the I/O on // the socket. // // - The buffering should be moved to the Write- // Buffer and ReadBuffer classes. By performing // the buffering here and there, we are dupli- // cating a lot of code and are adding a lot // of useless memory copies. // // - Stream compression should be removed. The // proxy should compress the frames based on // the type and should include the length of // the decompressed data in the header of the // packet. Besides avoiding the compression // of packets that cannot be reduced in size, // we would also save the additional memory // allocations due to the fact that we don't // know the size of the decode buffer at the // time we read the packet from the network. // // - The other utilities implemented here, like // the functions forcing a write on the socket // or waiting for more data to become available // should be moved to the Proxy or the Channel // classes. // class Transport { public: // // Member functions. // Transport(int fd); virtual ~Transport(); int fd() const { return fd_; } T_transport_type getType() { return type_; } // // Virtual members redefined by proxy // and 'memory-to-memory' I/O layers. // virtual int read(unsigned char *data, unsigned int size); virtual int write(T_write type, const unsigned char *data, const unsigned int size); virtual int flush(); virtual int drain(int limit, int timeout); virtual void finish() { fullReset(); finish_ = 1; } virtual int length() const { return w_buffer_.length_; } virtual int pending() const { return 0; } virtual int readable() const { return GetBytesReadable(fd_); } virtual int writable() const { return GetBytesWritable(fd_); } virtual int queued() const { return GetBytesQueued(fd_); } virtual int flushable() const { return 0; } virtual int wait(int timeout) const; void setSize(unsigned int initialSize, unsigned int thresholdSize, unsigned int maximumSize); // // Return a pointer to the data // in the read buffer. // virtual unsigned int getPending(unsigned char *&data) { data = NULL; return 0; } virtual void pendingReset() { } virtual void partialReset() { partialReset(w_buffer_); } virtual void fullReset(); int blocked() const { return blocked_; } protected: // // Make room in the buffer to accomodate // at least size bytes. // int resize(T_buffer &buffer, const int &size); void partialReset(T_buffer &buffer) { if (buffer.length_ == 0 && (buffer.data_.size() > initialSize_ || buffer.data_.capacity() > initialSize_)) { fullReset(buffer); } } void fullReset(T_buffer &buffer); // // Data members. // int fd_; int blocked_; int finish_; T_buffer w_buffer_; unsigned int initialSize_; unsigned int thresholdSize_; unsigned int maximumSize_; T_transport_type type_; private: #ifdef REFERENCES static int references_; #endif }; // // This class handles buffered I/O and // compression of the proxy stream. // class ProxyTransport : public Transport { public: ProxyTransport(int fd); virtual ~ProxyTransport(); virtual int read(unsigned char *data, unsigned int size); virtual int write(T_write type, const unsigned char *data, const unsigned int size); virtual int flush(); // // Same as in the base class. // // virtual int drain(int limit, int timeout); // // virtual void finish(); // // // Same as in the base class. // // virtual int length() const // virtual int pending() const { return r_buffer_.length_; } // // Same as in the base class. // // virtual int readable() const; // // virtual int writable() const; // // virtual int queued() const; // virtual int flushable() const { return flush_; } // // Same as in the base class, but // should not be called. // // int drained() const; // // Same as in the base class. // // virtual int wait(int timeout) const; // // Same as in the base class. // // void setSize(unsigned int initialSize, // unsigned int thresholdSize, // unsigned int maximumSize); // virtual unsigned int getPending(unsigned char *&data); virtual void pendingReset() { owner_ = 1; } virtual void partialReset() { if (owner_ == 1) { Transport::partialReset(r_buffer_); } Transport::partialReset(w_buffer_); } virtual void fullReset(); // // Same as in the base class. // // int blocked() const; // protected: int flush_; int owner_; T_buffer r_buffer_; z_stream r_stream_; z_stream w_stream_; private: #ifdef REFERENCES static int references_; #endif }; // // Handle memory-to-memory data transfers between // an agent and the proxy. // class AgentTransport : public Transport { public: AgentTransport(int fd); virtual ~AgentTransport(); virtual int read(unsigned char *data, unsigned int size); virtual int write(T_write type, const unsigned char *data, const unsigned int size); // // These two should never be called. // virtual int flush(); virtual int drain(int limit, int timeout); // // Same as in the base class. // // virtual void finish(); // // // Same as in the base class. // // virtual int length() const // virtual int pending() const { return r_buffer_.length_; } // // These are intended to operate only // on the internal buffers. // virtual int readable() const { return r_buffer_.length_; } virtual int writable() const { return control -> TransportMaximumBufferSize; } virtual int queued() const { return 0; } // // Same as in the base class. // // virtual int flushable() const; // // Same as in the base class, but // should not be called. // // int drained() const; // // // Return immediately or will // block until the timeout. // virtual int wait(int timeout) const { return 0; } // // Same as in the base class. // // void setSize(unsigned int initialSize, // unsigned int thresholdSize, // unsigned int maximumSize); // virtual unsigned int getPending(unsigned char *&data); virtual void pendingReset() { owner_ = 1; } virtual void partialReset() { if (owner_ == 1) { Transport::partialReset(r_buffer_); } Transport::partialReset(w_buffer_); } virtual void fullReset(); // // Same as in the base class. // // int blocked() const; // // // The following are specific of the // memory-to-memory transport. // int enqueue(const char *data, const int size); int dequeue(char *data, int size); int queuable() { // // Always allow the agent to enqueue // more data. // return control -> TransportMaximumBufferSize; } int dequeuable(); protected: // // Lock the buffer to handle reads and // writes safely. // #ifdef THREADS int lockRead(); int lockWrite(); int unlockRead(); int unlockWrite(); #endif // // Data members. // int owner_; T_buffer r_buffer_; // // Mutexes for safe read and write. // #ifdef THREADS pthread_mutex_t m_read_; pthread_mutex_t m_write_; #endif private: #ifdef REFERENCES static int references_; #endif }; #endif /* Transport_H */ nxcomp/README-IPAQ0000644000076400007640000000071111323113027013732 0ustar svetonisvetoniREADME-IPAQ ----------- 1. Install a cross-compiler for ARM. You can find detailed informations at: http://www.ailis.de/~k/knowledge/crosscompiling/toolchain.php There are also binaries needed to install the cross-compiler. 2. Configure and compile libXcomp using: $ ./configure --with-ipaq $ make After compilation type: $ arm-linux-strip libXcomp.* 3. Remember that you also need nxproxy to actually run your NX X session. nxcomp/Bitmap.cpp0000644000076400007640000000631511323113031014243 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Misc.h" #include "Bitmap.h" #define PANIC #define WARNING #undef TEST #undef DEBUG int UnpackBitmap(T_geometry *geometry, unsigned char method, unsigned char *src_data, int src_size, int dst_bpp, int dst_width, int dst_height, unsigned char *dst_data, int dst_size) { if (dst_bpp != 32) { #ifdef TEST *logofs << "UnpackBitmap: Nothing to do with " << "image of " << dst_bpp << " bits per plane " << "and size " << src_size << ".\n" << logofs_flush; #endif if (src_size != dst_size) { #ifdef PANIC *logofs << "UnpackBitmap: PANIC! Size mismatch with " << src_size << " bytes in the source and " << dst_size << " in the destination.\n" << logofs_flush; #endif return -1; } memcpy(dst_data, src_data, src_size); return 1; } else if (src_size != dst_width * dst_height * 3 || dst_size != dst_width * dst_height * 4) { #ifdef PANIC *logofs << "UnpackBitmap: PANIC! Size mismatch with " << src_size << " bytes in the source and " << dst_size << " in the destination.\n" << logofs_flush; #endif return -1; } /* * Insert the 4th byte in the bitmap. */ unsigned char *next_src = src_data; unsigned char *next_dst = dst_data; if (geometry -> image_byte_order == LSBFirst) { while (next_src < src_data + src_size) { *next_dst++ = *next_src++; *next_dst++ = *next_src++; *next_dst++ = *next_src++; next_dst++; } } else { while (next_src < src_data + src_size) { next_dst++; *next_dst++ = *next_src++; *next_dst++ = *next_src++; *next_dst++ = *next_src++; } } #ifdef TEST *logofs << "UnpackBitmap: Unpacked " << src_size << " bytes to a buffer of " << dst_size << " with " << dst_bpp << " bits per plane.\n" << logofs_flush; #endif return 1; } nxcomp/RenderCompositeGlyphs.cpp0000644000076400007640000005223211323113030017316 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderCompositeGlyphs.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding value " << ((size - MESSAGE_OFFSET) >> 2) << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); #ifdef DEBUG *logofs << name() << ": Decoded value " << size << ".\n" << logofs_flush; #endif size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), clientCache -> renderDstPictureCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, clientCache -> renderFormatCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 29, clientCache -> renderGlyphSetCache); unsigned int src_x = GetUINT(buffer + 24, bigEndian); unsigned int src_y = GetUINT(buffer + 26, bigEndian); if (control -> isProtoStep8() == 1) { encodeBuffer.encodeDiffCachedValue(src_x, clientCache -> renderGlyphX, 16, clientCache -> renderGlyphXCache, 11); encodeBuffer.encodeDiffCachedValue(src_y, clientCache -> renderGlyphY, 16, clientCache -> renderGlyphYCache, 11); } else { encodeBuffer.encodeDiffCachedValue(src_x, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); encodeBuffer.encodeDiffCachedValue(src_y, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); } #ifdef TEST *logofs << name() << ": Encoded source X " << GetUINT(buffer + 24, bigEndian) << " source Y " << GetUINT(buffer + 26, bigEndian) << ".\n" << logofs_flush; #endif // // Bytes from 28 to 36 contain in the order: // // 1 byte for the length of the first string. // 3 bytes of padding. // 2 bytes for the X offset. // 2 bytes for the Y offset. // // Encode these bytes differentially to match // all the strings that have equal glyphs. // // Only manage the first string of glyphs. The // others strings should match, if they contain // the same glyphs, since the offset are rela- // tive to the first offset coordinates. // if (control -> isProtoStep8() == 1 && size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) { unsigned int numGlyphs = *(buffer + 28); encodeBuffer.encodeCachedValue(numGlyphs, 8, clientCache -> renderNumGlyphsCache); unsigned int offset_x = GetUINT(buffer + 32, bigEndian); unsigned int offset_y = GetUINT(buffer + 34, bigEndian); if (offset_x == src_x && offset_y == src_y) { encodeBuffer.encodeBoolValue(0); #ifdef TEST *logofs << name() << ": Matched offset X " << GetUINT(buffer + 32, bigEndian) << " offset Y " << GetUINT(buffer + 34, bigEndian) << ".\n" << logofs_flush; #endif } else { encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeDiffCachedValue(offset_x, clientCache -> renderGlyphX, 16, clientCache -> renderGlyphXCache, 11); encodeBuffer.encodeDiffCachedValue(offset_y, clientCache -> renderGlyphY, 16, clientCache -> renderGlyphYCache, 11); #ifdef TEST *logofs << name() << ": Missed offset X " << GetUINT(buffer + 32, bigEndian) << " offset Y " << GetUINT(buffer + 34, bigEndian) << ".\n" << logofs_flush; #endif } } #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> renderDstPictureCache); PutULONG(value, buffer + 12, bigEndian); decodeBuffer.decodeCachedValue(value, 32, clientCache -> renderFormatCache); PutULONG(value, buffer + 16, bigEndian); decodeBuffer.decodeCachedValue(value, 29, clientCache -> renderGlyphSetCache); PutULONG(value, buffer + 20, bigEndian); unsigned int src_x; unsigned int src_y; if (control -> isProtoStep8() == 1) { decodeBuffer.decodeDiffCachedValue(src_x, clientCache -> renderGlyphX, 16, clientCache -> renderGlyphXCache, 11); decodeBuffer.decodeDiffCachedValue(src_y, clientCache -> renderGlyphY, 16, clientCache -> renderGlyphYCache, 11); } else { decodeBuffer.decodeDiffCachedValue(src_x, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); decodeBuffer.decodeDiffCachedValue(src_y, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); } PutUINT(src_x, buffer + 24, bigEndian); PutUINT(src_y, buffer + 26, bigEndian); if (control -> isProtoStep8() == 1 && size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) { decodeBuffer.decodeCachedValue(value, 8, clientCache -> renderNumGlyphsCache); *(buffer + 28) = value; decodeBuffer.decodeBoolValue(value); if (value == 0) { PutUINT(src_x, buffer + 32, bigEndian); PutUINT(src_y, buffer + 34, bigEndian); } else { decodeBuffer.decodeDiffCachedValue(src_x, clientCache -> renderGlyphX, 16, clientCache -> renderGlyphXCache, 11); PutUINT(src_x, buffer + 32, bigEndian); decodeBuffer.decodeDiffCachedValue(src_y, clientCache -> renderGlyphY, 16, clientCache -> renderGlyphYCache, 11); PutUINT(src_y, buffer + 34, bigEndian); } } #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { if (control -> isProtoStep8() == 1 && size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) { encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET_IF_PROTO_STEP_8, size, bigEndian, channelCache); } else if (size > MESSAGE_OFFSET) { encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); } #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of text data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { if (control -> isProtoStep8() == 1 && size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) { decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET_IF_PROTO_STEP_8, size, bigEndian, channelCache); } else if (size > MESSAGE_OFFSET) { decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); } #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.composite_glyphs.type = *(buffer + 1); renderExtension -> data.composite_glyphs.op = *(buffer + 4); renderExtension -> data.composite_glyphs.src_id = GetULONG(buffer + 8, bigEndian); renderExtension -> data.composite_glyphs.dst_id = GetULONG(buffer + 12, bigEndian); renderExtension -> data.composite_glyphs.format = GetULONG(buffer + 16, bigEndian); renderExtension -> data.composite_glyphs.set_id = GetULONG(buffer + 20, bigEndian); renderExtension -> data.composite_glyphs.src_x = GetUINT(buffer + 24, bigEndian); renderExtension -> data.composite_glyphs.src_y = GetUINT(buffer + 26, bigEndian); if (control -> isProtoStep8() == 1 && size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) { renderExtension -> data.composite_glyphs.num_elm = *(buffer + 28); renderExtension -> data.composite_glyphs.offset_x = GetUINT(buffer + 32, bigEndian); renderExtension -> data.composite_glyphs.offset_y = GetUINT(buffer + 34, bigEndian); } #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.composite_glyphs.type << " size is " << renderExtension -> size_ << " identity size " << renderExtension -> i_size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.composite_glyphs.type; *(buffer + 4) = renderExtension -> data.composite_glyphs.op; PutULONG(renderExtension -> data.composite_glyphs.src_id, buffer + 8, bigEndian); PutULONG(renderExtension -> data.composite_glyphs.dst_id, buffer + 12, bigEndian); PutULONG(renderExtension -> data.composite_glyphs.format, buffer + 16, bigEndian); PutULONG(renderExtension -> data.composite_glyphs.set_id, buffer + 20, bigEndian); PutUINT(renderExtension -> data.composite_glyphs.src_x, buffer + 24, bigEndian); PutUINT(renderExtension -> data.composite_glyphs.src_y, buffer + 26, bigEndian); if (control -> isProtoStep8() == 1 && size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) { *(buffer + 28) = renderExtension -> data.composite_glyphs.num_elm; PutUINT(renderExtension -> data.composite_glyphs.offset_x, buffer + 32, bigEndian); PutUINT(renderExtension -> data.composite_glyphs.offset_y, buffer + 34, bigEndian); } #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.composite_glyphs.type << " size is " << renderExtension -> size_ << " identity size " << renderExtension -> i_size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { // // Include minor opcode, size and // the composite operator in the // identity. // md5_append(md5_state, buffer + 1, 4); // // Include the format. // md5_append(md5_state, buffer + 16, 4); // // Also include the length of the // first string. // if (control -> isProtoStep8() == 1 && size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) { md5_append(md5_state, buffer + 28, 1); } } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.composite_glyphs.src_id = renderExtension -> data.composite_glyphs.src_id; encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs.dst_id, clientCache -> renderDstPictureCache); cachedRenderExtension -> data.composite_glyphs.dst_id = renderExtension -> data.composite_glyphs.dst_id; encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs.set_id, 29, clientCache -> renderGlyphSetCache); cachedRenderExtension -> data.composite_glyphs.set_id = renderExtension -> data.composite_glyphs.set_id; // // Src X and Y. // // The source X and Y coordinates are // encoded as differerences in respect // to the cached message. // unsigned int value; unsigned int previous; if (control -> isProtoStep8() == 1) { value = renderExtension -> data.composite_glyphs.src_x; previous = cachedRenderExtension -> data.composite_glyphs.src_x; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderGlyphXCache, 11); cachedRenderExtension -> data.composite_glyphs.src_x = value; value = renderExtension -> data.composite_glyphs.src_y; previous = cachedRenderExtension -> data.composite_glyphs.src_y; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderGlyphYCache, 11); cachedRenderExtension -> data.composite_glyphs.src_y = value; } else { value = renderExtension -> data.composite_glyphs.src_x; previous = cachedRenderExtension -> data.composite_glyphs.src_x; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderXCache, 11); cachedRenderExtension -> data.composite_glyphs.src_x = value; value = renderExtension -> data.composite_glyphs.src_y; previous = cachedRenderExtension -> data.composite_glyphs.src_y; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderYCache, 11); cachedRenderExtension -> data.composite_glyphs.src_y = value; } #ifdef TEST *logofs << name() << ": Encoded source X " << renderExtension -> data.composite_glyphs.src_x << " source Y " << renderExtension -> data.composite_glyphs.src_y << ".\n" << logofs_flush; #endif if (control -> isProtoStep8() == 1 && renderExtension -> size_ >= MESSAGE_OFFSET_IF_PROTO_STEP_8) { // // Offset X and Y. // if (renderExtension -> data.composite_glyphs.offset_x == renderExtension -> data.composite_glyphs.src_x && renderExtension -> data.composite_glyphs.offset_y == renderExtension -> data.composite_glyphs.src_y) { encodeBuffer.encodeBoolValue(0); cachedRenderExtension -> data.composite_glyphs.offset_x = renderExtension -> data.composite_glyphs.offset_x; cachedRenderExtension -> data.composite_glyphs.offset_y = renderExtension -> data.composite_glyphs.offset_y; #ifdef TEST *logofs << name() << ": Matched offset X " << renderExtension -> data.composite_glyphs.offset_x << " offset Y " << renderExtension -> data.composite_glyphs.offset_y << ".\n" << logofs_flush; #endif } else { encodeBuffer.encodeBoolValue(1); value = renderExtension -> data.composite_glyphs.offset_x; previous = cachedRenderExtension -> data.composite_glyphs.offset_x; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderGlyphXCache, 11); cachedRenderExtension -> data.composite_glyphs.offset_x = value; value = renderExtension -> data.composite_glyphs.offset_y; previous = cachedRenderExtension -> data.composite_glyphs.offset_y; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderGlyphYCache, 11); cachedRenderExtension -> data.composite_glyphs.offset_y = value; #ifdef TEST *logofs << name() << ": Missed offset X " << renderExtension -> data.composite_glyphs.offset_x << " offset Y " << renderExtension -> data.composite_glyphs.offset_y << ".\n" << logofs_flush; #endif } } #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.composite_glyphs.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs.src_id, clientCache -> renderSrcPictureCache); decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs.dst_id, clientCache -> renderDstPictureCache); decodeBuffer.decodeCachedValue(renderExtension -> data.composite_glyphs.set_id, 29, clientCache -> renderGlyphSetCache); // // Src X and Y. // unsigned int value; unsigned int previous; if (control -> isProtoStep8() == 1) { previous = renderExtension -> data.composite_glyphs.src_x; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderGlyphXCache, 11); renderExtension -> data.composite_glyphs.src_x = value; previous = renderExtension -> data.composite_glyphs.src_y; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderGlyphYCache, 11); renderExtension -> data.composite_glyphs.src_y = value; } else { previous = renderExtension -> data.composite_glyphs.src_x; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderXCache, 11); renderExtension -> data.composite_glyphs.src_x = value; previous = renderExtension -> data.composite_glyphs.src_y; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderYCache, 11); renderExtension -> data.composite_glyphs.src_y = value; } if (control -> isProtoStep8() == 1 && renderExtension -> size_ >= MESSAGE_OFFSET_IF_PROTO_STEP_8) { // // Offset X and Y. // decodeBuffer.decodeBoolValue(value); if (value == 0) { renderExtension -> data.composite_glyphs.offset_x = renderExtension -> data.composite_glyphs.src_x; renderExtension -> data.composite_glyphs.offset_y = renderExtension -> data.composite_glyphs.src_y; } else { previous = renderExtension -> data.composite_glyphs.offset_x; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderGlyphXCache, 11); renderExtension -> data.composite_glyphs.offset_x = value; previous = renderExtension -> data.composite_glyphs.offset_y; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderGlyphYCache, 11); renderExtension -> data.composite_glyphs.offset_y = value; } } #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.composite_glyphs.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/ServerStore.cpp0000644000076400007640000001260411323113027015315 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ServerStore.h" // // Cached reply classes. // #include "GetImageReply.h" #include "ListFontsReply.h" #include "QueryFontReply.h" #include "GetPropertyReply.h" #include "GenericReply.h" // // Set the verbosity level. // #define WARNING #define PANIC #undef TEST ServerStore::ServerStore(StaticCompressor *compressor) { if (logofs == NULL) { logofs = &cout; } for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { replies_[i] = NULL; events_[i] = NULL; } replies_[X_ListFonts] = new ListFontsReplyStore(compressor); replies_[X_QueryFont] = new QueryFontReplyStore(compressor); replies_[X_GetImage] = new GetImageReplyStore(compressor); replies_[X_GetProperty] = new GetPropertyReplyStore(compressor); replies_[X_NXInternalGenericReply] = new GenericReplyStore(compressor); } ServerStore::~ServerStore() { if (logofs == NULL) { logofs = &cout; } for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { delete replies_[i]; delete events_[i]; } } int ServerStore::saveReplyStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient, T_checksum_action checksumAction, T_data_action dataAction) const { for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { if (replies_[i] != NULL && replies_[i] -> saveStore(cachefs, md5StateStream, md5StateClient, checksumAction, dataAction, storeBigEndian()) < 0) { #ifdef PANIC *logofs << "ServerStore: PANIC! Error saving reply store " << "for OPCODE#" << (unsigned int) i << ".\n" << logofs_flush; #endif cerr << "Error" << ": Error saving reply store " << "for opcode '" << (unsigned int) i << "'.\n"; return -1; } } return 1; } int ServerStore::saveEventStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient, T_checksum_action checksumAction, T_data_action dataAction) const { for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { if (events_[i] != NULL && events_[i] -> saveStore(cachefs, md5StateStream, md5StateClient, checksumAction, dataAction, storeBigEndian()) < 0) { #ifdef PANIC *logofs << "ServerStore: PANIC! Error saving event store " << "for OPCODE#" << (unsigned int) i << ".\n" << logofs_flush; #endif cerr << "Error" << ": Error saving event store " << "for opcode '" << (unsigned int) i << "'.\n"; return -1; } } return 1; } int ServerStore::loadReplyStores(istream *cachefs, md5_state_t *md5StateStream, T_checksum_action checksumAction, T_data_action dataAction) const { for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { if (replies_[i] != NULL && replies_[i] -> loadStore(cachefs, md5StateStream, checksumAction, dataAction, storeBigEndian()) < 0) { #ifdef PANIC *logofs << "ServerStore: PANIC! Error loading reply store " << "for OPCODE#" << (unsigned int) i << ".\n" << logofs_flush; #endif return -1; } } return 1; } int ServerStore::loadEventStores(istream *cachefs, md5_state_t *md5StateStream, T_checksum_action checksumAction, T_data_action dataAction) const { for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { if (events_[i] != NULL && events_[i] -> loadStore(cachefs, md5StateStream, checksumAction, dataAction, storeBigEndian()) < 0) { #ifdef PANIC *logofs << "ServerStore: PANIC! Error loading event store " << "for OPCODE#" << (unsigned int) i << ".\n" << logofs_flush; #endif return -1; } } return 1; } nxcomp/ClientStore.h0000644000076400007640000000646711323113027014744 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ClientStore_H #define ClientStore_H #include "Message.h" #include "Split.h" #include "ChannelStore.h" class StaticCompressor; class ClientStore : public ChannelStore { public: ClientStore(StaticCompressor *compressor); virtual ~ClientStore(); // // Get the store based on the index. // MessageStore *getRequestStore(unsigned char opcode) const { return requests_[opcode]; } SplitStore *getSplitStore(int resource) const { return splits_[resource]; } int getSplitTotalSize() const { return SplitStore::getTotalSize(); } int getSplitTotalStorageSize() const { return SplitStore::getTotalStorageSize(); } CommitStore *getCommitStore() const { return commits_; } int getCommitSize() const { return commits_ -> getSize(); } void dumpSplitStore(int resource) const { splits_[resource] -> dump(); } void dumpCommitStore() const { commits_ -> dump(); } void dumpSplitStores() const; SplitStore *createSplitStore(int resource) { splits_[resource] = new SplitStore(compressor_, commits_, resource); return splits_[resource]; } void destroySplitStore(int resource) { delete splits_[resource]; splits_[resource] = NULL; } // // Actually save the message store // to disk according to proxy mode. // int saveRequestStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient, T_checksum_action checksumAction, T_data_action dataAction) const; int loadRequestStores(istream *cachefs, md5_state_t *md5StateStream, T_checksum_action checksumAction, T_data_action dataAction) const; private: // // A client store contains requests. // MessageStore *requests_[CHANNEL_STORE_OPCODE_LIMIT]; // // Client messages being split. // SplitStore *splits_[CHANNEL_STORE_RESOURCE_LIMIT]; // // Messages having been recomposed. // CommitStore *commits_; // // Passed forward to the other stores. // StaticCompressor *compressor_; }; #endif /* ClientStore_H */ nxcomp/Socket.h0000644000076400007640000000455611323113030013730 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Socket_H #define Socket_H #include #include #include #ifdef __sun #include #include #endif // // Set socket options. // int SetReuseAddress(int fd); int SetNonBlocking(int fd, int value); int SetLingerTimeout(int fd, int timeout); int SetSendBuffer(int fd, int size); int SetReceiveBuffer(int fd, int size); int SetNoDelay(int fd, int value); int SetKeepAlive(int fd); int SetLowDelay(int fd); int SetCloseOnExec(int fd); // // Get kernel support level. // int GetKernelStep(); // // Get socket info. // int GetBytesReadable(int fd); int GetBytesWritable(int fd); int GetBytesQueued(int fd); // // Inline version, providing direct access // to the interface. // #include "Misc.h" inline int GetBytesReadable(int fd, int *readable) { long t; int result = ioctl(fd, FIONREAD, &t); #ifdef DEBUG *logofs << "Socket: Bytes readable from FD#" << fd << " are " << t << " with result " << result << ".\n" << logofs_flush; #endif *readable = (int) t; return result; } // // Query Internet address. // int GetHostAddress(const char *name); #endif /* Socket_H */ nxcomp/GenericReadBuffer.h0000644000076400007640000000400011323113026015767 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef GenericReadBuffer_H #define GenericReadBuffer_H #include "ReadBuffer.h" #include "Control.h" class GenericChannel; class GenericReadBuffer : public ReadBuffer { public: GenericReadBuffer(Transport *transport, GenericChannel *channel) : ReadBuffer(transport), channel_(channel) { } virtual ~GenericReadBuffer() { } protected: virtual unsigned int suggestedLength(unsigned int pendingLength); virtual int locateMessage(const unsigned char *start, const unsigned char *end, unsigned int &controlLength, unsigned int &dataLength, unsigned int &trailerLength); GenericChannel *channel_; }; #endif /* GenericReadBuffer_H */ nxcomp/ReadBuffer.cpp0000644000076400007640000003447111323113027015045 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ReadBuffer.h" #include "Transport.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG ReadBuffer::ReadBuffer(Transport *transport) : transport_(transport) { // // The read buffer will grow until // reaching the maximum buffer size // and then will remain stable at // that size. // initialReadSize_ = READ_BUFFER_DEFAULT_SIZE; maximumBufferSize_ = READ_BUFFER_DEFAULT_SIZE; size_ = 0; buffer_ = NULL; owner_ = 1; length_ = 0; start_ = 0; remaining_ = 0; } ReadBuffer::~ReadBuffer() { if (owner_ == 1) { delete [] buffer_; } } void ReadBuffer::readMessage(const unsigned char *message, unsigned int length) { // // To be here we must be the real owner // of the buffer and there must not be // pending bytes in the transport. // #ifdef TEST if (owner_ == 0) { *logofs << "ReadBuffer: PANIC! Class for FD#" << transport_ -> fd() << " doesn't " << "appear to be the owner of the buffer " << "while borrowing from the caller.\n" << logofs_flush; HandleCleanup(); } #endif // // Be sure that any outstanding data from // the transport is appended to our own // byffer. // if (transport_ -> pending() != 0) { #ifdef WARNING *logofs << "ReadBuffer: WARNING! Class for FD#" << transport_ -> fd() << " has pending " << "data in the transport while " << "borrowing from the caller.\n" << logofs_flush; #endif readMessage(); if (owner_ == 0) { convertBuffer(); } } // // Can't borrow the buffer if there is data // from a partial message. In this case add // the new data to the end of our buffer. // if (length_ == 0) { #ifdef TEST *logofs << "ReadBuffer: Borrowing " << length << " bytes from the caller for FD#" << transport_ -> fd() << " with " << length_ << " bytes in the buffer.\n" << logofs_flush; #endif delete [] buffer_; buffer_ = (unsigned char *) message; size_ = length; length_ = length; owner_ = 0; start_ = 0; } else { #ifdef TEST *logofs << "ReadBuffer: Appending " << length << " bytes from the caller for FD#" << transport_ -> fd() << " with " << length_ << " bytes in the buffer.\n" << logofs_flush; #endif appendBuffer(message, length); } } int ReadBuffer::readMessage() { int pendingLength = transport_ -> pending(); if (pendingLength > 0) { // // Can't move the data in the borrowed buffer, // so use the tansport buffer only if we don't // have any partial message. This can happen // with the proxy where we need to deflate the // stream. // if (length_ == 0) { unsigned char *newBuffer; length_ = transport_ -> getPending(newBuffer); if (newBuffer == NULL) { #ifdef PANIC *logofs << "ReadBuffer: PANIC! Failed to borrow " << length_ << " bytes of memory for buffer " << "in context [A].\n" << logofs_flush; #endif cerr << "Error" << ": Failed to borrow memory for " << "read buffer in context [A].\n"; HandleCleanup(); } delete [] buffer_; buffer_ = newBuffer; size_ = length_; owner_ = 0; start_ = 0; #ifdef TEST *logofs << "ReadBuffer: Borrowed " << length_ << " pending bytes for FD#" << transport_ -> fd() << ".\n" << logofs_flush; #endif return length_; } #ifdef TEST else { *logofs << "ReadBuffer: WARNING! Cannot borrow " << pendingLength << " bytes for FD#" << transport_ -> fd() << " with " << length_ << " bytes in the buffer.\n" << logofs_flush; } #endif } unsigned int readLength = suggestedLength(pendingLength); #ifdef DEBUG *logofs << "ReadBuffer: Requested " << readLength << " bytes for FD#" << transport_ -> fd() << " with readable " << transport_ -> readable() << " remaining " << remaining_ << " pending " << transport_ -> pending() << ".\n" << logofs_flush; #endif if (readLength < initialReadSize_) { readLength = initialReadSize_; } #ifdef DEBUG *logofs << "ReadBuffer: Buffer size is " << size_ << " length " << length_ << " and start " << start_ << ".\n" << logofs_flush; #endif // // We can't use the transport buffer // to read our own data in it. // #ifdef TEST if (owner_ == 0) { *logofs << "ReadBuffer: PANIC! Class for FD#" << transport_ -> fd() << " doesn't " << "appear to be the owner of the buffer " << "while reading.\n" << logofs_flush; HandleCleanup(); } #endif // // Be sure that we have enough space // to store all the requested data. // if (buffer_ == NULL || length_ + readLength > size_) { unsigned int newSize = length_ + readLength; #ifdef TEST *logofs << "ReadBuffer: Resizing buffer for FD#" << transport_ -> fd() << " in read from " << size_ << " to " << newSize << " bytes.\n" << logofs_flush; #endif unsigned char *newBuffer = allocateBuffer(newSize); memcpy(newBuffer, buffer_ + start_, length_); delete [] buffer_; buffer_ = newBuffer; size_ = newSize; transport_ -> pendingReset(); owner_ = 1; } else if (start_ != 0 && length_ != 0) { // // If any bytes are left due to a partial // message, shift them to the beginning // of the buffer. // #ifdef TEST *logofs << "ReadBuffer: Moving " << length_ << " bytes of data " << "at beginning of " << "the buffer for FD#" << transport_ -> fd() << ".\n" << logofs_flush; #endif memmove(buffer_, buffer_ + start_, length_); } start_ = 0; #ifdef DEBUG *logofs << "ReadBuffer: Buffer size is now " << size_ << " length is " << length_ << " and start is " << start_ << ".\n" << logofs_flush; #endif unsigned char *readData = buffer_ + length_; #ifdef DEBUG *logofs << "ReadBuffer: Going to read " << readLength << " bytes from FD#" << transport_ -> fd() << ".\n" << logofs_flush; #endif int bytesRead = transport_ -> read(readData, readLength); if (bytesRead > 0) { #ifdef TEST *logofs << "ReadBuffer: Read " << bytesRead << " bytes from FD#" << transport_ -> fd() << ".\n" << logofs_flush; #endif length_ += bytesRead; } else if (bytesRead < 0) { // // Check if there is more data pending than the // size of the provided buffer. After reading // the requested amount, in fact, the transport // may have decompressed the data and produced // more input. This trick allows us to always // borrow the buffer from the transport, even // when the partial read would have prevented // that. // if (transport_ -> pending() > 0) { #ifdef TEST *logofs << "ReadBuffer: WARNING! Trying to read some " << "more with " << transport_ -> pending() << " bytes pending for FD#" << transport_ -> fd() << ".\n" << logofs_flush; #endif return readMessage(); } #ifdef TEST *logofs << "ReadBuffer: Error detected reading " << "from FD#" << transport_ -> fd() << ".\n" << logofs_flush; #endif return -1; } #ifdef TEST else { *logofs << "ReadBuffer: No data read from FD#" << transport_ -> fd() << " with remaining " << remaining_ << ".\n" << logofs_flush; } #endif return bytesRead; } const unsigned char *ReadBuffer::getMessage(unsigned int &controlLength, unsigned int &dataLength) { #ifdef TEST if (transport_ -> pending() > 0) { *logofs << "ReadBuffer: PANIC! The transport " << "appears to have data pending.\n" << logofs_flush; HandleCleanup(); } #endif if (length_ == 0) { #ifdef DEBUG *logofs << "ReadBuffer: No message can be located " << "for FD#" << transport_ -> fd() << ".\n" << logofs_flush; #endif if (owner_ == 0) { buffer_ = NULL; size_ = 0; transport_ -> pendingReset(); owner_ = 1; start_ = 0; } return NULL; } unsigned int trailerLength; #ifdef DEBUG *logofs << "ReadBuffer: Going to locate message with " << "start at " << start_ << " and length " << length_ << " for FD#" << transport_ -> fd() << ".\n" << logofs_flush; #endif int located = locateMessage(buffer_ + start_, buffer_ + start_ + length_, controlLength, dataLength, trailerLength); if (located == 0) { // // No more complete messages are in // the buffer. // #ifdef DEBUG *logofs << "ReadBuffer: No message was located " << "for FD#" << transport_ -> fd() << ".\n" << logofs_flush; #endif if (owner_ == 0) { // // Must move the remaining bytes in // our own buffer. // convertBuffer(); } return NULL; } else { const unsigned char *result = buffer_ + start_; if (dataLength > 0) { // // Message contains data, so go to the // first byte of payload. // result += trailerLength; start_ += (dataLength + trailerLength); length_ -= (dataLength + trailerLength); } else { // // It is a control message. // start_ += (controlLength + trailerLength); length_ -= (controlLength + trailerLength); } #ifdef DEBUG *logofs << "ReadBuffer: Located message for FD#" << transport_ -> fd() << " with control length " << controlLength << " and data length " << dataLength << ".\n" << logofs_flush; #endif remaining_ = 0; return result; } } int ReadBuffer::setSize(int initialReadSize, int maximumBufferSize) { initialReadSize_ = initialReadSize; maximumBufferSize_ = maximumBufferSize; #ifdef TEST *logofs << "ReadBuffer: WARNING! Set buffer parameters to " << initialReadSize_ << "/" << maximumBufferSize_ << " for object at "<< this << ".\n" << logofs_flush; #endif return 1; } void ReadBuffer::fullReset() { #ifdef TEST if (owner_ == 0) { *logofs << "ReadBuffer: PANIC! Class for FD#" << transport_ -> fd() << " doesn't " << "appear to be the owner of the buffer " << "in reset.\n" << logofs_flush; HandleCleanup(); } #endif if (length_ == 0 && size_ > maximumBufferSize_) { #ifdef TEST *logofs << "ReadBuffer: Resizing buffer for FD#" << transport_ -> fd() << " in reset from " << size_ << " to " << maximumBufferSize_ << " bytes.\n" << logofs_flush; #endif delete [] buffer_; int newSize = maximumBufferSize_; unsigned char *newBuffer = allocateBuffer(newSize); buffer_ = newBuffer; size_ = newSize; transport_ -> pendingReset(); owner_ = 1; start_ = 0; } } unsigned char *ReadBuffer::allocateBuffer(unsigned int newSize) { unsigned char *newBuffer = new unsigned char[newSize]; if (newBuffer == NULL) { #ifdef PANIC *logofs << "ReadBuffer: PANIC! Can't allocate " << newSize << " bytes of memory for buffer " << "in context [B].\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory for " << "read buffer in context [B].\n"; HandleCleanup(); } #ifdef VALGRIND memset(newBuffer, '\0', newSize); #endif return newBuffer; } void ReadBuffer::appendBuffer(const unsigned char *message, unsigned int length) { if (start_ + length_ + length > size_) { unsigned int newSize = length_ + length + initialReadSize_; #ifdef TEST *logofs << "ReadBuffer: WARNING! Resizing buffer " << "for FD#" << transport_ -> fd() << " from " << size_ << " to " << newSize << " bytes.\n" << logofs_flush; #endif unsigned char *newBuffer = allocateBuffer(newSize); memcpy(newBuffer, buffer_ + start_, length_); delete [] buffer_; buffer_ = newBuffer; size_ = newSize; start_ = 0; } memcpy(buffer_ + start_ + length_, message, length); length_ += length; transport_ -> pendingReset(); owner_ = 1; } void ReadBuffer::convertBuffer() { unsigned int newSize = length_ + initialReadSize_; #ifdef TEST *logofs << "ReadBuffer: WARNING! Converting " << length_ << " bytes to own buffer " << "for FD#" << transport_ -> fd() << " with new size " << newSize << " bytes.\n" << logofs_flush; #endif unsigned char *newBuffer = allocateBuffer(newSize); memcpy(newBuffer, buffer_ + start_, length_); buffer_ = newBuffer; size_ = newSize; transport_ -> pendingReset(); owner_ = 1; start_ = 0; } nxcomp/ImageText8.h0000644000076400007640000001122211323113026014450 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ImageText8_H #define ImageText8_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define IMAGETEXT8_ENABLE_CACHE 1 #define IMAGETEXT8_ENABLE_DATA 0 #define IMAGETEXT8_ENABLE_SPLIT 0 #define IMAGETEXT8_ENABLE_COMPRESS 0 #define IMAGETEXT8_DATA_LIMIT 256 #define IMAGETEXT8_DATA_OFFSET 16 #define IMAGETEXT8_CACHE_SLOTS 3000 #define IMAGETEXT8_CACHE_THRESHOLD 5 #define IMAGETEXT8_CACHE_LOWER_THRESHOLD 1 // // The message class. // class ImageText8Message : public Message { friend class ImageText8Store; public: ImageText8Message() { } ~ImageText8Message() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char len; unsigned int drawable; unsigned int gcontext; unsigned short x; unsigned short y; }; class ImageText8Store : public MessageStore { // // Constructors and destructors. // public: ImageText8Store() : MessageStore() { enableCache = IMAGETEXT8_ENABLE_CACHE; enableData = IMAGETEXT8_ENABLE_DATA; enableSplit = IMAGETEXT8_ENABLE_SPLIT; enableCompress = IMAGETEXT8_ENABLE_COMPRESS; dataLimit = IMAGETEXT8_DATA_LIMIT; dataOffset = IMAGETEXT8_DATA_OFFSET; cacheSlots = IMAGETEXT8_CACHE_SLOTS; cacheThreshold = IMAGETEXT8_CACHE_THRESHOLD; cacheLowerThreshold = IMAGETEXT8_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~ImageText8Store() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "ImageText8"; } virtual unsigned char opcode() const { return X_ImageText8; } virtual unsigned int storage() const { return sizeof(ImageText8Message); } // // Message handling methods. // public: virtual Message *create() const { return new ImageText8Message(); } virtual Message *create(const Message &message) const { return new ImageText8Message((const ImageText8Message &) message); } virtual void destroy(Message *message) const { delete (ImageText8Message *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* ImageText8_H */ nxcomp/ServerChannel.h0000644000076400007640000003062311323113031015232 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ServerChannel_H #define ServerChannel_H #include "List.h" #include "Channel.h" #include "SequenceQueue.h" #include "ServerReadBuffer.h" #include "Unpack.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // How many sequence numbers of split commit // requests we are going to save in order to // mask errors. // #define MAX_COMMIT_SEQUENCE_QUEUE 16 // // Define this to know when a channel // is created or destroyed. // #undef REFERENCES // // This class implements the X server // side compression of X protocol. // class ServerChannel : public Channel { public: ServerChannel(Transport *transport, StaticCompressor *compressor); virtual ~ServerChannel(); virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, unsigned int length); virtual int handleWrite(const unsigned char *message, unsigned int length); virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store, T_store_action action, int position, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { return 0; } virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, T_store_action action, int position, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); virtual int handleSplit(EncodeBuffer &encodeBuffer) { return 0; } virtual int handleSplit(DecodeBuffer &decodeBuffer); virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split); virtual int handleSplitEvent(DecodeBuffer &decodeBuffer) { return 0; } // // Send the last motion notify event // received from the X server to the // remote proxy. // virtual int handleMotion(EncodeBuffer &encodeBuffer); virtual int handleCompletion(EncodeBuffer &encodeBuffer) { return 0; } virtual int handleConfiguration(); virtual int handleFinish(); virtual int handleAsyncEvents(); virtual int needSplit() const { return 0; } virtual int needMotion() const { return (lastMotion_[0] != '\0'); } virtual T_channel_type getType() const { return channel_x11; } int setBigEndian(int flag); // // Initialize the static members. // static int setReferences(); private: int handleFastReadReply(EncodeBuffer &encodeBuffer, const unsigned char &opcode, const unsigned char *&buffer, const unsigned int &size); int handleFastReadEvent(EncodeBuffer &encodeBuffer, const unsigned char &opcode, const unsigned char *&buffer, const unsigned int &size); int handleFastWriteRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Handle the fake authorization cookie // and the X server's reply. // int handleAuthorization(unsigned char *buffer); int handleAuthorization(const unsigned char *buffer, int size); // // Set the unpack colormap and the alpha // blending data to be used to unpack // images. // int handleGeometry(unsigned char &opcode, unsigned char *&buffer, unsigned int &size); int handleColormap(unsigned char &opcode, unsigned char *&buffer, unsigned int &size); int handleAlpha(unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Manage the decoded buffer to unpack // the image and move the data to the // shared memory segment. // int handleImage(unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Uncompress a packed image in one // or more graphic X requests. // int handleUnpack(unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Move the image to the shared // memory buffer. // int handleShmem(unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Handle suppression of error on // commit of image splits. // void initCommitQueue(); void updateCommitQueue(unsigned short sequence); int checkCommitError(unsigned char error, unsigned short sequence, const unsigned char *buffer); void clearCommitQueue() { if (commitSequenceQueue_[0] != 0) { initCommitQueue(); } } // // Check if the user pressed the // CTRL+ALT+SHIFT+ESC keystroke. // int checkKeyboardEvent(unsigned char event, unsigned short sequence, const unsigned char *buffer); // // Other utilities. // void handleEncodeCharInfo(const unsigned char *nextSrc, EncodeBuffer &encodeBuffer); // // Handle the MIT-SHM initialization // messages exchanged with the remote // proxy. // int handleShmemRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); int handleShmemReply(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned int stage, const unsigned char *buffer, const unsigned int size); // // Try to read more events in the attempt to // get the MIT-SHM image completion event // from the X server. // int handleShmemEvent(); // // Handle the MIT-SHM events as they are read // from the socket. // int checkShmemEvent(unsigned char event, unsigned short sequence, const unsigned char *buffer); int checkShmemError(unsigned char error, unsigned short sequence, const unsigned char *buffer); // // Query the port used to tunnel // the font server connections. // int handleFontRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); int handleFontReply(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); // // Set the cache policy for image // requests. // int handleCacheRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Decode the start and end split // requests. // int handleStartSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); int handleEndSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Remove the split store and the // incomplete messages from the // memory cache. // int handleAbortSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Send the split requests to the // X server once they have been // recomposed. // int handleCommitSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); int handleSplitChecksum(DecodeBuffer &decodeBuffer, T_checksum &checksum); void handleSplitEnable() { if (control -> isProtoStep7() == 0) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitEnable: WARNING! Disabling load " << "and save with an old proxy version.\n" << logofs_flush; #endif splitState_.save = 0; splitState_.load = 0; } } // // Allocate and free the shared memory // support resources. // void handleShmemStateAlloc(); void handleShmemStateRemove(); // // Temporary storage for the image info. // void handleImageStateAlloc(unsigned char opcode) { if (imageState_ == NULL) { imageState_ = new T_image_state(); } imageState_ -> opcode = opcode; } void handleImageStateRemove() { if (imageState_ != NULL) { delete imageState_; imageState_ = NULL; } } // // Store the information needed to unpack // images per each known agent's client. // void handleUnpackStateInit(int resource); void handleUnpackAllocGeometry(int resource); void handleUnpackAllocColormap(int resource); void handleUnpackAllocAlpha(int resource); void handleUnpackStateRemove(int resource); typedef struct { T_geometry *geometry; T_colormap *colormap; T_alpha *alpha; } T_unpack_state; T_unpack_state *unpackState_[256]; // // Own read buffer. It is able to identify // full messages read from X descriptor. // ServerReadBuffer readBuffer_; // // Sequence number of last request coming // from X client or X server. // unsigned int clientSequence_; unsigned int serverSequence_; // // Used to identify replies based on sequence // number of original request. // SequenceQueue sequenceQueue_; // // Last motion notify read from the X server. // unsigned char lastMotion_[32]; // // Sequence numbers of last auto-generated // put image requests. Needed to intercept // and suppress errors generated by such // requests. // unsigned int commitSequenceQueue_[MAX_COMMIT_SEQUENCE_QUEUE]; // // Let agent select which expose // events is going to receive. // unsigned int enableExpose_; unsigned int enableGraphicsExpose_; unsigned int enableNoExpose_; // // Used in initialization and handling // of MIT-SHM shared memory put images. // typedef struct { int stage; int present; int enabled; int segment; int id; void *address; unsigned int size; unsigned char opcode; unsigned char event; unsigned char error; unsigned int sequence; unsigned int offset; T_timestamp last; unsigned int checked; } T_shmem_state; T_shmem_state *shmemState_; // // Used to pass current image data between // the different decompression stages. // typedef struct { unsigned char opcode; unsigned int drawable; unsigned int gcontext; unsigned char method; unsigned char format; unsigned char srcDepth; unsigned char dstDepth; unsigned int srcLength; unsigned int dstLength; unsigned int dstLines; short int srcX; short int srcY; unsigned short srcWidth; unsigned short srcHeight; short int dstX; short int dstY; unsigned short dstWidth; unsigned short dstHeight; unsigned char leftPad; } T_image_state; T_image_state *imageState_; // // The flags is set according to the // split load and save policy set by // the encoding side. // typedef struct { int resource; int current; int load; int save; int commit; } T_split_state; T_split_state splitState_; // // List of agent resources. // List splitResources_; // // Keep track of object creation and // deletion. // private: #ifdef REFERENCES static int references_; #endif }; #endif /* ServerChannel_H */ nxcomp/Control.h0000644000076400007640000003255211323113030014115 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Control_H #define Control_H #include "NXpack.h" #include "Misc.h" #include "Types.h" #include "Timestamp.h" #include "Statistics.h" #define PANIC #define WARNING #undef TEST #undef DEBUG // // This is the mode proxy is running. // typedef enum { proxy_undefined = -1, proxy_client, proxy_server, proxy_last_tag } T_proxy_mode; // // Handle advances in the connection // procedure. // typedef enum { stage_undefined, stage_initializing, stage_connecting, stage_connected, stage_waiting_forwarder_version, stage_waiting_forwarder_options, stage_sending_forwarder_options, stage_waiting_proxy_version, stage_waiting_proxy_options, stage_sending_proxy_options, stage_waiting_proxy_caches, stage_sending_proxy_caches, stage_operational, stage_terminating, stage_terminated } T_proxy_stage; // // Hint about whether or not the proxy is // connected to a NX agen. // typedef enum { session_undefined = -1, session_agent, session_shadow, session_proxy, session_last_tag } T_session_mode; // // Set how data will be written to the peer // socket. // typedef enum { policy_undefined = -1, policy_immediate, policy_deferred } T_flush_policy; // // Link mode, after negotiation, will be set to // any of the values defined in the NXproto.h. // #define link_undefined -1; // // This class collects functioning parameters, // to be configurable at run-time. They are for // the most part regarding timeouts, transport // and message stores handling. // class Control { public: // // Does proxy run in client mode or server mode? // As soon as we'll have gone through parsing of // the command line options the current mode will // be propagated to the control class. // T_proxy_mode ProxyMode; // // Goes from initializing to operational. // T_proxy_stage ProxyStage; // // Hint about type of session currently running. // T_session_mode SessionMode; // // Either immediate or defferred flushes. // T_flush_policy FlushPolicy; // // If set, the channels will try to flush the // encoded data whenever there is a prioritized // message. Depending on the flush policy, this // may determine an immediate flush or an event // being generated telling to the agent that it // should flush the proxy link. // int FlushPriority; // // Id corresponding to link speed negotiated // between proxies. // int LinkMode; // // Set if the proxy is connected to a program // providing the encryption of the point to // point communication. // int LinkEncrypted; // // Maximum number of bytes sent for each token. // int TokenSize; // // Maximum number of tokens that can be spent // by the client proxy before having to block // waiting for a reply. // int TokenLimit; // // Bitmask used to determine the distribution // of channel ids between the client and server // proxies. // int ChannelMask; // // Kill session if control parameters cannot // be negotiated before this timeout. // int InitTimeout; // // Enter the congestion state if the remote does // not reply to the ping within the given amount // of time. // int PingTimeout; // // Enqueue motion notify events in server channel. // int MotionTimeout; // // Force an update of the congestion counter if // the proxy is idle for this time. // int IdleTimeout; // // Close the connection if can't write before // this timeout. // int ChannelTimeout; // // Close connection if can't write before // this timeout. // int ProxyTimeout; // // How many milliseconds to wait for the shared // memory completion event to become available. // int ShmemTimeout; // // Wait for applications to complete at the time // proxy is shut down. // int CleanupTimeout; // // Wait this amount of milliseconds before any // iteration of the house-keeping process. // int KeeperTimeout; // // Adjust timeout calculations. // int LatencyTimeout; // // Maximum allowed size of log files. // int FileSizeLimit; int FileSizeCheckTimeout; // // What do we do at the end of session? If // this flag is set we launch a new client // letting the user run a new NX session. // int EnableRestartOnShutdown; // // The client can request the proxy to kill // a number of processes before exiting. // int *KillDaemonOnShutdown; int KillDaemonOnShutdownNumber; int KillDaemonOnShutdownLimit; // // Do we generate a core dump and exit in // case of program errors? // int EnableCoreDumpOnAbort; // // Is statistic output enabled? // int EnableStatistics; // // Version number of local and remote proxy. // int LocalVersionMajor; int LocalVersionMinor; int LocalVersionPatch; int RemoteVersionMajor; int RemoteVersionMinor; int RemoteVersionPatch; int CompatVersionMajor; int CompatVersionMinor; int CompatVersionPatch; // // Which unpack methods are implemented in proxy? // unsigned char *LocalUnpackMethods; unsigned char *RemoteUnpackMethods; // // Memory restriction imposed by user. // int LocalMemoryLevel; // // Use or not differential compression // and caching of X protocol messages. // int LocalDeltaCompression; int RemoteDeltaCompression; // // Compression of images and replies. // int LocalDataCompression; int LocalDataCompressionLevel; int RemoteDataCompression; int RemoteDataCompressionLevel; // // Minimum packet size to be compressed. // int LocalDataCompressionThreshold; // // Compress or not data flowing through the proxy // link. Level should be one of the ZLIB level as // Z_DEFAULT_COMPRESSION or Z_BEST_COMPRESSION. // int LocalStreamCompression; int LocalStreamCompressionLevel; int RemoteStreamCompression; int RemoteStreamCompressionLevel; // // Size of read operations in read buffer classes. // int ClientInitialReadSize; int ClientMaximumBufferSize; int ServerInitialReadSize; int ServerMaximumBufferSize; int ProxyInitialReadSize; int ProxyMaximumBufferSize; int GenericInitialReadSize; int GenericMaximumBufferSize; // // Set initial size and resize policy of // transport buffers. If maximum size is // exceeded, print a warning. // int TransportXBufferSize; int TransportProxyBufferSize; int TransportGenericBufferSize; int TransportXBufferThreshold; int TransportProxyBufferThreshold; int TransportGenericBufferThreshold; int TransportMaximumBufferSize; // // Flush the data produced for the channel // connection if it exceeds this size. // int TransportFlushBufferSize; // // Socket options. // int OptionProxyKeepAlive; int OptionProxyLowDelay; int OptionProxyClientNoDelay; int OptionProxyServerNoDelay; int OptionClientNoDelay; int OptionServerNoDelay; int OptionProxyReceiveBuffer; int OptionClientReceiveBuffer; int OptionServerReceiveBuffer; int OptionProxySendBuffer; int OptionClientSendBuffer; int OptionServerSendBuffer; int OptionProxyRetryAccept; int OptionProxyRetryConnect; int OptionServerRetryConnect; // // Calculate current bitrate on proxy link // using these observation periods. Value // is in milliseconds. // int ShortBitrateTimeFrame; int LongBitrateTimeFrame; // // Limit the bandwidth usage of the proxy // link. // int LocalBitrateLimit; int ClientBitrateLimit; int ServerBitrateLimit; // // This is the limit imposed by user on // total cache size. // int ClientTotalStorageSize; int ServerTotalStorageSize; int LocalTotalStorageSize; int RemoteTotalStorageSize; // // Discard messages in store older than // this amount of seconds. // int StoreTimeLimit; // // Any new message in store starts with // this amount of hits. // int StoreHitsAddBonus; // // Unless it is loaded from persistent // cache. // int StoreHitsLoadBonus; // // Stop increasing hits at this threshold. // int StoreHitsLimit; // // Give a special weight to messages put or // taken from cache during startup time. // int StoreHitsStartup; // // Weight of touch and untoch operations. // int StoreHitsTouch; int StoreHitsUntouch; // // Directives on size of messages to cache. // int MinimumMessageSize; int MaximumMessageSize; // // Maximum size of a single X request. // int MaximumRequestSize; // // Currently selected streaming mode. // int SplitMode; // // Send new split data any given amount of // milliseconds. // int SplitTimeout; // // Maximum number of distinct messages and // maximum size in bytes of the temporary // storage. // int SplitTotalSize; int SplitTotalStorageSize; // // Don't split messages smaller that this // threshold and send no more than the // given amount of bytes in a single data // shot when streaming the split messages. // int SplitDataThreshold; int SplitDataPacketLimit; // // Agent related parameters. These values apply // to the agent which, at startup, must query // the user's settings. // int PackMethod; int PackQuality; int HideRender; int TaintReplies; int TaintThreshold; // // Do we allow shared memory image support in // client and or server? // int ShmemClient; int ShmemServer; // // Default size of shared memory segments used // in MIT-SHM support. // int ShmemClientSize; int ShmemServerSize; // // The user's home directory. // char *HomePath; // // The ".nx" directory, usually in // the user's home. // char *RootPath; // // Usually the /usr/NX" directory. // char *SystemPath; // // Usually the "/tmp" directory. // char *TempPath; // // The complete path to the client. // char *ClientPath; // // String containing path of cache // file selected for load or save. // char *PersistentCachePath; // // Name of selected cache file. // char *PersistentCacheName; // // Minimum size of cache in memory // to proceed to its storage on disk. // int PersistentCacheThreshold; // // Is persistent cache enabled? // int PersistentCacheEnableLoad; int PersistentCacheEnableSave; // // This is used just for test because // it requires that client and server // reside on the same machine. // int PersistentCacheCheckOnShutdown; // // Load packed image and render extension // message stores. This currently depends // on the type of session. // int PersistentCacheLoadPacked; int PersistentCacheLoadRender; // // Maximum disk consumption of message // caches on disk. // int PersistentCacheDiskLimit; // // String containing the base path // of image cache files. // char *ImageCachePath; // // Is image cache enabled? // int ImageCacheEnableLoad; int ImageCacheEnableSave; // // Maximum disk consumption of image // caches on disk. // int ImageCacheDiskLimit; // // Only constructor, destructor // and a few utility functions. // Control(); ~Control(); // // Should not leverage control to find channel // stores' size limits. As most of values in // control, this info must be moved elsewhere. // int getUpperStorageSize() const { return (ClientTotalStorageSize > ServerTotalStorageSize ? ClientTotalStorageSize : ServerTotalStorageSize); } int getLowerStorageSize() const { return (ClientTotalStorageSize < ServerTotalStorageSize ? ClientTotalStorageSize : ServerTotalStorageSize); } void setProtoStep(int step); int getProtoStep(); int isProtoStep7() { return protoStep7_; } int isProtoStep8() { return protoStep8_; } int isProtoStep9() { return protoStep9_; } int isProtoStep10() { return protoStep10_; } private: // // Look in Control.cpp. // void setLocalUnpackMethods(); // // Manage the encoding according // to the protocol version. // int protoStep6_; int protoStep7_; int protoStep8_; int protoStep9_; int protoStep10_; }; #endif /* Control_H */ nxcomp/GetProperty.cpp0000644000076400007640000000751311323113031015314 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "GetProperty.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int GetPropertyStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { GetPropertyMessage *getProperty = (GetPropertyMessage *) message; // // Here is the fingerprint. // getProperty -> property_delete = *(buffer + 1); getProperty -> window = GetULONG(buffer + 4, bigEndian); getProperty -> property = GetULONG(buffer + 8, bigEndian); getProperty -> type = GetULONG(buffer + 12, bigEndian); getProperty -> long_offset = GetULONG(buffer + 16, bigEndian); getProperty -> long_length = GetULONG(buffer + 20, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int GetPropertyStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { GetPropertyMessage *getProperty = (GetPropertyMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = getProperty -> property_delete; PutULONG(getProperty -> window, buffer + 4, bigEndian); PutULONG(getProperty -> property, buffer + 8, bigEndian); PutULONG(getProperty -> type, buffer + 12, bigEndian); PutULONG(getProperty -> long_offset, buffer + 16, bigEndian); PutULONG(getProperty -> long_length, buffer + 20, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void GetPropertyStore::dumpIdentity(const Message *message) const { #ifdef DUMP GetPropertyMessage *getProperty = (GetPropertyMessage *) message; *logofs << name() << ": Identity property_delete " << (unsigned int) getProperty -> property_delete << ", window " << getProperty -> window << ", property " << getProperty -> property << ", type " << getProperty -> type << ", long-offset " << getProperty -> long_offset << ", long-length " << getProperty -> long_length << ".\n" << logofs_flush; #endif } void GetPropertyStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 1, 1); md5_append(md5_state_, buffer + 4, 20); } nxcomp/VERSION0000644000076400007640000000000611561774132013405 0ustar svetonisvetoni3.5.0 nxcomp/EncodeBuffer.h0000644000076400007640000001237411323113027015032 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef EncodeBuffer_H #define EncodeBuffer_H #include "IntCache.h" #include "CharCache.h" #include "XidCache.h" #include "FreeCache.h" #include "OpcodeCache.h" #include "ActionCache.h" #include "ActionCacheCompat.h" #include "PositionCacheCompat.h" #define ENCODE_BUFFER_DEFAULT_SIZE 16384 // // This should match the maximum size of // a single message added to write buffer // (see WriteBuffer.h). // #define ENCODE_BUFFER_OVERFLOW_SIZE 4194304 // // Adjust for the control messages and the // frame length added by the proxy. // #define ENCODE_BUFFER_PREFIX_SIZE 64 // // The encode routines may write one byte // past the nominal end of the encode buffer. // This additional byte is included in the // payload. This is actually a harmless bug. // #define ENCODE_BUFFER_POSTFIX_SIZE 1 class EncodeBuffer { public: EncodeBuffer(); ~EncodeBuffer(); void setSize(unsigned int initialSize, unsigned int thresholdSize, unsigned int maximumSize); void encodeValue(unsigned int value, unsigned int numBits, unsigned int blockSize = 0); void encodeCachedValue(unsigned int value, unsigned int numBits, IntCache &cache, unsigned int blockSize = 0); void encodeCachedValue(unsigned char value, unsigned int numBits, CharCache &cache, unsigned int blockSize = 0); void encodeDiffCachedValue(const unsigned int value, unsigned int &previous, unsigned int numBits, IntCache &cache, unsigned int blockSize = 0) { encodeCachedValue((value - 1) - previous, numBits, cache, blockSize); previous = value; } void encodeBoolValue(unsigned int value) { encodeValue(value, 1); } void encodeOpcodeValue(unsigned char value, OpcodeCache &cache) { encodeCachedValue(value, 8, cache.base_[cache.slot_], 8); cache.slot_ = value; } void encodeActionValue(unsigned char value, ActionCache &cache) { unsigned short position = 0; encodeActionValue(value, position, cache); } void encodeActionValue(unsigned char value, unsigned short position, ActionCache &cache); void encodeNewXidValue(unsigned int value, unsigned int &lastId, IntCache &lastIdCache, IntCache &cache, FreeCache &freeCache); void encodeNewXidValue(unsigned int value, unsigned int &lastId, IntCache &lastIdCache, XidCache &cache, FreeCache &freeCache); void encodeXidValue(unsigned int value, XidCache &cache); void encodeFreeXidValue(unsigned int value, FreeCache &cache); void encodeActionValueCompat(unsigned char value, ActionCacheCompat &cache) { encodeCachedValue(value, 2, cache.base_[cache.slot_]); cache.slot_ = value; } void encodePositionValueCompat(short int value, PositionCacheCompat &cache); void encodeTextData(const unsigned char *buffer, unsigned int numBytes) { encodeMemory(buffer, numBytes); } void encodeIntData(const unsigned char *buffer, unsigned int numBytes) { encodeMemory(buffer, numBytes); } void encodeLongData(const unsigned char *buffer, unsigned int numBytes) { encodeMemory(buffer, numBytes); } void encodeMemory(const unsigned char *buffer, unsigned int numBytes); unsigned char *getData() { return buffer_; } unsigned int getLength() const; unsigned int getBits() const { return ((nextDest_ - buffer_) << 3) + (7 - destShift_); } unsigned int diffBits(); void fullReset(); private: void growBuffer(unsigned int numBytes = 0); void alignBuffer(); unsigned int size_; unsigned char *buffer_; // // This points to the first byte // just beyond end of the buffer. // const unsigned char *end_; unsigned char *nextDest_; unsigned int destShift_; unsigned int lastBits_; unsigned int initialSize_; unsigned int thresholdSize_; unsigned int maximumSize_; }; #endif /* EncodeBuffer_H */ nxcomp/PolyFillRectangle.h0000644000076400007640000001167611323113027016066 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PolyFillRectangle_H #define PolyFillRectangle_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define POLYFILLRECTANGLE_ENABLE_CACHE 1 #define POLYFILLRECTANGLE_ENABLE_DATA 0 #define POLYFILLRECTANGLE_ENABLE_SPLIT 0 #define POLYFILLRECTANGLE_ENABLE_COMPRESS 0 #define POLYFILLRECTANGLE_DATA_LIMIT 2048 #define POLYFILLRECTANGLE_DATA_OFFSET 12 #define POLYFILLRECTANGLE_CACHE_SLOTS 4000 #define POLYFILLRECTANGLE_CACHE_THRESHOLD 5 #define POLYFILLRECTANGLE_CACHE_LOWER_THRESHOLD 1 // // The message class. // class PolyFillRectangleMessage : public Message { friend class PolyFillRectangleStore; public: PolyFillRectangleMessage() { } ~PolyFillRectangleMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int drawable; unsigned int gcontext; }; class PolyFillRectangleStore : public MessageStore { // // Constructors and destructors. // public: PolyFillRectangleStore() : MessageStore() { enableCache = POLYFILLRECTANGLE_ENABLE_CACHE; enableData = POLYFILLRECTANGLE_ENABLE_DATA; enableSplit = POLYFILLRECTANGLE_ENABLE_SPLIT; enableCompress = POLYFILLRECTANGLE_ENABLE_COMPRESS; dataLimit = POLYFILLRECTANGLE_DATA_LIMIT; dataOffset = POLYFILLRECTANGLE_DATA_OFFSET; cacheSlots = POLYFILLRECTANGLE_CACHE_SLOTS; cacheThreshold = POLYFILLRECTANGLE_CACHE_THRESHOLD; cacheLowerThreshold = POLYFILLRECTANGLE_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~PolyFillRectangleStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "PolyFillRectangle"; } virtual unsigned char opcode() const { return X_PolyFillRectangle; } virtual unsigned int storage() const { return sizeof(PolyFillRectangleMessage); } // // Message handling methods. // public: virtual Message *create() const { return new PolyFillRectangleMessage(); } virtual Message *create(const Message &message) const { return new PolyFillRectangleMessage((const PolyFillRectangleMessage &) message); } virtual void destroy(Message *message) const { delete (PolyFillRectangleMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* PolyFillRectangle_H */ nxcomp/ChangeGCCompat.cpp0000644000076400007640000000766311323113027015606 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ChangeGCCompat.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int ChangeGCCompatStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { ChangeGCCompatMessage *changeGC = (ChangeGCCompatMessage *) message; // // Here is the fingerprint. // changeGC -> gcontext = GetULONG(buffer + 4, bigEndian); changeGC -> value_mask = GetULONG(buffer + 8, bigEndian); // // Clear the unused bytes carried in the // payload to increase the effectiveness // of the caching algorithm. // if ((int) size > dataOffset) { #ifdef DEBUG *logofs << name() << ": Removing unused bytes from the " << "data payload.\n" << logofs_flush; #endif changeGC -> value_mask &= (1 << 23) - 1; unsigned int mask = 0x1; unsigned char *source = (unsigned char *) buffer + CHANGEGC_DATA_OFFSET; unsigned long value = 0; for (unsigned int i = 0; i < 23; i++) { if (changeGC -> value_mask & mask) { value = GetULONG(source, bigEndian); value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i])); PutULONG(value, source, bigEndian); source += 4; } mask <<= 1; } } #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int ChangeGCCompatStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { ChangeGCCompatMessage *changeGC = (ChangeGCCompatMessage *) message; // // Fill all the message's fields. // PutULONG(changeGC -> gcontext, buffer + 4, bigEndian); PutULONG(changeGC -> value_mask, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void ChangeGCCompatStore::dumpIdentity(const Message *message) const { #ifdef DUMP ChangeGCCompatMessage *changeGC = (ChangeGCCompatMessage *) message; *logofs << name() << ": Identity gcontext " << changeGC -> gcontext << ", mask " << changeGC -> value_mask << ", size " << changeGC -> size_ << ".\n" << logofs_flush; #endif } void ChangeGCCompatStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 4, 8); } nxcomp/SetUnpackAlpha.h0000644000076400007640000001134011323113030015330 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef SetUnpackAlpha_H #define SetUnpackAlpha_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define SETUNPACKALPHA_ENABLE_CACHE 1 #define SETUNPACKALPHA_ENABLE_DATA 1 #define SETUNPACKALPHA_ENABLE_SPLIT 0 #define SETUNPACKALPHA_ENABLE_COMPRESS 1 #define SETUNPACKALPHA_DATA_LIMIT 16384 #define SETUNPACKALPHA_DATA_OFFSET 8 #define SETUNPACKALPHA_CACHE_SLOTS 2000 #define SETUNPACKALPHA_CACHE_THRESHOLD 10 #define SETUNPACKALPHA_CACHE_LOWER_THRESHOLD 5 #define SETUNPACKALPHA_DATA_OFFSET_IF_PROTO_STEP_7 16 #define SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_7 1 #define SETUNPACKALPHA_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 #define SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_8 0 // // The message class. // class SetUnpackAlphaMessage : public Message { friend class SetUnpackAlphaStore; public: SetUnpackAlphaMessage() { } ~SetUnpackAlphaMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char client; unsigned char method; unsigned int src_length; unsigned int dst_length; }; class SetUnpackAlphaStore : public MessageStore { public: SetUnpackAlphaStore(StaticCompressor *compressor); virtual ~SetUnpackAlphaStore(); virtual const char *name() const { return "SetUnpackAlpha"; } virtual unsigned char opcode() const { return X_NXSetUnpackAlpha; } virtual unsigned int storage() const { return sizeof(SetUnpackAlphaMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new SetUnpackAlphaMessage(); } virtual Message *create(const Message &message) const { return new SetUnpackAlphaMessage((const SetUnpackAlphaMessage &) message); } virtual void destroy(Message *message) const { delete (SetUnpackAlphaMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* SetUnpackAlpha_H */ nxcomp/README0000644000076400007640000000066111323113027013206 0ustar svetonisvetoniREADME ------ Building -------- 1. To compile: > tar zxvf nxcomp-X.Y.Z-N.tar.gz > cd nxcomp > ./configure > make You'll have to run gmake under Solaris. 2. The 'make install' target is not currently supported in the Makefile, but it should be simple to fix. You need at least nxproxy and nxagent packages to enjoy this code. Check the NoMachine website at http://www.nomachine.com to get the latest release. nxcomp/CharCache.cpp0000644000076400007640000000405111323113027014630 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "CharCache.h" int CharCache::lookup(unsigned char value, unsigned int &index) { for (unsigned int i = 0; i < length_; i++) if (value == buffer_[i]) { index = i; if (i) { unsigned int target = (i >> 1); do { buffer_[i] = buffer_[i - 1]; i--; } while (i > target); buffer_[target] = value; } return 1; } insert(value); return 0; } void CharCache::insert(unsigned char value) { unsigned int insertionPoint = 0; if (2 >= length_) insertionPoint = length_; else insertionPoint = 2; unsigned int start; if (length_ >= 7) start = 7 - 1; else { start = length_; length_++; } for (unsigned int k = start; k > insertionPoint; k--) buffer_[k] = buffer_[k - 1]; buffer_[insertionPoint] = value; } nxcomp/configure0000755000076400007640000051763211342773403014263 0ustar svetonisvetoni#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.59. # # Copyright (C) 2003 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi DUALCASE=1; export DUALCASE # for MKS sh # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` exec 6>&1 # # Initializations. # ac_default_prefix=/usr/local ac_config_libobj_dir=. cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Maximum number of lines to put in a shell here document. # This variable seems obsolete. It should probably be removed, and # only ac_max_sed_lines should be used. : ${ac_max_here_lines=38} # Identity of this package. PACKAGE_NAME= PACKAGE_TARNAME= PACKAGE_VERSION= PACKAGE_STRING= PACKAGE_BUGREPORT= ac_unique_file="NX.h" ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS armcxx armcc CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT CC CFLAGS ac_ct_CC INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CXXCPP X_CFLAGS X_PRE_LIBS X_LIBS X_EXTRA_LIBS LIBVERSION VERSION MAKEDEPEND ALL LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. ac_init_help= ac_init_version=false # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' ac_prev= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_option in -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir=$ac_optarg ;; -disable-* | --disable-*) ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` eval "enable_$ac_feature=no" ;; -enable-* | --enable-*) ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid feature name: $ac_feature" >&2 { (exit 1); exit 1; }; } ac_feature=`echo $ac_feature | sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "enable_$ac_feature='$ac_optarg'" ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package| sed 's/-/_/g'` case $ac_option in *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; *) ac_optarg=yes ;; esac eval "with_$ac_package='$ac_optarg'" ;; -without-* | --without-*) ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid package name: $ac_package" >&2 { (exit 1); exit 1; }; } ac_package=`echo $ac_package | sed 's/-/_/g'` eval "with_$ac_package=no" ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) { echo "$as_me: error: unrecognized option: $ac_option Try \`$0 --help' for more information." >&2 { (exit 1); exit 1; }; } ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 { (exit 1); exit 1; }; } ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` eval "$ac_envvar='$ac_optarg'" export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` { echo "$as_me: error: missing argument to $ac_option" >&2 { (exit 1); exit 1; }; } fi # Be sure to have absolute paths. for ac_var in exec_prefix prefix do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* | NONE | '' ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # Be sure to have absolute paths. for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ localstatedir libdir includedir oldincludedir infodir mandir do eval ac_val=$`echo $ac_var` case $ac_val in [\\/$]* | ?:[\\/]* ) ;; *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 { (exit 1); exit 1; }; };; esac done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then its parent. ac_confdir=`(dirname "$0") 2>/dev/null || $as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$0" : 'X\(//\)[^/]' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$0" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` srcdir=$ac_confdir if test ! -r $srcdir/$ac_unique_file; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r $srcdir/$ac_unique_file; then if test "$ac_srcdir_defaulted" = yes; then { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 { (exit 1); exit 1; }; } else { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 { (exit 1); exit 1; }; } fi fi (cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 { (exit 1); exit 1; }; } srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` ac_env_build_alias_set=${build_alias+set} ac_env_build_alias_value=$build_alias ac_cv_env_build_alias_set=${build_alias+set} ac_cv_env_build_alias_value=$build_alias ac_env_host_alias_set=${host_alias+set} ac_env_host_alias_value=$host_alias ac_cv_env_host_alias_set=${host_alias+set} ac_cv_env_host_alias_value=$host_alias ac_env_target_alias_set=${target_alias+set} ac_env_target_alias_value=$target_alias ac_cv_env_target_alias_set=${target_alias+set} ac_cv_env_target_alias_value=$target_alias ac_env_CXX_set=${CXX+set} ac_env_CXX_value=$CXX ac_cv_env_CXX_set=${CXX+set} ac_cv_env_CXX_value=$CXX ac_env_CXXFLAGS_set=${CXXFLAGS+set} ac_env_CXXFLAGS_value=$CXXFLAGS ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} ac_cv_env_CXXFLAGS_value=$CXXFLAGS ac_env_LDFLAGS_set=${LDFLAGS+set} ac_env_LDFLAGS_value=$LDFLAGS ac_cv_env_LDFLAGS_set=${LDFLAGS+set} ac_cv_env_LDFLAGS_value=$LDFLAGS ac_env_CPPFLAGS_set=${CPPFLAGS+set} ac_env_CPPFLAGS_value=$CPPFLAGS ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} ac_cv_env_CPPFLAGS_value=$CPPFLAGS ac_env_CC_set=${CC+set} ac_env_CC_value=$CC ac_cv_env_CC_set=${CC+set} ac_cv_env_CC_value=$CC ac_env_CFLAGS_set=${CFLAGS+set} ac_env_CFLAGS_value=$CFLAGS ac_cv_env_CFLAGS_set=${CFLAGS+set} ac_cv_env_CFLAGS_value=$CFLAGS ac_env_CXXCPP_set=${CXXCPP+set} ac_env_CXXCPP_value=$CXXCPP ac_cv_env_CXXCPP_set=${CXXCPP+set} ac_cv_env_CXXCPP_value=$CXXCPP # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures this package to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] _ACEOF cat <<_ACEOF Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data [PREFIX/share] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --infodir=DIR info documentation [PREFIX/info] --mandir=DIR man documentation [PREFIX/man] _ACEOF cat <<\_ACEOF X features: --x-includes=DIR X include files are in DIR --x-libraries=DIR X library files are in DIR _ACEOF fi if test -n "$ac_init_help"; then cat <<\_ACEOF Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-x use the X Window System Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CC C compiler command CFLAGS C compiler flags CXXCPP C++ preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. _ACEOF fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. ac_popdir=`pwd` for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d $ac_dir || continue ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac cd $ac_dir # Check for guested configure; otherwise get Cygnus style configure. if test -f $ac_srcdir/configure.gnu; then echo $SHELL $ac_srcdir/configure.gnu --help=recursive elif test -f $ac_srcdir/configure; then echo $SHELL $ac_srcdir/configure --help=recursive elif test -f $ac_srcdir/configure.ac || test -f $ac_srcdir/configure.in; then echo $ac_configure --help else echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi cd $ac_popdir done fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF Copyright (C) 2003 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit 0 fi exec 5>config.log cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by $as_me, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ _ACEOF { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` hostinfo = `(hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. echo "PATH: $as_dir" done } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_sep= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; 2) ac_configure_args1="$ac_configure_args1 '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" # Get rid of the leading space. ac_sep=" " ;; esac done done $as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } $as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Be sure not to use single quotes in there, as some shells, # such as our DU 5.0 friend, will then `close' the trap. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo cat <<\_ASBOX ## ---------------- ## ## Cache variables. ## ## ---------------- ## _ASBOX echo # The following way of writing the cache mishandles newlines in values, { (set) 2>&1 | case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in *ac_space=\ *) sed -n \ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" ;; *) sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } echo cat <<\_ASBOX ## ----------------- ## ## Output variables. ## ## ----------------- ## _ASBOX echo for ac_var in $ac_subst_vars do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo if test -n "$ac_subst_files"; then cat <<\_ASBOX ## ------------- ## ## Output files. ## ## ------------- ## _ASBOX echo for ac_var in $ac_subst_files do eval ac_val=$`echo $ac_var` echo "$ac_var='"'"'$ac_val'"'"'" done | sort echo fi if test -s confdefs.h; then cat <<\_ASBOX ## ----------- ## ## confdefs.h. ## ## ----------- ## _ASBOX echo sed "/^$/d" confdefs.h | sort echo fi test "$ac_signal" != 0 && echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 rm -f core *.core && rm -rf conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo >confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special # files actually), so we avoid doing that. if test -f "$cache_file"; then { echo "$as_me:$LINENO: loading cache $cache_file" >&5 echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . $cache_file;; *) . ./$cache_file;; esac fi else { echo "$as_me:$LINENO: creating cache $cache_file" >&5 echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in `(set) 2>&1 | sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val="\$ac_cv_env_${ac_var}_value" eval ac_new_val="\$ac_env_${ac_var}_value" case $ac_old_set,$ac_new_set in set,) { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 echo "$as_me: former value: $ac_old_val" >&2;} { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 echo "$as_me: current value: $ac_new_val" >&2;} ac_cache_corrupted=: fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 echo "$as_me: error: changes in the environment can compromise the build" >&2;} { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} { (exit 1); exit 1; }; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CXXFLAGS="-O3 -fno-rtti -fno-exceptions" CFLAGS="-O3" LIBSTATIC="" LIBSHARED="" if test -d "../nx-X11/include" ; then CXXFLAGS="$CXXFLAGS -I../nx-X11/exports/include" CFLAGS="$CFLAGS -I../nx-X11/exports/include" LIBS="$LIBS -L../nx-X11/exports/lib" fi if test "${with_ipaq}" = yes; then echo -e "enabling IPAQ configuration" CXX="arm-linux-c++" CC="arm-linux-gcc" unset ac_cv_prog_armcxx unset ac_cv_prog_armcc unset ac_cv_prog_CXXCPP # Extract the first word of ""$CXX"", so it can be a program name with args. set dummy "$CXX"; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_armcxx+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$armcxx"; then ac_cv_prog_armcxx="$armcxx" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_armcxx="yes" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_armcxx" && ac_cv_prog_armcxx="no" fi fi armcxx=$ac_cv_prog_armcxx if test -n "$armcxx"; then echo "$as_me:$LINENO: result: $armcxx" >&5 echo "${ECHO_T}$armcxx" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi # Extract the first word of ""$CC"", so it can be a program name with args. set dummy "$CC"; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_armcc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$armcc"; then ac_cv_prog_armcc="$armcc" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_armcc="yes" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done test -z "$ac_cv_prog_armcc" && ac_cv_prog_armcc="no" fi fi armcc=$ac_cv_prog_armcc if test -n "$armcc"; then echo "$as_me:$LINENO: result: $armcc" >&5 echo "${ECHO_T}$armcc" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi if test $armcxx = "yes" && test $armcc = "yes" ; then ac_cv_prog_CXX="$CXX" ac_cv_prog_CC="$CC" else { { echo "$as_me:$LINENO: error: Installation or configuration problem. Cannot find compiler for arm-linux." >&5 echo "$as_me: error: Installation or configuration problem. Cannot find compiler for arm-linux." >&2;} { (exit 1); exit 1; }; } fi else unset ac_cv_prog_CXX unset ac_cv_prog_CC unset ac_cv_prog_CXXCPP fi ac_ext=cc ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -n "$ac_tool_prefix"; then for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CXX+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then echo "$as_me:$LINENO: result: $CXX" >&5 echo "${ECHO_T}$CXX" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 echo "${ECHO_T}$ac_ct_CXX" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$ac_ct_CXX" && break done test -n "$ac_ct_CXX" || ac_ct_CXX="g++" CXX=$ac_ct_CXX fi # Provide some information about the compiler. echo "$as_me:$LINENO:" \ "checking for C++ compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 (eval $ac_compiler --version &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 (eval $ac_compiler -v &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 (eval $ac_compiler -V &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5 echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6 ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 (eval $ac_link_default) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # Find the output, starting from the most likely. This scheme is # not robust to junk in `.', hence go to wildcards (a.*) only as a last # resort. # Be careful to initialize this variable, since it used to be cached. # Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. ac_cv_exeext= # b.out is created by i960 compilers. for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; conftest.$ac_ext ) # This is the source file. ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` # FIXME: I believe we export ac_cv_exeext for Libtool, # but it would be cool to find out if it's true. Does anybody # maintain Libtool? --akim. export ac_cv_exeext break;; * ) break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: C++ compiler cannot create executables See \`config.log' for more details." >&5 echo "$as_me: error: C++ compiler cannot create executables See \`config.log' for more details." >&2;} { (exit 77); exit 77; }; } fi ac_exeext=$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_file" >&5 echo "${ECHO_T}$ac_file" >&6 # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5 echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6 # FIXME: These cross compiler hacks should be removed for Autoconf 3.0 # If not cross compiling, check that we can run a simple program. if test "$cross_compiling" != yes; then if { ac_try='./$ac_file' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { echo "$as_me:$LINENO: error: cannot run C++ compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&5 echo "$as_me: error: cannot run C++ compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi fi fi echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 rm -f a.out a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save # Check the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 echo "$as_me:$LINENO: result: $cross_compiling" >&5 echo "${ECHO_T}$cross_compiling" >&6 echo "$as_me:$LINENO: checking for suffix of executables" >&5 echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` export ac_cv_exeext break;; * ) break;; esac done else { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of executables: cannot compile and link See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest$ac_cv_exeext echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 echo "${ECHO_T}$ac_cv_exeext" >&6 rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT echo "$as_me:$LINENO: checking for suffix of object files" >&5 echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 if test "${ac_cv_objext+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; then for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&5 echo "$as_me: error: cannot compute suffix of object files: cannot compile See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 echo "${ECHO_T}$ac_cv_objext" >&6 OBJEXT=$ac_cv_objext ac_objext=$OBJEXT echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 if test "${ac_cv_cxx_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 GXX=`test $ac_compiler_gnu = yes && echo yes` ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS CXXFLAGS="-g" echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 if test "${ac_cv_prog_cxx_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cxx_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_prog_cxx_g=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6 if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi for ac_declaration in \ '' \ 'extern "C" void std::exit (int) throw (); using std::exit;' \ 'extern "C" void std::exit (int); using std::exit;' \ 'extern "C" void exit (int) throw ();' \ 'extern "C" void exit (int);' \ 'void exit (int);' do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration #include int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 continue fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done rm -f conftest* if test -n "$ac_declaration"; then echo '#ifdef __cplusplus' >>confdefs.h echo $ac_declaration >>confdefs.h echo '#endif' >>confdefs.h fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi CC=$ac_ct_CC else CC="$ac_cv_prog_CC" fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then echo "$as_me:$LINENO: result: $CC" >&5 echo "${ECHO_T}$CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 echo "$as_me:$LINENO: checking for $ac_word" >&5 echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 if test "${ac_cv_prog_ac_ct_CC+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 echo "${ECHO_T}$ac_ct_CC" >&6 else echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 fi test -n "$ac_ct_CC" && break done CC=$ac_ct_CC fi fi test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&5 echo "$as_me: error: no acceptable C compiler found in \$PATH See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } # Provide some information about the compiler. echo "$as_me:$LINENO:" \ "checking for C compiler version" >&5 ac_compiler=`set X $ac_compile; echo $2` { (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 (eval $ac_compiler --version &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 (eval $ac_compiler -v &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } { (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 (eval $ac_compiler -V &5) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 if test "${ac_cv_c_compiler_gnu+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_compiler_gnu=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_compiler_gnu=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 GCC=`test $ac_compiler_gnu = yes && echo yes` ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS CFLAGS="-g" echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 if test "${ac_cv_prog_cc_g+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_g=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_prog_cc_g=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 if test "${ac_cv_prog_cc_stdc+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_cv_prog_cc_stdc=no ac_save_CC=$CC cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std1 is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std1. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF # Don't try gcc -ansi; that turns off useful extensions and # breaks some systems' header files. # AIX -qlanglvl=ansi # Ultrix and OSF/1 -std1 # HP-UX 10.20 and later -Ae # HP-UX older versions -Aa -D_HPUX_SOURCE # SVR4 -Xc -D__EXTENSIONS__ for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_prog_cc_stdc=$ac_arg break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext done rm -f conftest.$ac_ext conftest.$ac_objext CC=$ac_save_CC fi case "x$ac_cv_prog_cc_stdc" in x|xno) echo "$as_me:$LINENO: result: none needed" >&5 echo "${ECHO_T}none needed" >&6 ;; *) echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 CC="$CC $ac_cv_prog_cc_stdc" ;; esac # Some people use a C++ compiler to compile C. Since we use `exit', # in C++ we need to declare it. In case someone uses the same compiler # for both compiling C and C++ we need to have the C++ compiler decide # the declaration of exit, since it's the most demanding environment. cat >conftest.$ac_ext <<_ACEOF #ifndef __cplusplus choke me #endif _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then for ac_declaration in \ '' \ 'extern "C" void std::exit (int) throw (); using std::exit;' \ 'extern "C" void std::exit (int); using std::exit;' \ 'extern "C" void exit (int) throw ();' \ 'extern "C" void exit (int);' \ 'void exit (int);' do cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration #include int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 continue fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ $ac_declaration int main () { exit (42); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then break else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext done rm -f conftest* if test -n "$ac_declaration"; then echo '#ifdef __cplusplus' >>confdefs.h echo $ac_declaration >>confdefs.h echo '#endif' >>confdefs.h fi else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=cc ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu echo "$as_me:$LINENO: checking whether compiler needs -Wno-deprecated" >&5 echo $ECHO_N "checking whether compiler needs -Wno-deprecated... $ECHO_C" >&6 gcc_version=`${CC} --version | grep 'gcc (GCC) [3-4].' | head -n 1` case "${gcc_version}" in gcc*) echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 CXXFLAGS="$CXXFLAGS -Wno-deprecated" ;; *) echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 ;; esac echo "$as_me:$LINENO: checking whether compiler accepts -Wmissing-declarations" >&5 echo $ECHO_N "checking whether compiler accepts -Wmissing-declarations... $ECHO_C" >&6 gcc_version=`${CC} --version | grep 'gcc (GCC) [3-4].' | head -n 1` case "${gcc_version}" in gcc*) echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 ;; *) echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 CXXFLAGS="$CXXFLAGS -Wmissing-declarations" ;; esac ac_aux_dir= for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do if test -f $ac_dir/install-sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f $ac_dir/install.sh; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f $ac_dir/shtool; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} { (exit 1); exit 1; }; } fi ac_config_guess="$SHELL $ac_aux_dir/config.guess" ac_config_sub="$SHELL $ac_aux_dir/config.sub" ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 if test -z "$INSTALL"; then if test "${ac_cv_path_install+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in ./ | .// | /cC/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi done done ;; esac done fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. We don't cache a # path for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the path is relative. INSTALL=$ac_install_sh fi fi echo "$as_me:$LINENO: result: $INSTALL" >&5 echo "${ECHO_T}$INSTALL" >&6 # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' ac_ext=cc ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 if test -z "$CXXCPP"; then if test "${ac_cv_prog_CXXCPP+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi echo "$as_me:$LINENO: result: $CXXCPP" >&5 echo "${ECHO_T}$CXXCPP" >&6 ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Broken: fails on valid input. continue fi rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether non-existent headers # can be detected and how. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # Broken: success on invalid input. continue else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details." >&5 echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi ac_ext=cc ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu echo "$as_me:$LINENO: checking for X" >&5 echo $ECHO_N "checking for X... $ECHO_C" >&6 # Check whether --with-x or --without-x was given. if test "${with_x+set}" = set; then withval="$with_x" fi; # $have_x is `yes', `no', `disabled', or empty when we do not yet know. if test "x$with_x" = xno; then # The user explicitly disabled X. have_x=disabled else if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then # Both variables are already set. have_x=yes else if test "${ac_cv_have_x+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else # One or both of the vars are not set, and there is no cached value. ac_x_includes=no ac_x_libraries=no rm -fr conftest.dir if mkdir conftest.dir; then cd conftest.dir # Make sure to not put "make" in the Imakefile rules, since we grep it out. cat >Imakefile <<'_ACEOF' acfindx: @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' _ACEOF if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then # GNU make sometimes prints "make[1]: Entering...", which would confuse us. eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. for ac_extension in a so sl; do if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && test -f $ac_im_libdir/libX11.$ac_extension; then ac_im_usrlibdir=$ac_im_libdir; break fi done # Screen out bogus values from the imake configuration. They are # bogus both because they are the default anyway, and because # using them would break gcc on systems where it needs fixed includes. case $ac_im_incroot in /usr/include) ;; *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; esac case $ac_im_usrlibdir in /usr/lib | /lib) ;; *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; esac fi cd .. rm -fr conftest.dir fi # Standard set of common directories for X headers. # Check X11 before X11Rn because it is often a symlink to the current release. ac_x_header_dirs=' /usr/X11/include /usr/X11R6/include /usr/X11R5/include /usr/X11R4/include /usr/include/X11 /usr/include/X11R6 /usr/include/X11R5 /usr/include/X11R4 /usr/local/X11/include /usr/local/X11R6/include /usr/local/X11R5/include /usr/local/X11R4/include /usr/local/include/X11 /usr/local/include/X11R6 /usr/local/include/X11R5 /usr/local/include/X11R4 /usr/X386/include /usr/x386/include /usr/XFree86/include/X11 /usr/include /usr/local/include /usr/unsupported/include /usr/athena/include /usr/local/x11r5/include /usr/lpp/Xamples/include /usr/openwin/include /usr/openwin/share/include' if test "$ac_x_includes" = no; then # Guess where to find include files, by looking for Intrinsic.h. # First, try using that file with no special directory specified. cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include _ACEOF if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } >/dev/null; then if test -s conftest.err; then ac_cpp_err=$ac_cxx_preproc_warn_flag ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag else ac_cpp_err= fi else ac_cpp_err=yes fi if test -z "$ac_cpp_err"; then # We can compile using X headers with no special include directory. ac_x_includes= else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 for ac_dir in $ac_x_header_dirs; do if test -r "$ac_dir/X11/Intrinsic.h"; then ac_x_includes=$ac_dir break fi done fi rm -f conftest.err conftest.$ac_ext fi # $ac_x_includes = no if test "$ac_x_libraries" = no; then # Check for the libraries. # See if we find them without any special options. # Don't add to $LIBS permanently. ac_save_LIBS=$LIBS LIBS="-lXt $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { XtMalloc (0) ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then LIBS=$ac_save_LIBS # We can link X programs with no special library path. ac_x_libraries= else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 LIBS=$ac_save_LIBS for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` do # Don't even attempt the hair of trying to link an X program! for ac_extension in a so sl; do if test -r $ac_dir/libXt.$ac_extension; then ac_x_libraries=$ac_dir break 2 fi done done fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi # $ac_x_libraries = no if test "$ac_x_includes" = no || test "$ac_x_libraries" = no; then # Didn't find X anywhere. Cache the known absence of X. ac_cv_have_x="have_x=no" else # Record where we found X for the cache. ac_cv_have_x="have_x=yes \ ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" fi fi fi eval "$ac_cv_have_x" fi # $with_x != no if test "$have_x" != yes; then echo "$as_me:$LINENO: result: $have_x" >&5 echo "${ECHO_T}$have_x" >&6 no_x=yes else # If each of the values was on the command line, it overrides each guess. test "x$x_includes" = xNONE && x_includes=$ac_x_includes test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries # Update the cache value to reflect the command line values. ac_cv_have_x="have_x=yes \ ac_x_includes=$x_includes ac_x_libraries=$x_libraries" echo "$as_me:$LINENO: result: libraries $x_libraries, headers $x_includes" >&5 echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6 fi if test "$no_x" = yes; then # Not all programs may use this symbol, but it does not hurt to define it. cat >>confdefs.h <<\_ACEOF #define X_DISPLAY_MISSING 1 _ACEOF X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS= else if test -n "$x_includes"; then X_CFLAGS="$X_CFLAGS -I$x_includes" fi # It would also be nice to do this for all -L options, not just this one. if test -n "$x_libraries"; then X_LIBS="$X_LIBS -L$x_libraries" # For Solaris; some versions of Sun CC require a space after -R and # others require no space. Words are not sufficient . . . . case `(uname -sr) 2>/dev/null` in "SunOS 5"*) echo "$as_me:$LINENO: checking whether -R must be followed by a space" >&5 echo $ECHO_N "checking whether -R must be followed by a space... $ECHO_C" >&6 ac_xsave_LIBS=$LIBS; LIBS="$LIBS -R$x_libraries" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_R_nospace=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_R_nospace=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test $ac_R_nospace = yes; then echo "$as_me:$LINENO: result: no" >&5 echo "${ECHO_T}no" >&6 X_LIBS="$X_LIBS -R$x_libraries" else LIBS="$ac_xsave_LIBS -R $x_libraries" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_R_space=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_R_space=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test $ac_R_space = yes; then echo "$as_me:$LINENO: result: yes" >&5 echo "${ECHO_T}yes" >&6 X_LIBS="$X_LIBS -R $x_libraries" else echo "$as_me:$LINENO: result: neither works" >&5 echo "${ECHO_T}neither works" >&6 fi fi LIBS=$ac_xsave_LIBS esac fi # Check for system-dependent libraries X programs must link with. # Do this before checking for the system-independent R6 libraries # (-lICE), since we may need -lsocket or whatever for X linking. if test "$ISC" = yes; then X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet" else # Martyn Johnson says this is needed for Ultrix, if the X # libraries were built with DECnet support. And Karl Berry says # the Alpha needs dnet_stub (dnet does not exist). ac_xsave_LIBS="$LIBS"; LIBS="$LIBS $X_LIBS -lX11" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char XOpenDisplay (); int main () { XOpenDisplay (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then : else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 echo "$as_me:$LINENO: checking for dnet_ntoa in -ldnet" >&5 echo $ECHO_N "checking for dnet_ntoa in -ldnet... $ECHO_C" >&6 if test "${ac_cv_lib_dnet_dnet_ntoa+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldnet $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char dnet_ntoa (); int main () { dnet_ntoa (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_dnet_dnet_ntoa=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_dnet_dnet_ntoa=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_dnet_dnet_ntoa" >&5 echo "${ECHO_T}$ac_cv_lib_dnet_dnet_ntoa" >&6 if test $ac_cv_lib_dnet_dnet_ntoa = yes; then X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet" fi if test $ac_cv_lib_dnet_dnet_ntoa = no; then echo "$as_me:$LINENO: checking for dnet_ntoa in -ldnet_stub" >&5 echo $ECHO_N "checking for dnet_ntoa in -ldnet_stub... $ECHO_C" >&6 if test "${ac_cv_lib_dnet_stub_dnet_ntoa+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldnet_stub $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char dnet_ntoa (); int main () { dnet_ntoa (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_dnet_stub_dnet_ntoa=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_dnet_stub_dnet_ntoa=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5 echo "${ECHO_T}$ac_cv_lib_dnet_stub_dnet_ntoa" >&6 if test $ac_cv_lib_dnet_stub_dnet_ntoa = yes; then X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub" fi fi fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS="$ac_xsave_LIBS" # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT, # to get the SysV transport functions. # Chad R. Larson says the Pyramis MIS-ES running DC/OSx (SVR4) # needs -lnsl. # The nsl library prevents programs from opening the X display # on Irix 5.2, according to T.E. Dickey. # The functions gethostbyname, getservbyname, and inet_addr are # in -lbsd on LynxOS 3.0.1/i386, according to Lars Hecking. echo "$as_me:$LINENO: checking for gethostbyname" >&5 echo $ECHO_N "checking for gethostbyname... $ECHO_C" >&6 if test "${ac_cv_func_gethostbyname+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define gethostbyname to an innocuous variant, in case declares gethostbyname. For example, HP-UX 11i declares gettimeofday. */ #define gethostbyname innocuous_gethostbyname /* System header to define __stub macros and hopefully few prototypes, which can conflict with char gethostbyname (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef gethostbyname /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char gethostbyname (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_gethostbyname) || defined (__stub___gethostbyname) choke me #else char (*f) () = gethostbyname; #endif #ifdef __cplusplus } #endif int main () { return f != gethostbyname; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_gethostbyname=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_func_gethostbyname=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_func_gethostbyname" >&5 echo "${ECHO_T}$ac_cv_func_gethostbyname" >&6 if test $ac_cv_func_gethostbyname = no; then echo "$as_me:$LINENO: checking for gethostbyname in -lnsl" >&5 echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6 if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char gethostbyname (); int main () { gethostbyname (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_nsl_gethostbyname=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_nsl_gethostbyname=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_gethostbyname" >&5 echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6 if test $ac_cv_lib_nsl_gethostbyname = yes; then X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl" fi if test $ac_cv_lib_nsl_gethostbyname = no; then echo "$as_me:$LINENO: checking for gethostbyname in -lbsd" >&5 echo $ECHO_N "checking for gethostbyname in -lbsd... $ECHO_C" >&6 if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbsd $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char gethostbyname (); int main () { gethostbyname (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_bsd_gethostbyname=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_bsd_gethostbyname=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_bsd_gethostbyname" >&5 echo "${ECHO_T}$ac_cv_lib_bsd_gethostbyname" >&6 if test $ac_cv_lib_bsd_gethostbyname = yes; then X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd" fi fi fi # lieder@skyler.mavd.honeywell.com says without -lsocket, # socket/setsockopt and other routines are undefined under SCO ODT # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary # on later versions), says Simon Leinen: it contains gethostby* # variants that don't use the name server (or something). -lsocket # must be given before -lnsl if both are needed. We assume that # if connect needs -lnsl, so does gethostbyname. echo "$as_me:$LINENO: checking for connect" >&5 echo $ECHO_N "checking for connect... $ECHO_C" >&6 if test "${ac_cv_func_connect+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define connect to an innocuous variant, in case declares connect. For example, HP-UX 11i declares gettimeofday. */ #define connect innocuous_connect /* System header to define __stub macros and hopefully few prototypes, which can conflict with char connect (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef connect /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char connect (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_connect) || defined (__stub___connect) choke me #else char (*f) () = connect; #endif #ifdef __cplusplus } #endif int main () { return f != connect; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_connect=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_func_connect=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_func_connect" >&5 echo "${ECHO_T}$ac_cv_func_connect" >&6 if test $ac_cv_func_connect = no; then echo "$as_me:$LINENO: checking for connect in -lsocket" >&5 echo $ECHO_N "checking for connect in -lsocket... $ECHO_C" >&6 if test "${ac_cv_lib_socket_connect+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $X_EXTRA_LIBS $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char connect (); int main () { connect (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_socket_connect=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_socket_connect=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_socket_connect" >&5 echo "${ECHO_T}$ac_cv_lib_socket_connect" >&6 if test $ac_cv_lib_socket_connect = yes; then X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS" fi fi # Guillermo Gomez says -lposix is necessary on A/UX. echo "$as_me:$LINENO: checking for remove" >&5 echo $ECHO_N "checking for remove... $ECHO_C" >&6 if test "${ac_cv_func_remove+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define remove to an innocuous variant, in case declares remove. For example, HP-UX 11i declares gettimeofday. */ #define remove innocuous_remove /* System header to define __stub macros and hopefully few prototypes, which can conflict with char remove (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef remove /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char remove (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_remove) || defined (__stub___remove) choke me #else char (*f) () = remove; #endif #ifdef __cplusplus } #endif int main () { return f != remove; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_remove=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_func_remove=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_func_remove" >&5 echo "${ECHO_T}$ac_cv_func_remove" >&6 if test $ac_cv_func_remove = no; then echo "$as_me:$LINENO: checking for remove in -lposix" >&5 echo $ECHO_N "checking for remove in -lposix... $ECHO_C" >&6 if test "${ac_cv_lib_posix_remove+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lposix $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char remove (); int main () { remove (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_posix_remove=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_posix_remove=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_posix_remove" >&5 echo "${ECHO_T}$ac_cv_lib_posix_remove" >&6 if test $ac_cv_lib_posix_remove = yes; then X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix" fi fi # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay. echo "$as_me:$LINENO: checking for shmat" >&5 echo $ECHO_N "checking for shmat... $ECHO_C" >&6 if test "${ac_cv_func_shmat+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Define shmat to an innocuous variant, in case declares shmat. For example, HP-UX 11i declares gettimeofday. */ #define shmat innocuous_shmat /* System header to define __stub macros and hopefully few prototypes, which can conflict with char shmat (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef shmat /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" { #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char shmat (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_shmat) || defined (__stub___shmat) choke me #else char (*f) () = shmat; #endif #ifdef __cplusplus } #endif int main () { return f != shmat; ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_func_shmat=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_func_shmat=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi echo "$as_me:$LINENO: result: $ac_cv_func_shmat" >&5 echo "${ECHO_T}$ac_cv_func_shmat" >&6 if test $ac_cv_func_shmat = no; then echo "$as_me:$LINENO: checking for shmat in -lipc" >&5 echo $ECHO_N "checking for shmat in -lipc... $ECHO_C" >&6 if test "${ac_cv_lib_ipc_shmat+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lipc $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char shmat (); int main () { shmat (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_ipc_shmat=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_ipc_shmat=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_ipc_shmat" >&5 echo "${ECHO_T}$ac_cv_lib_ipc_shmat" >&6 if test $ac_cv_lib_ipc_shmat = yes; then X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc" fi fi fi # Check for libraries that X11R6 Xt/Xaw programs need. ac_save_LDFLAGS=$LDFLAGS test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries" # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to # check for ICE first), but we must link in the order -lSM -lICE or # we get undefined symbols. So assume we have SM if we have ICE. # These have to be linked with before -lX11, unlike the other # libraries we check for below, so use a different variable. # John Interrante, Karl Berry echo "$as_me:$LINENO: checking for IceConnectionNumber in -lICE" >&5 echo $ECHO_N "checking for IceConnectionNumber in -lICE... $ECHO_C" >&6 if test "${ac_cv_lib_ICE_IceConnectionNumber+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lICE $X_EXTRA_LIBS $LIBS" cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ /* Override any gcc2 internal prototype to avoid an error. */ #ifdef __cplusplus extern "C" #endif /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char IceConnectionNumber (); int main () { IceConnectionNumber (); ; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 (eval $ac_link) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest$ac_exeext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then ac_cv_lib_ICE_IceConnectionNumber=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_cv_lib_ICE_IceConnectionNumber=no fi rm -f conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi echo "$as_me:$LINENO: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5 echo "${ECHO_T}$ac_cv_lib_ICE_IceConnectionNumber" >&6 if test $ac_cv_lib_ICE_IceConnectionNumber = yes; then X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE" fi LDFLAGS=$ac_save_LDFLAGS fi ac_help="$ac_help --with-symbols add the -g flag to produce the debug symbols --with-use-malloc add the __USE_MALLOC flag to avoid the STL allocators --with-info define INFO at compile time to get basic log output --with-valgrind clean up allocated buffers to avoid valgrind warnings --with-version use this version for produced libraries --with-static-png enable static linking of PNG library --with-static-jpeg enable static linking of JPEG library --with-static-z enable static linking of Z library" echo "$as_me:$LINENO: checking for Cygwin32 environment" >&5 echo $ECHO_N "checking for Cygwin32 environment... $ECHO_C" >&6 if test "${nxconf_cv_cygwin32+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return __CYGWIN32__; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then nxconf_cv_cygwin32=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 nxconf_cv_cygwin32=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext rm -f conftest* fi echo "$as_me:$LINENO: result: $nxconf_cv_cygwin32" >&5 echo "${ECHO_T}$nxconf_cv_cygwin32" >&6 CYGWIN32= test "$nxconf_cv_cygwin32" = yes && CYGWIN32=yes echo "$as_me:$LINENO: checking for Amd64 environment" >&5 echo $ECHO_N "checking for Amd64 environment... $ECHO_C" >&6 if test "${nxconf_cv_amd64+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return (__amd64__ || __x86_64__); ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then nxconf_cv_amd64=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 nxconf_cv_amd64=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext rm -f conftest* fi echo "$as_me:$LINENO: result: $nxconf_cv_amd64" >&5 echo "${ECHO_T}$nxconf_cv_amd64" >&6 AMD64= test "$nxconf_cv_amd64" = yes && AMD64=yes echo "$as_me:$LINENO: checking for Darwin environment" >&5 echo $ECHO_N "checking for Darwin environment... $ECHO_C" >&6 if test "${nxconf_cv_darwin+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return __APPLE__; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then nxconf_cv_darwin=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 nxconf_cv_darwin=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext rm -f conftest* fi echo "$as_me:$LINENO: result: $nxconf_cv_darwin" >&5 echo "${ECHO_T}$nxconf_cv_darwin" >&6 DARWIN= test "$nxconf_cv_darwin" = yes && DARWIN=yes echo "$as_me:$LINENO: checking for Solaris environment" >&5 echo $ECHO_N "checking for Solaris environment... $ECHO_C" >&6 if test "${nxconf_cv_sun+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return __sun; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then nxconf_cv_sun=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 nxconf_cv_sun=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext rm -f conftest* fi echo "$as_me:$LINENO: result: $nxconf_cv_sun" >&5 echo "${ECHO_T}$nxconf_cv_sun" >&6 SUN= test "$nxconf_cv_sun" = yes && SUN=yes echo "$as_me:$LINENO: checking for FreeBSD environment" >&5 echo $ECHO_N "checking for FreeBSD environment... $ECHO_C" >&6 if test "${nxconf_cv_freebsd+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ int main () { return __FreeBSD__; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then nxconf_cv_freebsd=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 nxconf_cv_freebsd=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext rm -f conftest* fi echo "$as_me:$LINENO: result: $nxconf_cv_freebsd" >&5 echo "${ECHO_T}$nxconf_cv_freebsd" >&6 FreeBSD= test "$nxconf_cv_freebsd" = yes && FreeBSD=yes if test "$CYGWIN32" != yes -a "$DARWIN" != yes; then CXXFLAGS="$CXXFLAGS -fPIC" CFLAGS="$CFLAGS -fPIC" fi if test "$SUN" = yes; then LIBS="$LIBS -L/usr/sfw/lib -lsocket " CXXFLAGS="$CXXFLAGS -I/usr/sfw/include" CFLAGS="$CFLAGS -I/usr/sfw/include" fi if test "$FreeBSD" = yes; then LIBS="$LIBS -L/usr/local/lib" CXXFLAGS="$CXXFLAGS -I/usr/local/include" CFLAGS="$CFLAGS -I/usr/local/include" fi if test "$DARWIN" = yes; then LDFLAGS="$LDFLAGS -bundle" elif test "$SUN" = yes; then LDFLAGS="$LDFLAGS -G -h \$(LIBLOAD)" else LDFLAGS="$LDFLAGS -Wl,-soname,\$(LIBLOAD)" fi echo "$as_me:$LINENO: checking for in_addr_t" >&5 echo $ECHO_N "checking for in_addr_t... $ECHO_C" >&6 if test "${nxconf_cv_inaddrt+set}" = set; then echo $ECHO_N "(cached) $ECHO_C" >&6 else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include int main () { in_addr_t t; t = 1; return t; ; return 0; } _ACEOF rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_cxx_werror_flag" || test ! -s conftest.err' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; } && { ac_try='test -s conftest.$ac_objext' { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 (eval $ac_try) 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); }; }; then nxconf_cv_inaddrt=yes else echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 nxconf_cv_inaddrt=no fi rm -f conftest.err conftest.$ac_objext conftest.$ac_ext rm -f conftest* fi echo "$as_me:$LINENO: result: $nxconf_cv_inaddrt" >&5 echo "${ECHO_T}$nxconf_cv_inaddrt" >&6 INADDRT= test "$nxconf_cv_inaddrt" = yes && INADDRT=yes if test "$INADDRT" != yes ; then echo -e "using unsigned int for type in_addr_t" CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=unsigned" CFLAGS="$CFLAGS -DIN_ADDR_T=unsigned" else CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=in_addr_t" CFLAGS="$CFLAGS -DIN_ADDR_T=in_addr_t" fi if test "${with_version}" = yes; then VERSION=${ac_option} else VERSION=`cat VERSION` fi echo -e "compiling version ${VERSION}" LIBVERSION=`echo ${VERSION} | cut -d '.' -f 1` CXXFLAGS="$CXXFLAGS -DVERSION=\\\"${VERSION}\\\"" CFLAGS="$CFLAGS -DVERSION=\\\"${VERSION}\\\"" if test "${with_static_png}" = yes; then echo -e "enabling static linking of PNG library" if test "$SUN" = yes && test -f "/usr/sfw/lib/libpng.a"; then LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libpng.a" else if test -f "/usr/lib/libpng.a" ; then LIBSTATIC="$LIBSTATIC /usr/lib/libpng.a" else if test -f "/usr/local/lib/libpng.a" ; then echo -e "assuming libpng.a in /usr/local/lib" LIBSTATIC="$LIBSTATIC /usr/local/lib/libpng.a" else echo -e "Warning: assuming libpng.a in the local path" LIBSTATIC="$LIBSTATIC libpng.a" fi fi fi else echo -e "enabling dynamic linking of PNG library" LIBSHARED="$LIBSHARED -lpng" fi if test "${with_static_jpeg}" = yes; then echo -e "enabling static linking of JPEG library" if test "$SUN" = yes && test -f "/usr/sfw/lib/libjpeg.a"; then LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libjpeg.a" else if test -f "/usr/lib/libjpeg.a" ; then LIBSTATIC="$LIBSTATIC /usr/lib/libjpeg.a" else if test -f "/usr/local/lib/libjpeg.a" ; then echo -e "assuming libjpeg.a in /usr/local/lib" LIBSTATIC="$LIBSTATIC /usr/local/lib/libjpeg.a" else echo -e "Warning: assuming libjpeg.a in the local path" LIBSTATIC="$LIBSTATIC libjpeg.a" fi fi fi else echo -e "enabling dynamic linking of JPEG library" LIBSHARED="$LIBSHARED -ljpeg" fi if test "${with_static_z}" = yes; then echo -e "enabling static linking of Z library" if test "$SUN" = yes && test -f "/usr/sfw/lib/libz.a"; then LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libz.a" else if test -f "/usr/lib/libz.a" ; then LIBSTATIC="$LIBSTATIC /usr/lib/libz.a" else if test -f "/usr/local/lib/libz.a" ; then echo -e "assuming libz.a in /usr/local/lib" LIBSTATIC="$LIBSTATIC /usr/local/lib/libz.a" else echo -e "Warning: assuming libz.a in the local path" LIBSTATIC="$LIBSTATIC libz.a" fi fi fi else echo -e "enabling dynamic linking of Z library" LIBSHARED="$LIBSHARED -lz" fi if test "$DARWIN" = yes ; then LIBS="$LIBS $LIBSTATIC $LIBSHARED" elif test "$SUN" = yes ; then LIBS="$LIBS $LIBSTATIC $LIBSHARED" else LIBS="$LIBS $LIBSTATIC -shared $LIBSHARED" fi if test "${with_symbols}" = yes; then echo -e "enabling production of debug symbols" CXXFLAGS="-g $CXXFLAGS" CFLAGS="-g $CFLAGS" else echo -e "disabling production of debug symbols" fi if test "${with_use_malloc}" = yes; then echo -e "disabling use of the STL allocators" CXXFLAGS="$CXXFLAGS -D__USE_MALLOC" else echo -e "enabling use of the STL allocators" fi if test "${with_info}" = yes; then echo -e "enabling info output in the log file" CXXFLAGS="$CXXFLAGS -DINFO" CFLAGS="$CFLAGS -DINFO" else echo -e "disabling info output in the log file" fi if test "${with_valgrind}" = yes; then echo -e "enabling valgrind memory checker workarounds" CXXFLAGS="$CXXFLAGS -DVALGRIND" CFLAGS="$CFLAGS -DVALGRIND" else echo -e "disabling valgrind memory checker workarounds" fi if test -x "../nx-X11/config/makedepend/makedepend" ; then MAKEDEPEND=../nx-X11/config/makedepend/makedepend else if test -x "/usr/X11R6/bin/makedepend" ; then MAKEDEPEND=/usr/X11R6/bin/makedepend else if test -x "/usr/openwin/bin/makedepend" ; then MAKEDEPEND=/usr/openwin/bin/makedepend else MAKEDEPEND=/usr/bin/makedepend fi fi fi if test "$CYGWIN32" = yes; then ALL="\$(LIBCYGARCHIVE) \$(LIBCYGSHARED) \$(LIBARCHIVE)" LIBS="-lstdc++ -lpng -ljpeg -lz" else ALL="\$(LIBFULL) \$(LIBLOAD) \$(LIBSHARED) \$(LIBARCHIVE)" fi ac_config_files="$ac_config_files Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. { (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote # substitution turns \\\\ into \\, and sed turns \\ into \). sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n \ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" ;; esac; } | sed ' t clear : clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ : end' >>confcache if diff $cache_file confcache >/dev/null 2>&1; then :; else if test -w $cache_file; then test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" cat confcache >$cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # VPATH may cause trouble with some makes, so we remove $(srcdir), # ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=/{ s/:*\$(srcdir):*/:/; s/:*\${srcdir}:*/:/; s/:*@srcdir@:*/:/; s/^\([^=]*=[ ]*\):*/\1/; s/:*$//; s/^[^=]*=[ ]*$//; }' fi # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. # # If the first sed substitution is executed (which looks for macros that # take arguments), then we branch to the quote section. Otherwise, # look for a macro that doesn't take arguments. cat >confdef2opt.sed <<\_ACEOF t clear : clear s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g t quote s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g t quote d : quote s,[ `~#$^&*(){}\\|;'"<>?],\\&,g s,\[,\\&,g s,\],\\&,g s,\$,$$,g p _ACEOF # We use echo to avoid assuming a particular line-breaking character. # The extra dot is to prevent the shell from consuming trailing # line-breaks from the sub-command output. A line-break within # single-quotes doesn't work because, if this script is created in a # platform that uses two characters for line-breaks (e.g., DOS), tr # would break. ac_LF_and_DOT=`echo; echo .` DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` rm -f confdef2opt.sed ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_i=`echo "$ac_i" | sed 's/\$U\././;s/\.o$//;s/\.obj$//'` # 2. Add them. ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : ${CONFIG_STATUS=./config.status} ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 echo "$as_me: creating $CONFIG_STATUS" >&6;} cat >$CONFIG_STATUS <<_ACEOF #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF ## --------------------- ## ## M4sh Initialization. ## ## --------------------- ## # Be Bourne compatible if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then set -o posix fi DUALCASE=1; export DUALCASE # for MKS sh # Support unset when possible. if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then as_unset=unset else as_unset=false fi # Work around bugs in pre-3.0 UWIN ksh. $as_unset ENV MAIL MAILPATH PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done # Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi # Name of the executable. as_me=`$as_basename "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)$' \| \ . : '\(.\)' 2>/dev/null || echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } /^X\/\(\/\/\)$/{ s//\1/; q; } /^X\/\(\/\).*/{ s//\1/; q; } s/.*/./; q'` # PATH needs CR, and LINENO needs CR and PATH. # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" || { # Find who we are. Look in the path if we contain no path at all # relative or not. case $0 in *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} { (exit 1); exit 1; }; } fi case $CONFIG_SHELL in '') as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for as_base in sh bash ksh sh5; do case $as_dir in /*) if ("$as_dir/$as_base" -c ' as_lineno_1=$LINENO as_lineno_2=$LINENO as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` test "x$as_lineno_1" != "x$as_lineno_2" && test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } CONFIG_SHELL=$as_dir/$as_base export CONFIG_SHELL exec "$CONFIG_SHELL" "$0" ${1+"$@"} fi;; esac done done ;; esac # Create $as_me.lineno as a copy of $as_myself, but with $LINENO # uniformly replaced by the line number. The first 'sed' inserts a # line-number line before each line; the second 'sed' does the real # work. The second script uses 'N' to pair each line-number line # with the numbered line, and appends trailing '-' during # substitution so that $LINENO is not a special case at line end. # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) sed '=' <$as_myself | sed ' N s,$,-, : loop s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, t loop s,-$,, s,^['$as_cr_digits']*\n,, ' >$as_me.lineno && chmod +x $as_me.lineno || { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} { (exit 1); exit 1; }; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensible to this). . ./$as_me.lineno # Exit status is that of the last command. exit } case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in *c*,-n*) ECHO_N= ECHO_C=' ' ECHO_T=' ' ;; *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; *) ECHO_N= ECHO_C='\c' ECHO_T= ;; esac if expr a : '\(a\)' >/dev/null 2>&1; then as_expr=expr else as_expr=false fi rm -f conf$$ conf$$.exe conf$$.file echo >conf$$.file if ln -s conf$$.file conf$$ 2>/dev/null; then # We could just check for DJGPP; but this test a) works b) is more generic # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). if test -f conf$$.exe; then # Don't use ln at all; we don't have any links as_ln_s='cp -p' else as_ln_s='ln -s' fi elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.file if mkdir -p . 2>/dev/null; then as_mkdir_p=: else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_executable_p="test -f" # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" # IFS # We need space, tab and new line, in precisely that order. as_nl=' ' IFS=" $as_nl" # CDPATH. $as_unset CDPATH exec 6>&1 # Open the log real soon, to keep \$[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. Logging --version etc. is OK. exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX } >&5 cat >&5 <<_CSEOF This file was extended by $as_me, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ _CSEOF echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 echo >&5 _ACEOF # Files that config.status was made for. if test -n "$ac_config_files"; then echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS fi if test -n "$ac_config_headers"; then echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS fi if test -n "$ac_config_links"; then echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS fi if test -n "$ac_config_commands"; then echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS fi cat >>$CONFIG_STATUS <<\_ACEOF ac_cs_usage="\ \`$as_me' instantiates files from templates according to the current configuration. Usage: $0 [OPTIONS] [FILE]... -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE Configuration files: $config_files Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ config.status configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2003 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." srcdir=$srcdir INSTALL="$INSTALL" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # If no file are specified by the user, then we need to provide default # value. By we need to know if files were specified by the user. ac_need_defaults=: while test $# != 0 do case $1 in --*=*) ac_option=`expr "x$1" : 'x\([^=]*\)='` ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` ac_shift=: ;; -*) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; *) # This is not an option, so the user has probably given explicit # arguments. ac_option=$1 ac_need_defaults=false;; esac case $ac_option in # Handling of the options. _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --vers* | -V ) echo "$ac_cs_version"; exit 0 ;; --he | --h) # Conflict between --help and --header { { echo "$as_me:$LINENO: error: ambiguous option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: ambiguous option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; };; --help | --hel | -h ) echo "$ac_cs_usage"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift CONFIG_FILES="$CONFIG_FILES $ac_optarg" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" ac_need_defaults=false;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 Try \`$0 --help' for more information." >&5 echo "$as_me: error: unrecognized option: $1 Try \`$0 --help' for more information." >&2;} { (exit 1); exit 1; }; } ;; *) ac_config_targets="$ac_config_targets $1" ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF if \$ac_cs_recheck; then echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_config_target in $ac_config_targets do case "$ac_config_target" in # Handling of arguments. "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 echo "$as_me: error: invalid argument: $ac_config_target" >&2;} { (exit 1); exit 1; }; };; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason to put it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Create a temporary directory, and hook for its removal unless debugging. $debug || { trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 trap '{ (exit 1); exit 1; }' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" } || { tmp=./confstat$$-$RANDOM (umask 077 && mkdir $tmp) } || { echo "$me: cannot create a temporary directory in ." >&2 { (exit 1); exit 1; } } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF # # CONFIG_FILES section. # # No need to generate the scripts if there are no CONFIG_FILES. # This happens for instance when ./config.status config.h if test -n "\$CONFIG_FILES"; then # Protect against being on the right side of a sed subst in config.status. sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF s,@SHELL@,$SHELL,;t t s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t s,@exec_prefix@,$exec_prefix,;t t s,@prefix@,$prefix,;t t s,@program_transform_name@,$program_transform_name,;t t s,@bindir@,$bindir,;t t s,@sbindir@,$sbindir,;t t s,@libexecdir@,$libexecdir,;t t s,@datadir@,$datadir,;t t s,@sysconfdir@,$sysconfdir,;t t s,@sharedstatedir@,$sharedstatedir,;t t s,@localstatedir@,$localstatedir,;t t s,@libdir@,$libdir,;t t s,@includedir@,$includedir,;t t s,@oldincludedir@,$oldincludedir,;t t s,@infodir@,$infodir,;t t s,@mandir@,$mandir,;t t s,@build_alias@,$build_alias,;t t s,@host_alias@,$host_alias,;t t s,@target_alias@,$target_alias,;t t s,@DEFS@,$DEFS,;t t s,@ECHO_C@,$ECHO_C,;t t s,@ECHO_N@,$ECHO_N,;t t s,@ECHO_T@,$ECHO_T,;t t s,@LIBS@,$LIBS,;t t s,@armcxx@,$armcxx,;t t s,@armcc@,$armcc,;t t s,@CXX@,$CXX,;t t s,@CXXFLAGS@,$CXXFLAGS,;t t s,@LDFLAGS@,$LDFLAGS,;t t s,@CPPFLAGS@,$CPPFLAGS,;t t s,@ac_ct_CXX@,$ac_ct_CXX,;t t s,@EXEEXT@,$EXEEXT,;t t s,@OBJEXT@,$OBJEXT,;t t s,@CC@,$CC,;t t s,@CFLAGS@,$CFLAGS,;t t s,@ac_ct_CC@,$ac_ct_CC,;t t s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t s,@INSTALL_DATA@,$INSTALL_DATA,;t t s,@CXXCPP@,$CXXCPP,;t t s,@X_CFLAGS@,$X_CFLAGS,;t t s,@X_PRE_LIBS@,$X_PRE_LIBS,;t t s,@X_LIBS@,$X_LIBS,;t t s,@X_EXTRA_LIBS@,$X_EXTRA_LIBS,;t t s,@LIBVERSION@,$LIBVERSION,;t t s,@VERSION@,$VERSION,;t t s,@MAKEDEPEND@,$MAKEDEPEND,;t t s,@ALL@,$ALL,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t CEOF _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_lines=48 ac_sed_frag=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_lines # Line after last line for current file. ac_more_lines=: ac_sed_cmds= while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag else sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag fi if test ! -s $tmp/subs.frag; then ac_more_lines=false else # The purpose of the label and of the branching condition is to # speed up the sed processing (if there are no `@' at all, there # is no need to browse any of the substitutions). # These are the two extra sed commands mentioned above. (echo ':t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" else ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" fi ac_sed_frag=`expr $ac_sed_frag + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_lines` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi fi # test -n "$CONFIG_FILES" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case $ac_file in - | *:- | *:-:* ) # input from stdin cat >$tmp/stdin ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; * ) ac_file_in=$ac_file.in ;; esac # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. ac_dir=`(dirname "$ac_file") 2>/dev/null || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` { if $as_mkdir_p; then mkdir -p "$ac_dir" else as_dir="$ac_dir" as_dirs= while test ! -d "$as_dir"; do as_dirs="$as_dir $as_dirs" as_dir=`(dirname "$as_dir") 2>/dev/null || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| \ . : '\(.\)' 2>/dev/null || echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } /^X\(\/\/\)[^/].*/{ s//\1/; q; } /^X\(\/\/\)$/{ s//\1/; q; } /^X\(\/\).*/{ s//\1/; q; } s/.*/./; q'` done test ! -n "$as_dirs" || mkdir $as_dirs fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} { (exit 1); exit 1; }; }; } ac_builddir=. if test "$ac_dir" != .; then ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` # A "../" for each directory in $ac_dir_suffix. ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` else ac_dir_suffix= ac_top_builddir= fi case $srcdir in .) # No --srcdir option. We are building in place. ac_srcdir=. if test -z "$ac_top_builddir"; then ac_top_srcdir=. else ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` fi ;; [\\/]* | ?:[\\/]* ) # Absolute path. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ;; *) # Relative path. ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_builddir$srcdir ;; esac # Do not use `cd foo && pwd` to compute absolute paths, because # the directories may not exist. case `pwd` in .) ac_abs_builddir="$ac_dir";; *) case "$ac_dir" in .) ac_abs_builddir=`pwd`;; [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; *) ac_abs_builddir=`pwd`/"$ac_dir";; esac;; esac case $ac_abs_builddir in .) ac_abs_top_builddir=${ac_top_builddir}.;; *) case ${ac_top_builddir}. in .) ac_abs_top_builddir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; esac;; esac case $ac_abs_builddir in .) ac_abs_srcdir=$ac_srcdir;; *) case $ac_srcdir in .) ac_abs_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; esac;; esac case $ac_abs_builddir in .) ac_abs_top_srcdir=$ac_top_srcdir;; *) case $ac_top_srcdir in .) ac_abs_top_srcdir=$ac_abs_builddir;; [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; esac;; esac case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_builddir$INSTALL ;; esac if test x"$ac_file" != x-; then { echo "$as_me:$LINENO: creating $ac_file" >&5 echo "$as_me: creating $ac_file" >&6;} rm -f "$ac_file" fi # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ if test x"$ac_file" = x-; then configure_input= else configure_input="$ac_file. " fi configure_input=$configure_input"Generated from `echo $ac_file_in | sed 's,.*/,,'` by configure." # First look for the input files in the build tree, otherwise in the # src tree. ac_file_inputs=`IFS=: for f in $ac_file_in; do case $f in -) echo $tmp/stdin ;; [\\/$]*) # Absolute (can't be DOS-style, as IFS=:) test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } echo "$f";; *) # Relative if test -f "$f"; then # Build tree echo "$f" elif test -f "$srcdir/$f"; then # Source tree echo "$srcdir/$f" else # /dev/null tree { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 echo "$as_me: error: cannot find input file: $f" >&2;} { (exit 1); exit 1; }; } fi;; esac done` || { (exit 1); exit 1; } _ACEOF cat >>$CONFIG_STATUS <<_ACEOF sed "$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s,@configure_input@,$configure_input,;t t s,@srcdir@,$ac_srcdir,;t t s,@abs_srcdir@,$ac_abs_srcdir,;t t s,@top_srcdir@,$ac_top_srcdir,;t t s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t s,@builddir@,$ac_builddir,;t t s,@abs_builddir@,$ac_abs_builddir,;t t s,@top_builddir@,$ac_top_builddir,;t t s,@abs_top_builddir@,$ac_abs_top_builddir,;t t s,@INSTALL@,$ac_INSTALL,;t t " $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out rm -f $tmp/stdin if test x"$ac_file" != x-; then mv $tmp/out $ac_file else cat $tmp/out rm -f $tmp/out fi done _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF { (exit 0); exit 0; } _ACEOF chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || { (exit 1); exit 1; } fi nxcomp/Keeper.cpp0000644000076400007640000003125111323113030014236 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include #include "Keeper.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Remove the directory if it's empty // since more than 30 * 24 h. // #define EMPTY_DIR_TIME 2592000 // // Sleep once any ONCE entries. // #define ONCE 2 // // Define this to trace messages while // they are allocated and deallocated. // #undef REFERENCES // // This is used for reference count. // #ifdef REFERENCES int File::references_ = 0; #endif bool T_older::operator () (File *a, File *b) const { return a -> compare(b); } File::File() { name_ = NULL; size_ = 0; time_ = 0; #ifdef REFERENCES references_++; *logofs << "Keeper: Created new File at " << this << " out of " << references_ << " allocated references.\n" << logofs_flush; #endif } // // TODO: This class can leak industrial amounts of memory. // I'm 100% sure that the desctructor is called and that // also the string pointed in the File structure is dele- // ted. Everything is logged, but still the memory is not // freed. This is less a problem on Windows, where the me- // mory occupation remains almost constant. Obviously the // problem lies in the STL allocators of the GNU libstc++. // File::~File() { #ifdef TEST *logofs << "Keeper: Deleting name for File at " << this << ".\n" << logofs_flush; #endif delete [] name_; #ifdef REFERENCES *logofs << "Keeper: Deleted File at " << this << " out of " << references_ << " allocated references.\n" << logofs_flush; references_--; #endif } bool File::compare(File *b) const { if (this -> time_ == b -> time_) { return (this -> size_ < b -> size_); } return (this -> time_ < b -> time_); } Keeper::Keeper(int caches, int images, const char *root, int sleep, int parent) { caches_ = caches; images_ = images; sleep_ = sleep; parent_ = parent; root_ = new char[strlen(root) + 1]; strcpy(root_, root); total_ = 0; signal_ = 0; files_ = new T_files; } Keeper::~Keeper() { empty(); delete files_; delete [] root_; } int Keeper::cleanupCaches() { #ifdef TEST *logofs << "Keeper: Looking for cache directories in '" << root_ << "'.\n" << logofs_flush; #endif DIR *rootDir = opendir(root_); if (rootDir != NULL) { dirent *dirEntry; struct stat fileStat; int baseSize = strlen(root_); int s = 0; while (((dirEntry = readdir(rootDir)) != NULL)) { if (s++ % ONCE == 0) usleep(sleep_ * 1000); if (signal_ != 0) break; if (strcmp(dirEntry -> d_name, "cache") == 0 || strncmp(dirEntry -> d_name, "cache-", 6) == 0) { char *dirName = new char[baseSize + strlen(dirEntry -> d_name) + 2]; if (dirName == NULL) { #ifdef WARNING *logofs << "Keeper: WARNING! Can't check directory entry '" << dirEntry -> d_name << "'.\n" << logofs_flush; #endif delete [] dirName; continue; } strcpy(dirName, root_); strcpy(dirName + baseSize, "/"); strcpy(dirName + baseSize + 1, dirEntry -> d_name); #ifdef TEST *logofs << "Keeper: Checking directory '" << dirName << "'.\n" << logofs_flush; #endif if (stat(dirName, &fileStat) == 0 && S_ISDIR(fileStat.st_mode) != 0) { // // Add to repository all the "C-" and // "S-" files in the given directory. // collect(dirName); } delete [] dirName; } } closedir(rootDir); } else { #ifdef WARNING *logofs << "Keeper: WARNING! Can't open NX root directory '" << root_ << "'. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Can't open NX root directory '" << root_ << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"; } // // Remove older files. // cleanup(caches_); // // Empty the repository. // empty(); return 1; } int Keeper::cleanupImages() { #ifdef TEST *logofs << "Keeper: Looking for image directory in '" << root_ << "'.\n" << logofs_flush; #endif char *imagesPath = new char[strlen(root_) + strlen("/images") + 1]; if (imagesPath == NULL) { return -1; } strcpy(imagesPath, root_); strcat(imagesPath, "/images"); // // Check if the cache directory does exist. // struct stat dirStat; if (stat(imagesPath, &dirStat) == -1) { #ifdef WARNING *logofs << "Keeper: WARNING! Can't stat NX images cache directory '" << imagesPath << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Can't stat NX images cache directory '" << imagesPath << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; delete [] imagesPath; return -1; } // // Check any of the 16 directories in the // images root path. // char *digitPath = new char[strlen(imagesPath) + 5]; strcpy(digitPath, imagesPath); for (char digit = 0; digit < 16; digit++) { // // Give up if we received a signal or // our parent is gone. // if (signal_ != 0) { #ifdef TEST *logofs << "Keeper: Signal detected. Aborting.\n" << logofs_flush; #endif goto KeeperCleanupImagesAbort; } else if (parent_ != getppid() || parent_ == 1) { #ifdef WARNING *logofs << "Keeper: WARNING! Parent process appears " << "to be dead. Returning.\n" << logofs_flush; #endif goto KeeperCleanupImagesAbort; return 0; } sprintf(digitPath + strlen(imagesPath), "/I-%01X", digit); // // Add to the repository all the files // in the given directory. // collect(digitPath); } delete [] imagesPath; delete [] digitPath; // // Remove the oldest files. // cleanup(images_); // // Empty the repository. // empty(); return 1; KeeperCleanupImagesAbort: delete [] imagesPath; delete [] digitPath; empty(); return 0; } int Keeper::collect(const char *path) { #ifdef TEST *logofs << "Keeper: Looking for files in directory '" << path << "'.\n" << logofs_flush; #endif DIR *cacheDir = opendir(path); if (cacheDir != NULL) { File *file; dirent *dirEntry; struct stat fileStat; int baseSize = strlen(path); int fileSize = baseSize + 3 + MD5_LENGTH * 2 + 1; int n = 0; int s = 0; while (((dirEntry = readdir(cacheDir)) != NULL)) { if (s++ % ONCE == 0) usleep(sleep_ * 1000); if (signal_ != 0) break; if (strcmp(dirEntry -> d_name, ".") == 0 || strcmp(dirEntry -> d_name, "..") == 0) { continue; } n++; if (strlen(dirEntry -> d_name) == (MD5_LENGTH * 2 + 2) && (strncmp(dirEntry -> d_name, "I-", 2) == 0 || strncmp(dirEntry -> d_name, "S-", 2) == 0 || strncmp(dirEntry -> d_name, "C-", 2) == 0)) { file = new File(); char *fileName = new char[fileSize]; if (file == NULL || fileName == NULL) { #ifdef WARNING *logofs << "Keeper: WARNING! Can't add file '" << dirEntry -> d_name << "' to repository.\n" << logofs_flush; #endif delete [] fileName; delete file; continue; } strcpy(fileName, path); strcpy(fileName + baseSize, "/"); strcpy(fileName + baseSize + 1, dirEntry -> d_name); file -> name_ = fileName; #ifdef DEBUG *logofs << "Keeper: Adding file '" << file -> name_ << "'.\n" << logofs_flush; #endif if (stat(file -> name_, &fileStat) == -1) { #ifdef WARNING *logofs << "Keeper: WARNING! Can't stat NX file '" << file -> name_ << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif delete file; continue; } file -> size_ = fileStat.st_size; file -> time_ = fileStat.st_mtime; files_ -> insert(T_files::value_type(file)); total_ += file -> size_; } } closedir(cacheDir); if (n == 0) { time_t now = time(NULL); if (now > 0 && stat(path, &fileStat) == 0) { #ifdef TEST *logofs << "Keeper: Empty NX subdirectory '" << path << "' unused since " << now - fileStat.st_mtime << " S.\n" << logofs_flush; #endif if (now - fileStat.st_mtime > EMPTY_DIR_TIME) { #ifdef TEST *logofs << "Keeper: Removing empty NX subdirectory '" << path << "'.\n" << logofs_flush; #endif rmdir(path); } } } } else { #ifdef WARNING *logofs << "Keeper: WARNING! Can't open NX subdirectory '" << path << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Can't open NX subdirectory '" << path << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; } return 1; } int Keeper::cleanup(int threshold) { #ifdef TEST *logofs << "Keeper: Deleting the oldest files with " << files_ -> size() << " elements threshold " << threshold << " and size " << total_ << ".\n" << logofs_flush; #endif // // At least some versions of the standard // library don't allow erasing an element // while looping. This is not the most ef- // ficient way to release the objects, but // it's better than making a copy of the // container. // while (total_ > threshold && files_ -> size() > 0) { T_files::iterator i = files_ -> begin(); File *file = *i; #ifdef TEST *logofs << "Keeper: Removing '" << file -> name_ << "' with time " << file -> time_ << " and size " << file -> size_ << ".\n" << logofs_flush; #endif unlink(file -> name_); total_ -= file -> size_; #ifdef DEBUG *logofs << "Keeper: Going to delete the file at " << file << " while cleaning up.\n" << logofs_flush; #endif delete file; #ifdef DEBUG *logofs << "Keeper: Going to erase the element " << "while cleaning up.\n" << logofs_flush; #endif files_ -> erase(i); } #ifdef TEST *logofs << "Keeper: Bytes in repository are " << total_ << ".\n" << logofs_flush; #endif return 1; } void Keeper::empty() { #ifdef TEST *logofs << "Keeper: Getting rid of files in repository.\n" << logofs_flush; #endif while (files_ -> size() > 0) { T_files::iterator i = files_ -> begin(); File *file = *i; #ifdef DEBUG *logofs << "Keeper: Going to delete the file at " << file << " while emptying.\n" << logofs_flush; #endif delete file; #ifdef DEBUG *logofs << "Keeper: Going to erase the element " << "while emptying.\n" << logofs_flush; #endif files_ -> erase(i); } total_ = 0; #ifdef TEST *logofs << "Keeper: Bytes in repository are " << total_ << ".\n" << logofs_flush; #endif } nxcomp/RenderCreatePictureCompat.cpp0000644000076400007640000002036111323113031020067 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderCreatePictureCompat.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian), clientCache -> renderLastId, 29, clientCache -> renderIdCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> drawableCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 12, bigEndian), 32, clientCache -> renderFormatCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, clientCache -> renderValueMaskCache); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastId, 29, clientCache -> renderIdCache); PutULONG(value, buffer + 4, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeCachedValue(value, 32, clientCache -> renderFormatCache); PutULONG(value, buffer + 12, bigEndian); decodeBuffer.decodeCachedValue(value, 32, clientCache -> renderValueMaskCache); PutULONG(value, buffer + 16, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.create_picture.type = *(buffer + 1); renderExtension -> data.create_picture.src_id = GetULONG(buffer + 4, bigEndian); renderExtension -> data.create_picture.dst_id = GetULONG(buffer + 8, bigEndian); renderExtension -> data.create_picture.format = GetULONG(buffer + 12, bigEndian); renderExtension -> data.create_picture.mask = GetULONG(buffer + 16, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.create_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.create_picture.type; PutULONG(renderExtension -> data.create_picture.src_id, buffer + 4, bigEndian); PutULONG(renderExtension -> data.create_picture.dst_id, buffer + 8, bigEndian); PutULONG(renderExtension -> data.create_picture.format, buffer + 12, bigEndian); PutULONG(renderExtension -> data.create_picture.mask, buffer + 16, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.create_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); md5_append(md5_state, buffer + 12, 8); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding new id value " << renderExtension -> data.create_picture.src_id - clientCache -> renderLastId << ".\n"; #endif encodeBuffer.encodeDiffCachedValue(renderExtension -> data.create_picture.src_id, clientCache -> renderLastId, 29, clientCache -> renderIdCache); cachedRenderExtension -> data.create_picture.src_id = renderExtension -> data.create_picture.src_id; encodeBuffer.encodeXidValue(renderExtension -> data.create_picture.dst_id, clientCache -> drawableCache); cachedRenderExtension -> data.create_picture.dst_id = renderExtension -> data.create_picture.dst_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.create_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeDiffCachedValue(renderExtension -> data.create_picture.src_id, clientCache -> renderLastId, 29, clientCache -> renderIdCache); decodeBuffer.decodeXidValue(renderExtension -> data.create_picture.dst_id, clientCache -> drawableCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.create_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/ListFontsReply.h0000644000076400007640000000730611323113027015443 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ListFontsReply_H #define ListFontsReply_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define LISTFONTSREPLY_ENABLE_CACHE 1 #define LISTFONTSREPLY_ENABLE_DATA 1 #define LISTFONTSREPLY_ENABLE_SPLIT 0 #define LISTFONTSREPLY_ENABLE_COMPRESS 1 #define LISTFONTSREPLY_DATA_LIMIT 1048576 - 32 #define LISTFONTSREPLY_DATA_OFFSET 32 #define LISTFONTSREPLY_CACHE_SLOTS 200 #define LISTFONTSREPLY_CACHE_THRESHOLD 20 #define LISTFONTSREPLY_CACHE_LOWER_THRESHOLD 5 #define LISTFONTSREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 // // The message class. // class ListFontsReplyMessage : public Message { friend class ListFontsReplyStore; public: ListFontsReplyMessage() { } ~ListFontsReplyMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned short int number_of_names; }; class ListFontsReplyStore : public MessageStore { // // Constructors and destructors. // public: ListFontsReplyStore(StaticCompressor *compressor); virtual ~ListFontsReplyStore(); virtual const char *name() const { return "ListFontsReply"; } virtual unsigned char opcode() const { return X_ListFonts; } virtual unsigned int storage() const { return sizeof(ListFontsReplyMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new ListFontsReplyMessage(); } virtual Message *create(const Message &message) const { return new ListFontsReplyMessage((const ListFontsReplyMessage &) message); } virtual void destroy(Message *message) const { delete (ListFontsReplyMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* ListFontsReply_H */ nxcomp/TextCompressor.h0000644000076400007640000000337011323113030015472 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef TextCompressor_H #define TextCompressor_H #include "CharCache.h" class EncodeBuffer; class DecodeBuffer; class TextCompressor { public: TextCompressor(CharCache* cache, unsigned int cacheSize): cache_(cache), cacheSize_(cacheSize), key_(0) { } void encodeChar(unsigned char ch, EncodeBuffer &); unsigned char decodeChar(DecodeBuffer &); void reset(unsigned int newKey = 0) { key_ = newKey; } private: CharCache* cache_; unsigned int cacheSize_; unsigned int key_; }; #endif /* TextCompressor_H */ nxcomp/SendEvent.h0000644000076400007640000001136111323113030014363 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef SendEvent_H #define SendEvent_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define SENDEVENT_ENABLE_CACHE 1 #define SENDEVENT_ENABLE_DATA 0 #define SENDEVENT_ENABLE_SPLIT 0 #define SENDEVENT_ENABLE_COMPRESS 0 #define SENDEVENT_DATA_LIMIT 24 #define SENDEVENT_DATA_OFFSET 20 #define SENDEVENT_CACHE_SLOTS 2000 #define SENDEVENT_CACHE_THRESHOLD 2 #define SENDEVENT_CACHE_LOWER_THRESHOLD 1 // // The message class. // class SendEventMessage : public Message { friend class SendEventStore; public: SendEventMessage() { } ~SendEventMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char propagate; unsigned int window; unsigned int mask; // // These are part of the event data. // unsigned char code; unsigned char byte_data; unsigned short sequence; unsigned int int_data; }; class SendEventStore : public MessageStore { // // Constructors and destructors. // public: SendEventStore() : MessageStore() { enableCache = SENDEVENT_ENABLE_CACHE; enableData = SENDEVENT_ENABLE_DATA; enableSplit = SENDEVENT_ENABLE_SPLIT; enableCompress = SENDEVENT_ENABLE_COMPRESS; dataLimit = SENDEVENT_DATA_LIMIT; dataOffset = SENDEVENT_DATA_OFFSET; cacheSlots = SENDEVENT_CACHE_SLOTS; cacheThreshold = SENDEVENT_CACHE_THRESHOLD; cacheLowerThreshold = SENDEVENT_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~SendEventStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "SendEvent"; } virtual unsigned char opcode() const { return X_SendEvent; } virtual unsigned int storage() const { return sizeof(SendEventMessage); } // // Message handling methods. // public: virtual Message *create() const { return new SendEventMessage(); } virtual Message *create(const Message &message) const { return new SendEventMessage((const SendEventMessage &) message); } virtual void destroy(Message *message) const { delete (SendEventMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* SendEvent_H */ nxcomp/mkinstalldirs0000755000076400007640000000127611323113027015137 0ustar svetonisvetoni#! /bin/sh # mkinstalldirs --- make directory hierarchy # Author: Noah Friedman # Created: 1993-05-16 # Last modified: 1995-03-05 # Public domain errstatus=0 for file in ${1+"$@"} ; do set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` shift pathcomp= for d in ${1+"$@"} ; do pathcomp="$pathcomp$d" case "$pathcomp" in -* ) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" 1>&2 mkdir "$pathcomp" > /dev/null 2>&1 || lasterr=$? fi if test ! -d "$pathcomp"; then errstatus=$lasterr fi pathcomp="$pathcomp/" done done exit $errstatus nxcomp/ServerStore.h0000644000076400007640000000526011323113030014754 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ServerStore_H #define ServerStore_H #include "Message.h" #include "ChannelStore.h" class StaticCompressor; class ServerStore : public ChannelStore { public: ServerStore(StaticCompressor *compressor); virtual ~ServerStore(); MessageStore *getReplyStore(unsigned char opcode) const { return replies_[opcode]; } MessageStore *getEventStore(unsigned char opcode) const { return events_[opcode]; } // // Actually save the message store // to disk according to proxy mode. // int saveReplyStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient, T_checksum_action checksumAction, T_data_action dataAction) const; int saveEventStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient, T_checksum_action checksumAction, T_data_action dataAction) const; int loadReplyStores(istream *cachefs, md5_state_t *md5StateStream, T_checksum_action checksumAction, T_data_action dataAction) const; int loadEventStores(istream *cachefs, md5_state_t *md5StateStream, T_checksum_action checksumAction, T_data_action dataAction) const; private: // // A server store contains replies and events. // MessageStore *replies_[CHANNEL_STORE_OPCODE_LIMIT]; MessageStore *events_[CHANNEL_STORE_OPCODE_LIMIT]; }; #endif /* ServerStore_H */ nxcomp/RenderCompositeCompat.cpp0000644000076400007640000002402411323113027017277 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderCompositeCompat.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { // // Strictly speaking this request doesn't have // a data part. We just encode the field from // offset 24 to 36 as they were data using an // int cache. // #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { size = MESSAGE_OFFSET + 12; buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 16, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian), clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian), clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 12, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 16, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { ClientCache *clientCache = (ClientCache *) channelCache; for (unsigned int i = MESSAGE_OFFSET, c = 0; i < size; i += 4) { #ifdef DEBUG *logofs << name() << ": Encoding long value " << GetULONG(buffer + i, bigEndian) << " with i = " << i << " c = " << c << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(GetULONG(buffer + i, bigEndian), 32, *clientCache -> renderCompositeDataCache[c]); if (++c == 3) c = 0; } #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; for (unsigned int i = MESSAGE_OFFSET, c = 0; i < size; i += 4) { decodeBuffer.decodeCachedValue(value, 32, *clientCache -> renderCompositeDataCache[c]); #ifdef DEBUG *logofs << name() << ": Decoded long value " << value << " with i = " << i << " c = " << c << ".\n" << logofs_flush; #endif PutULONG(value, buffer + i, bigEndian); if (++c == 3) c = 0; } #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.composite.type = *(buffer + 1); renderExtension -> data.composite.op = *(buffer + 4); renderExtension -> data.composite.src_id = GetULONG(buffer + 8, bigEndian); renderExtension -> data.composite.msk_id = GetULONG(buffer + 12, bigEndian); renderExtension -> data.composite.dst_id = GetULONG(buffer + 16, bigEndian); renderExtension -> data.composite.src_x = GetUINT(buffer + 20, bigEndian); renderExtension -> data.composite.src_y = GetUINT(buffer + 22, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.composite.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.composite.type; *(buffer + 4) = renderExtension -> data.composite.op; PutULONG(renderExtension -> data.composite.src_id, buffer + 8, bigEndian); PutULONG(renderExtension -> data.composite.msk_id, buffer + 12, bigEndian); PutULONG(renderExtension -> data.composite.dst_id, buffer + 16, bigEndian); PutUINT(renderExtension -> data.composite.src_x, buffer + 20, bigEndian); PutUINT(renderExtension -> data.composite.src_y, buffer + 22, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.composite.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { // // Include minor opcode, size and // operator in the identity, plus // the x and y of the source. // md5_append(md5_state, buffer + 1, 4); md5_append(md5_state, buffer + 20, 4); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Source " << renderExtension -> data.composite.src_id << " mask " << renderExtension -> data.composite.msk_id << " destination " << renderExtension -> data.composite.msk_id << ".\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(renderExtension -> data.composite.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.composite.src_id = renderExtension -> data.composite.src_id; encodeBuffer.encodeXidValue(renderExtension -> data.composite.msk_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.composite.msk_id = renderExtension -> data.composite.msk_id; encodeBuffer.encodeXidValue(renderExtension -> data.composite.dst_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.composite.dst_id = renderExtension -> data.composite.dst_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.composite.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.composite.src_id, clientCache -> renderSrcPictureCache); decodeBuffer.decodeXidValue(renderExtension -> data.composite.msk_id, clientCache -> renderSrcPictureCache); decodeBuffer.decodeXidValue(renderExtension -> data.composite.dst_id, clientCache -> renderSrcPictureCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.composite.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/Colormap.cpp0000644000076400007640000000621611342775650014631 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Misc.h" #include "Unpack.h" #include "Colormap.h" #define PANIC #define WARNING #undef TEST #undef DEBUG int UnpackColormap(unsigned char method, unsigned char *src_data, int src_size, unsigned char *dst_data, int dst_size) { if (*src_data == 0) { if (dst_size != src_size - 1) { #ifdef TEST *logofs << "UnpackColormap: PANIC! Invalid destination size " << dst_size << " with source " << src_size << ".\n" << logofs_flush; #endif return -1; } #ifdef TEST *logofs << "UnpackColormap: Expanding " << src_size - 1 << " bytes of plain colormap data.\n" << logofs_flush; #endif memcpy(dst_data, src_data + 1, src_size - 1); return 1; } unsigned int check_size = dst_size; int result = ZDecompress(&unpackStream, dst_data, &check_size, src_data + 1, src_size - 1); if (result != Z_OK) { #ifdef PANIC *logofs << "UnpackColormap: PANIC! Failure decompressing colormap data. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failure decompressing colormap data. " << "Error is '" << zError(result) << "'.\n"; return -1; } else if (check_size != (unsigned int) dst_size) { #ifdef PANIC *logofs << "UnpackColormap: PANIC! Size mismatch in colormap data. " << "Resulting size is " << check_size << " with " << "expected size " << dst_size << ".\n" << logofs_flush; #endif cerr << "Error" << ": Size mismatch in colormap data. " << "Resulting size is " << check_size << " with " << "expected size " << dst_size << ".\n"; return -1; } #ifdef TEST *logofs << "UnpackColormap: Decompressed " << src_size - 1 << " bytes to " << dst_size << " bytes of colormap data.\n" << logofs_flush; #endif return 1; } nxcomp/OpcodeStore.cpp0000644000076400007640000000506111323113030015251 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "OpcodeStore.h" OpcodeStore::OpcodeStore() { // // Assign values of the specific // NX opcodes. // getControlParameters = X_NXGetControlParameters; getCleanupParameters = X_NXGetCleanupParameters; getImageParameters = X_NXGetImageParameters; getUnpackParameters = X_NXGetUnpackParameters; getShmemParameters = X_NXGetShmemParameters; getFontParameters = X_NXGetFontParameters; startSplit = X_NXStartSplit; endSplit = X_NXEndSplit; commitSplit = X_NXCommitSplit; finishSplit = X_NXFinishSplit; abortSplit = X_NXAbortSplit; splitData = X_NXSplitData; splitEvent = X_NXSplitEvent; setCacheParameters = X_NXSetCacheParameters; setExposeParameters = X_NXSetExposeParameters; setUnpackGeometry = X_NXSetUnpackGeometry; setUnpackColormap = X_NXSetUnpackColormap; setUnpackAlpha = X_NXSetUnpackAlpha; putPackedImage = X_NXPutPackedImage; freeUnpack = X_NXFreeUnpack; freeSplit = X_NXFreeSplit; // // These values must be fetched // from the X server. // shapeExtension = 0; renderExtension = 0; // // Events sent as ClientMessage. // noSplitNotify = NXNoSplitNotify; startSplitNotify = NXStartSplitNotify; commitSplitNotify = NXCommitSplitNotify; endSplitNotify = NXEndSplitNotify; emptySplitNotify = NXEmptySplitNotify; } OpcodeStore::~OpcodeStore() { } nxcomp/Pipe.h0000644000076400007640000000303511342773403013405 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // These are slightly modified versions of popen(3) and pclose(3) // that don't rely on a shell to be available on the system, so // that they can also work on Windows. // extern FILE *Popen(char * const parameters[], const char *type); extern FILE *Popen(const char *command, const char *type); extern int Pclose(FILE *file); nxcomp/DecodeBuffer.cpp0000644000076400007640000004143411323113027015352 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Misc.h" #include "Control.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP DecodeBuffer::DecodeBuffer(const unsigned char *data, unsigned int length) : buffer_(data), end_(buffer_ + length), nextSrc_(buffer_), srcMask_(0x80) { if (control -> isProtoStep7() == 1) { end_ = buffer_ + length - DECODE_BUFFER_POSTFIX_SIZE; } } int DecodeBuffer::decodeValue(unsigned int &value, unsigned int numBits, unsigned int blockSize, int endOkay) { #ifdef DUMP *logofs << "DecodeBuffer: Decoding " << numBits << " bits value with block " << blockSize << " and " << (nextSrc_ - buffer_) << " bytes in buffer.\n" << logofs_flush; #endif unsigned int result = 0; unsigned int destMask = 0x1; unsigned int bitsRead = 0; if (blockSize == 0) blockSize = numBits; unsigned char nextSrcChar = *nextSrc_; unsigned int numBlocks = 1; do { if (numBlocks == 4) { blockSize = numBits; } unsigned int bitsToRead = (blockSize > numBits - bitsRead ? numBits - bitsRead : blockSize); unsigned int count = 0; unsigned char lastBit; do { if (nextSrc_ >= end_) { if (!endOkay) { #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [A] " << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) << " end_ = " << (end_ - buffer_) << ".\n" << logofs_flush; #endif // // Label "context" is just used to identify // the routine which detected the problem in // present source file. // cerr << "Error" << ": Failure decoding data in context [A].\n"; HandleAbort(); } #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [B] " << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) << " end_ = " << (end_ - buffer_) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [B].\n"; HandleAbort(); } lastBit = (nextSrcChar & srcMask_); if (lastBit) result |= destMask; srcMask_ >>= 1; if (srcMask_ == 0) { srcMask_ = 0x80; nextSrc_++; nextSrcChar = *nextSrc_; } destMask <<= 1; } while (bitsToRead > ++count); bitsRead += bitsToRead; if (bitsRead < numBits) { if (nextSrc_ >= end_) { if (!endOkay) { #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [C] " << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) << " end_ = " << (end_ - buffer_) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [C].\n"; HandleAbort(); } #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [D] " << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) << " end_ = " << (end_ - buffer_) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [D].\n"; HandleAbort(); } unsigned char moreData = (nextSrcChar & srcMask_); srcMask_ >>= 1; if (srcMask_ == 0) { srcMask_ = 0x80; nextSrc_++; nextSrcChar = *nextSrc_; } if (!moreData) { if (lastBit) { do { result |= destMask; destMask <<= 1; } while (numBits > ++bitsRead); } else bitsRead = numBits; } } blockSize >>= 1; if (blockSize < 2) blockSize = 2; numBlocks++; } while (numBits > bitsRead); value = result; return 1; } int DecodeBuffer::decodeCachedValue(unsigned int &value, unsigned int numBits, IntCache &cache, unsigned int blockSize, int endOkay) { #ifdef DUMP *logofs << "DecodeBuffer: Decoding " << numBits << " bits cached value with block " << blockSize << " and " << (nextSrc_ - buffer_) << " bytes in buffer.\n" << logofs_flush; #endif if (nextSrc_ >= end_) { #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [E] " << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) << " end_ = " << (end_ - buffer_) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [E].\n"; HandleAbort(); } unsigned int index = 0; unsigned char nextSrcChar = *nextSrc_; while (!(nextSrcChar & srcMask_)) { index++; srcMask_ >>= 1; if (srcMask_ == 0) { srcMask_ = 0x80; nextSrc_++; if (nextSrc_ >= end_) { if (!endOkay) { #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [F] " << "in decodeCachedValue() nextSrc_ = " << (nextSrc_ - buffer_) << " end_ = " << (end_ - buffer_) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [F].\n"; HandleAbort(); } #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [G] " << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) << " end_ = " << (end_ - buffer_) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [G].\n"; HandleAbort(); } nextSrcChar = *nextSrc_; } } srcMask_ >>= 1; if (srcMask_ == 0) { srcMask_ = 0x80; nextSrc_++; } if (index == 2) { if (control -> isProtoStep8() == 1) { blockSize = cache.getBlockSize(blockSize); if (decodeValue(value, numBits, blockSize, endOkay)) { cache.insert(value, IntMask[numBits]); return 1; } #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [H] " << "in decodeCacheValue() with no value found.\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [H].\n"; HandleAbort(); } else { unsigned int sameDiff; decodeBoolValue(sameDiff); if (sameDiff) { value = cache.getLastDiff(IntMask[numBits]); cache.insert(value, IntMask[numBits]); return 1; } else { blockSize = cache.getBlockSize(blockSize); if (decodeValue(value, numBits, blockSize, endOkay)) { cache.insert(value, IntMask[numBits]); return 1; } #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [H] " << "in decodeCacheValue() with no value found.\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [H].\n"; HandleAbort(); } } } else { if (index > 2) { index--; } if (index > cache.getSize()) { #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [I] " << "in decodeCachedValue() index = " << index << " cache size = " << cache.getSize() << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [I].\n"; HandleAbort(); } value = cache.get(index); return 1; } } int DecodeBuffer::decodeCachedValue(unsigned char &value, unsigned int numBits, CharCache &cache, unsigned int blockSize, int endOkay) { #ifdef DUMP *logofs << "DecodeBuffer: Decoding " << numBits << " bits char cached value with block " << blockSize << " and " << nextSrc_ - buffer_ << " bytes read out of " << end_ - buffer_ << ".\n" << logofs_flush; #endif if (nextSrc_ >= end_) { #ifdef TEST *logofs << "DecodeBuffer: End of buffer reached in context [J] with " << nextSrc_ - buffer_ << " bytes read out of " << end_ - buffer_ << ".\n" << logofs_flush; #endif return 0; } unsigned int index = 0; unsigned char nextSrcChar = *nextSrc_; while (!(nextSrcChar & srcMask_)) { index++; srcMask_ >>= 1; if (srcMask_ == 0) { srcMask_ = 0x80; nextSrc_++; if (nextSrc_ >= end_) { if (!endOkay) { #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [K] " << "in decodeCachedValue() nextSrc_ " << (nextSrc_ - buffer_) << " end_ " << (end_ - buffer_) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [K].\n"; HandleAbort(); } #ifdef TEST *logofs << "DecodeBuffer: End of buffer reached in context [L] with " << nextSrc_ - buffer_ << " bytes read out of " << end_ - buffer_ << ".\n" << logofs_flush; #endif return 0; } nextSrcChar = *nextSrc_; } } srcMask_ >>= 1; if (srcMask_ == 0) { srcMask_ = 0x80; nextSrc_++; } if (index == 2) { unsigned int temp; if (decodeValue(temp, numBits, blockSize, endOkay)) { value = (unsigned char) temp; cache.insert(value); } else { #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [M] " << "in decodeValue() with index = 2.\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [M].\n"; HandleAbort(); } } else { if (index > 2) { index--; } if (index > cache.getSize()) { #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [N] " << "in decodeCachedValue() " << "index = " << index << " cache size = " << cache.getSize() << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [N].\n"; HandleAbort(); } value = cache.get(index); } return 1; } // // Simply returns a pointer to the correct spot in // the internal buffer. If the caller needs this // data to last beyond the lifetime of the internal // buffer, it must copy the data in its own memory. // const unsigned char *DecodeBuffer::decodeMemory(unsigned int numBytes) { #ifdef DUMP *logofs << "DecodeBuffer: Decoding " << numBytes << " bytes of memory with " << (nextSrc_ - buffer_) << " bytes in buffer.\n" << logofs_flush; #endif const unsigned char *result; // // Force ourselves to a byte boundary. // Is up to application to ensure data // is word alligned when needed. // if (srcMask_ != 0x80) { srcMask_ = 0x80; nextSrc_++; } result = nextSrc_; if (numBytes > DECODE_BUFFER_OVERFLOW_SIZE) { #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Can't decode a buffer of " << numBytes << " bytes with limit set to " << DECODE_BUFFER_OVERFLOW_SIZE << ".\n" << logofs_flush; *logofs << "DecodeBuffer: PANIC! Assuming failure decoding " << "data in context [O].\n" << logofs_flush; #endif cerr << "Error" << ": Should never decode buffer of size " << "greater than " << DECODE_BUFFER_OVERFLOW_SIZE << " bytes.\n"; cerr << "Error" << ": Assuming failure decoding data in " << "context [O].\n"; HandleAbort(); } else if (end_ - nextSrc_ < (int) numBytes) { #ifdef PANIC *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [P] " << "in decodeMemory() " << "with length " << numBytes << " and " << (end_ - nextSrc_) << " bytes remaining.\n" << logofs_flush; #endif cerr << "Error" << ": Failure decoding data in context [P].\n"; HandleAbort(); } nextSrc_ += numBytes; return result; } void DecodeBuffer::decodeActionValue(unsigned char &value, unsigned short &position, ActionCache &cache) { unsigned int t; decodeCachedValue(t, 15, *(cache.base_[cache.slot_])); cache.last_ += t; cache.last_ &= 0x7fff; value = cache.last_ >> 13; position = cache.last_ & 0x1fff; #ifdef DEBUG *logofs << "DecodeBuffer: Decoded value " << (unsigned) value << " and position " << position << " with base " << cache.slot_ << ".\n" << logofs_flush; #endif #ifdef DEBUG *logofs << "DecodeBuffer: Action block prediction is " << (*(cache.base_[cache.slot_])).getBlockSize(15) << ".\n" << logofs_flush; #endif cache.slot_ = (cache.last_ & 0xff); } void DecodeBuffer::decodeNewXidValue(unsigned int &value, unsigned int &lastId, IntCache &lastIdCache, IntCache &cache, FreeCache &freeCache) { decodeCachedValue(value, 29, lastIdCache); lastId += (value + 1); lastId &= 0x1fffffff; value = lastId; cache.push(value, 0x1fffffff); freeCache.push(value, 0x1fffffff); } void DecodeBuffer::decodeNewXidValue(unsigned int &value, unsigned int &lastId, IntCache &lastIdCache, XidCache &cache, FreeCache &freeCache) { decodeCachedValue(value, 29, lastIdCache); #ifdef DEBUG *logofs << "DecodeBuffer: Decoded new Xid difference " << value << ".\n" << logofs_flush; #endif lastId += (value + 1); lastId &= 0x1fffffff; value = lastId; unsigned int t = (value - cache.last_); cache.last_ = value; #ifdef DEBUG *logofs << "DecodeBuffer: Decoded new Xid " << value << " with base " << cache.slot_ << ".\n" << logofs_flush; #endif cache.slot_ = (value & 0xff); cache.base_[cache.slot_] -> push(t, 0x1fffffff); freeCache.push(value, 0x1fffffff); } void DecodeBuffer::decodeXidValue(unsigned int &value, XidCache &cache) { unsigned int t; decodeCachedValue(t, 29, *(cache.base_[cache.slot_])); cache.last_ += t; cache.last_ &= 0x1fffffff; value = cache.last_; #ifdef DEBUG *logofs << "DecodeBuffer: Decoded Xid " << value << " with base " << cache.slot_ << ".\n" << logofs_flush; #endif cache.slot_ = (value & 0xff); #ifdef DEBUG *logofs << "DecodeBuffer: Xid block prediction is " << (*(cache.base_[cache.slot_])).getBlockSize(29) << ".\n" << logofs_flush; #endif } void DecodeBuffer::decodeFreeXidValue(unsigned int &value, FreeCache &cache) { decodeCachedValue(value, 29, cache); } void DecodeBuffer::decodePositionValueCompat(short int &value, PositionCacheCompat &cache) { unsigned int t; decodeCachedValue(t, 13, *(cache.base_[cache.slot_])); cache.last_ += t; cache.last_ &= 0x1fff; value = cache.last_; #ifdef DEBUG *logofs << "DecodeBuffer: Decoded position " << value << " with base " << cache.slot_ << ".\n" << logofs_flush; #endif #ifdef DEBUG *logofs << "DecodeBuffer: Position block prediction is " << (*(cache.base_[cache.slot_])).getBlockSize(13) << ".\n" << logofs_flush; #endif cache.slot_ = (value & 0x1f); } nxcomp/SetUnpackAlpha.cpp0000644000076400007640000001762411323113026015703 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "SetUnpackAlpha.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // SetUnpackAlphaStore::SetUnpackAlphaStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = SETUNPACKALPHA_ENABLE_CACHE; enableData = SETUNPACKALPHA_ENABLE_DATA; enableSplit = SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_7; enableCompress = SETUNPACKALPHA_ENABLE_COMPRESS_IF_PROTO_STEP_7; dataLimit = SETUNPACKALPHA_DATA_LIMIT; dataOffset = SETUNPACKALPHA_DATA_OFFSET_IF_PROTO_STEP_7; cacheSlots = SETUNPACKALPHA_CACHE_SLOTS; cacheThreshold = SETUNPACKALPHA_CACHE_THRESHOLD; cacheLowerThreshold = SETUNPACKALPHA_CACHE_LOWER_THRESHOLD; if (control -> isProtoStep8() == 1) { enableSplit = SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_8; } messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } SetUnpackAlphaStore::~SetUnpackAlphaStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int SetUnpackAlphaStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif // // Encode the source length first because // we need it to determine the size of // the output buffer. // // SrcLength. encodeBuffer.encodeValue(GetULONG(buffer + 8, bigEndian), 32, 9); // Client. encodeBuffer.encodeCachedValue(*(buffer + 1), 8, clientCache -> resourceCache); // Method. encodeBuffer.encodeCachedValue(*(buffer + 4), 8, clientCache -> methodCache); // DstLength. encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9); #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int SetUnpackAlphaStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif unsigned int value; unsigned char cValue; // SrcLength. decodeBuffer.decodeValue(value, 32, 9); size = RoundUp4(value) + 16; buffer = writeBuffer -> addMessage(size); PutULONG(value, buffer + 8, bigEndian); // Client. decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> resourceCache); *(buffer + 1) = cValue; // Method. decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> methodCache); *(buffer + 4) = cValue; // DstLength. decodeBuffer.decodeValue(value, 32, 9); PutULONG(value, buffer + 12, bigEndian); #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int SetUnpackAlphaStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message; setUnpackAlpha -> client = *(buffer + 1); setUnpackAlpha -> method = *(buffer + 4); setUnpackAlpha -> src_length = GetULONG(buffer + 8, bigEndian); setUnpackAlpha -> dst_length = GetULONG(buffer + 12, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int SetUnpackAlphaStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message; *(buffer + 1) = setUnpackAlpha -> client; *(buffer + 4) = setUnpackAlpha -> method; PutULONG(setUnpackAlpha -> src_length, buffer + 8, bigEndian); PutULONG(setUnpackAlpha -> dst_length, buffer + 12, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void SetUnpackAlphaStore::dumpIdentity(const Message *message) const { #ifdef DUMP SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message; *logofs << name() << ": Identity client " << (unsigned int) setUnpackAlpha -> client << " method " << (unsigned int) setUnpackAlpha -> method << " source length " << setUnpackAlpha -> src_length << " destination length " << setUnpackAlpha -> dst_length << " size " << setUnpackAlpha -> size_ << ".\n"; #endif } void SetUnpackAlphaStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // Include the pack method and the source // and destination length. // md5_append(md5_state_, buffer + 4, 1); md5_append(md5_state_, buffer + 8, 8); } void SetUnpackAlphaStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message; SetUnpackAlphaMessage *cachedSetUnpackAlpha = (SetUnpackAlphaMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(setUnpackAlpha -> client, 8, clientCache -> resourceCache); cachedSetUnpackAlpha -> client = setUnpackAlpha -> client; } void SetUnpackAlphaStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(setUnpackAlpha -> client, 8, clientCache -> resourceCache); } nxcomp/SequenceQueue.h0000644000076400007640000000521711323113030015250 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef SequenceQueue_H #define SequenceQueue_H // // List of outstanding request messages which // are waiting for a reply. This class is used // in X client and server channels to correlate // the replies sequence numbers to the original // request type. // class SequenceQueue { public: SequenceQueue(); virtual ~SequenceQueue(); void push(unsigned short int sequence, unsigned char opcode, unsigned int data1 = 0, unsigned int data2 = 0, unsigned int data3 = 0); int peek(unsigned short int &sequence, unsigned char &opcode); int peek(unsigned short int &sequence, unsigned char &opcode, unsigned int &data1, unsigned int &data2, unsigned int &data3); int pop(unsigned short int &sequence, unsigned char &opcode, unsigned int &data1, unsigned int &data2, unsigned int &data3); int pop(unsigned short int &sequence, unsigned char &opcode) { unsigned int data1, data2, data3; return pop(sequence, opcode, data1, data2, data3); } int length() { return length_; } private: struct RequestSequence { unsigned short int sequence; unsigned char opcode; unsigned int data1; unsigned int data2; unsigned int data3; }; RequestSequence *queue_; unsigned int size_; unsigned int length_; unsigned int start_; unsigned int end_; }; #endif /* SequenceQueue_H */ nxcomp/RenderPictureClipCompat.h0000644000076400007640000000470111323113027017225 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderPictureClipCompat_H #define RenderPictureClipCompat_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderPictureClipCompat" #undef MESSAGE_STORE #define MESSAGE_STORE RenderPictureClipCompatStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 12 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderPictureClipCompat_H */ nxcomp/RenderComposite.h0000644000076400007640000000463111323113030015574 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderComposite_H #define RenderComposite_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderComposite" #undef MESSAGE_STORE #define MESSAGE_STORE RenderCompositeStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 36 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 0 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 0 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderComposite_H */ nxcomp/BlockCache.cpp0000644000076400007640000000432111323113030014777 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include "BlockCache.h" int BlockCache::compare(unsigned int size, const unsigned char *data, int overwrite) { int match = 0; if (size == size_) { match = 1; for (unsigned int i = 0; i < size_; i++) if (data[i] != buffer_[i]) { match = 0; break; } } if (!match && overwrite) set(size, data); return match; } void BlockCache::set(unsigned int size, const unsigned char *data) { if (size_ < size) { delete[]buffer_; buffer_ = new unsigned char[size]; } size_ = size; memcpy(buffer_, data, size); checksum_ = checksum(size, data); } unsigned int BlockCache::checksum(unsigned int size, const unsigned char *data) { unsigned int sum = 0; unsigned int shift = 0; const unsigned char *next = data; for (unsigned int i = 0; i < size; i++) { unsigned int value = (unsigned int) *next++; sum += (value << shift); shift++; if (shift == 8) shift = 0; } return sum; } nxcomp/GenericChannel.h0000644000076400007640000001777211323113027015357 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef GenericChannel_H #define GenericChannel_H #include "Channel.h" #include "Statistics.h" #include "GenericReadBuffer.h" // // Set the verbosity level. // #define PANIC #undef TEST #undef DEBUG // // Define this to log a line when a channel // is created or destroyed. // #undef REFERENCES // // This class implements the client // side compression of X protocol. // class GenericChannel : public Channel { public: GenericChannel(Transport *transport, StaticCompressor *compressor); virtual ~GenericChannel(); virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, unsigned int length); virtual int handleWrite(const unsigned char *message, unsigned int length); virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store, T_store_action action, int position, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { return 0; } virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, T_store_action action, int position, unsigned char &opcode, unsigned char *&buffer, unsigned int &size) { return 0; } virtual int handleSplit(EncodeBuffer &encodeBuffer) { return 0; } virtual int handleSplit(DecodeBuffer &decodeBuffer) { return 0; } virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split) { return 0; } virtual int handleSplitEvent(DecodeBuffer &decodeBuffer) { return 0; } virtual int handleMotion(EncodeBuffer &encodeBuffer) { return 0; } virtual int handleCompletion(EncodeBuffer &encodeBuffer); virtual int handleConfiguration(); virtual int handleFinish(); virtual int handleAsyncEvents() { return 0; } virtual int needSplit() const { return 0; } virtual int needMotion() const { return 0; } virtual T_channel_type getType() const = 0; // // Initialize the static members. // static int setReferences(); protected: // // Generic channels are considered to be // in congestion state as soon as the // socket is blocked for write. // virtual int isCongested() { return (transport_ -> blocked() == 1); } virtual int isReliable() { return 0; } // // Model generic channels' encoding and // decoding policy. // virtual int isCompressed() = 0; // // Return true if the channel contains // time sensitive data. // virtual int isPrioritized() = 0; // // Record the protocol bits for the // specific service. // virtual void addProtocolBits(unsigned int bitsIn, unsigned int bitsOut) = 0; // // Channel's own read buffer. // GenericReadBuffer readBuffer_; private: // // Keep track of object's creation // and deletion. // #ifdef REFERENCES static int references_; #endif }; class CupsChannel : public GenericChannel { public: CupsChannel(Transport *transport, StaticCompressor *compressor) : GenericChannel(transport, compressor) { } virtual ~CupsChannel() { } protected: virtual T_channel_type getType() const { return channel_cups; } virtual int isCompressed() { if (control -> isProtoStep8() == 0) { return 1; } return 0; } virtual int isPrioritized() { return 0; } virtual void addProtocolBits(unsigned int bitsIn, unsigned int bitsOut) { statistics -> addCupsBits(bitsIn, bitsOut); } }; class SmbChannel : public GenericChannel { public: SmbChannel(Transport *transport, StaticCompressor *compressor) : GenericChannel(transport, compressor) { } virtual ~SmbChannel() { } protected: virtual T_channel_type getType() const { return channel_smb; } virtual int isCompressed() { if (control -> isProtoStep8() == 0) { return 1; } return 0; } virtual int isPrioritized() { return 0; } virtual void addProtocolBits(unsigned int bitsIn, unsigned int bitsOut) { statistics -> addSmbBits(bitsIn, bitsOut); } }; class MediaChannel : public GenericChannel { public: MediaChannel(Transport *transport, StaticCompressor *compressor) : GenericChannel(transport, compressor) { } virtual ~MediaChannel() { } protected: virtual T_channel_type getType() const { return channel_media; } // // Don't try to compress the media data. // virtual int isCompressed() { return 0; } // // Reduce the latency of media channels // by setting them as prioritized, even // if this will take away bandwidth from // the X channels. // virtual int isPrioritized() { return 1; } virtual void addProtocolBits(unsigned int bitsIn, unsigned int bitsOut) { statistics -> addMediaBits(bitsIn, bitsOut); } }; class HttpChannel : public GenericChannel { public: HttpChannel(Transport *transport, StaticCompressor *compressor) : GenericChannel(transport, compressor) { } virtual ~HttpChannel() { } protected: virtual T_channel_type getType() const { return channel_http; } virtual int isCompressed() { if (control -> isProtoStep8() == 0) { return 1; } return 0; } virtual int isPrioritized() { return 0; } virtual void addProtocolBits(unsigned int bitsIn, unsigned int bitsOut) { statistics -> addHttpBits(bitsIn, bitsOut); } }; class FontChannel : public GenericChannel { public: FontChannel(Transport *transport, StaticCompressor *compressor) : GenericChannel(transport, compressor) { } virtual ~FontChannel() { } protected: virtual T_channel_type getType() const { return channel_font; } virtual int isCompressed() { if (control -> isProtoStep8() == 0) { return 1; } return 0; } virtual int isPrioritized() { return 1; } virtual void addProtocolBits(unsigned int bitsIn, unsigned int bitsOut) { statistics -> addFontBits(bitsIn, bitsOut); } }; class SlaveChannel : public GenericChannel { public: SlaveChannel(Transport *transport, StaticCompressor *compressor) : GenericChannel(transport, compressor) { } virtual ~SlaveChannel() { } protected: virtual T_channel_type getType() const { return channel_slave; } virtual int isCompressed() { return 0; } virtual int isPrioritized() { return 0; } virtual void addProtocolBits(unsigned int bitsIn, unsigned int bitsOut) { statistics -> addSlaveBits(bitsIn, bitsOut); } }; #endif /* GenericChannel_H */ nxcomp/RenderCreateGlyphSet.h0000644000076400007640000000466211323113030016521 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderCreateGlyphSet_H #define RenderCreateGlyphSet_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderCreateGlyphSet" #undef MESSAGE_STORE #define MESSAGE_STORE RenderCreateGlyphSetStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 12 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 0 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 0 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderCreateGlyphSet_H */ nxcomp/CopyArea.h0000644000076400007640000001131411323113031014172 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef CopyArea_H #define CopyArea_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define COPYAREA_ENABLE_CACHE 1 #define COPYAREA_ENABLE_DATA 0 #define COPYAREA_ENABLE_SPLIT 0 #define COPYAREA_ENABLE_COMPRESS 0 #define COPYAREA_DATA_LIMIT 0 #define COPYAREA_DATA_OFFSET 28 #define COPYAREA_CACHE_SLOTS 3000 #define COPYAREA_CACHE_THRESHOLD 5 #define COPYAREA_CACHE_LOWER_THRESHOLD 1 // // The message class. // class CopyAreaMessage : public Message { friend class CopyAreaStore; public: CopyAreaMessage() { } ~CopyAreaMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int src_drawable; unsigned int dst_drawable; unsigned int gcontext; unsigned short src_x; unsigned short src_y; unsigned short dst_x; unsigned short dst_y; unsigned short width; unsigned short height; }; class CopyAreaStore : public MessageStore { // // Constructors and destructors. // public: CopyAreaStore() : MessageStore() { enableCache = COPYAREA_ENABLE_CACHE; enableData = COPYAREA_ENABLE_DATA; enableSplit = COPYAREA_ENABLE_SPLIT; enableCompress = COPYAREA_ENABLE_COMPRESS; dataLimit = COPYAREA_DATA_LIMIT; dataOffset = COPYAREA_DATA_OFFSET; cacheSlots = COPYAREA_CACHE_SLOTS; cacheThreshold = COPYAREA_CACHE_THRESHOLD; cacheLowerThreshold = COPYAREA_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~CopyAreaStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "CopyArea"; } virtual unsigned char opcode() const { return X_CopyArea; } virtual unsigned int storage() const { return sizeof(CopyAreaMessage); } // // Message handling methods. // public: virtual Message *create() const { return new CopyAreaMessage(); } virtual Message *create(const Message &message) const { return new CopyAreaMessage((const CopyAreaMessage &) message); } virtual void destroy(Message *message) const { delete (CopyAreaMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* CopyArea_H */ nxcomp/ChangeGC.cpp0000644000076400007640000001217111323113030014422 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ChangeGC.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int ChangeGCStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { ChangeGCMessage *changeGC = (ChangeGCMessage *) message; // // Here is the fingerprint. // changeGC -> gcontext = GetULONG(buffer + 4, bigEndian); changeGC -> value_mask = GetULONG(buffer + 8, bigEndian); // // Clear the unused bytes carried in the // payload to increase the effectiveness // of the caching algorithm. // if ((int) size > dataOffset) { #ifdef DEBUG *logofs << name() << ": Removing unused bytes from the " << "data payload.\n" << logofs_flush; #endif changeGC -> value_mask &= (1 << 23) - 1; unsigned int mask = 0x1; unsigned char *source = (unsigned char *) buffer + CHANGEGC_DATA_OFFSET; unsigned long value = 0; for (unsigned int i = 0; i < 23; i++) { if (changeGC -> value_mask & mask) { value = GetULONG(source, bigEndian); value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i])); PutULONG(value, source, bigEndian); source += 4; } mask <<= 1; } } #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int ChangeGCStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { ChangeGCMessage *changeGC = (ChangeGCMessage *) message; // // Fill all the message's fields. // PutULONG(changeGC -> gcontext, buffer + 4, bigEndian); PutULONG(changeGC -> value_mask, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void ChangeGCStore::dumpIdentity(const Message *message) const { #ifdef DUMP ChangeGCMessage *changeGC = (ChangeGCMessage *) message; *logofs << name() << ": Identity gcontext " << changeGC -> gcontext << ", mask " << changeGC -> value_mask << ", size " << changeGC -> size_ << ".\n" << logofs_flush; #endif } void ChangeGCStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { /* md5_append(md5_state_, buffer + 4, 8); */ md5_append(md5_state_, buffer + 8, 4); } void ChangeGCStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { ChangeGCMessage *changeGC = (ChangeGCMessage *) message; ChangeGCMessage *cachedChangeGC = (ChangeGCMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << changeGC -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(changeGC -> gcontext, clientCache -> gcCache); cachedChangeGC -> gcontext = changeGC -> gcontext; } void ChangeGCStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { ChangeGCMessage *changeGC = (ChangeGCMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> gcCache); changeGC -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << changeGC -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } nxcomp/GenericRequest.cpp0000644000076400007640000002335411323113027015763 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "GenericRequest.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // GenericRequestStore::GenericRequestStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = GENERICREQUEST_ENABLE_CACHE; enableData = GENERICREQUEST_ENABLE_DATA; enableSplit = GENERICREQUEST_ENABLE_SPLIT; enableCompress = GENERICREQUEST_ENABLE_COMPRESS; if (control -> isProtoStep7() == 1) { enableCompress = GENERICREQUEST_ENABLE_COMPRESS_IF_PROTO_STEP_7; enableCompress = 0; } dataLimit = GENERICREQUEST_DATA_LIMIT; dataOffset = GENERICREQUEST_DATA_OFFSET; cacheSlots = GENERICREQUEST_CACHE_SLOTS; cacheThreshold = GENERICREQUEST_CACHE_THRESHOLD; cacheLowerThreshold = GENERICREQUEST_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } GenericRequestStore::~GenericRequestStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int GenericRequestStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif encodeBuffer.encodeValue(size >> 2, 16, 10); encodeBuffer.encodeCachedValue(*(buffer + 1), 8, clientCache -> genericRequestOpcodeCache); for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) { #ifdef DEBUG *logofs << name() << ": Encoding data[" << i << "] " << "at position " << i * 2 + 4 << " with value " << GetUINT(buffer + (i * 2) + 4, bigEndian) << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(GetUINT(buffer + (i * 2) + 4, bigEndian), 16, *clientCache -> genericRequestDataCache[i]); } #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int GenericRequestStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif decodeBuffer.decodeValue(size, 16, 10); size <<= 2; buffer = writeBuffer -> addMessage(size); decodeBuffer.decodeCachedValue(*(buffer + 1), 8, clientCache -> genericRequestOpcodeCache); unsigned int value; for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) { decodeBuffer.decodeCachedValue(value, 16, *clientCache -> genericRequestDataCache[i]); #ifdef DEBUG *logofs << name() << ": Decoding data[" << i << "] " << "at position " << i * 2 + 4 << " with value " << value << ".\n" << logofs_flush; #endif PutUINT(value, buffer + 4 + (i * 2), bigEndian); } #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int GenericRequestStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { GenericRequestMessage *genericRequest = (GenericRequestMessage *) message; genericRequest -> opcode = *(buffer + 1); for (unsigned int i = 0; i < 8; i++) { if ((i * 2 + 4) < size) { genericRequest -> data[i] = GetUINT(buffer + i * 2 + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed data[" << i << "] " << "with value " << genericRequest -> data[i] << ".\n" << logofs_flush; #endif } else { genericRequest -> data[i] = 0; } } #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int GenericRequestStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { GenericRequestMessage *genericRequest = (GenericRequestMessage *) message; *(buffer + 1) = genericRequest -> opcode; for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) { #ifdef DEBUG *logofs << name() << ": Unparsed data[" << i << "] " << "with value " << genericRequest -> data[i] << ".\n" << logofs_flush; #endif PutUINT(genericRequest -> data[i], buffer + i * 2 + 4, bigEndian); } #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void GenericRequestStore::dumpIdentity(const Message *message) const { #ifdef DUMP GenericRequestMessage *genericRequest = (GenericRequestMessage *) message; *logofs << name() << ": Identity opcode " << (unsigned) genericRequest -> opcode; for (int i = 0; i < 8; i++) { *logofs << ", data[" << i << "] " << genericRequest -> data[i]; } *logofs << ", size " << genericRequest -> size_ << ".\n" << logofs_flush; #endif } void GenericRequestStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // As data offset can be beyond the real end of // the message, we need to include the message's // size or we will match any message whose size // is less or equal to the data offset. // md5_append(md5_state_, buffer + 2, 2); } void GenericRequestStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { // // Encode the variant part. // GenericRequestMessage *genericRequest = (GenericRequestMessage *) message; GenericRequestMessage *cachedGenericRequest = (GenericRequestMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Updating value " << (unsigned) genericRequest -> opcode << " as opcode field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue((unsigned int) genericRequest -> opcode, 8, clientCache -> genericRequestOpcodeCache); cachedGenericRequest -> opcode = genericRequest -> opcode; for (int i = 0; i < 8 && (i * 2 + 4) < genericRequest -> size_; i++) { #ifdef TEST *logofs << name() << ": Updating data[" << i << "] " << "with value " << genericRequest -> data[i] << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue((unsigned int) genericRequest -> data[i], 16, *clientCache -> genericRequestDataCache[i]); cachedGenericRequest -> data[i] = genericRequest -> data[i]; } } void GenericRequestStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { GenericRequestMessage *genericRequest = (GenericRequestMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(genericRequest -> opcode, 8, clientCache -> genericRequestOpcodeCache); #ifdef TEST *logofs << name() << ": Updated value " << (unsigned) genericRequest -> opcode << " as opcode field.\n" << logofs_flush; #endif unsigned int value; for (int i = 0; i < 8 && (i * 2 + 4) < genericRequest -> size_; i++) { decodeBuffer.decodeCachedValue(value, 16, *clientCache -> genericRequestDataCache[i]); genericRequest -> data[i] = (unsigned short) value; #ifdef TEST *logofs << name() << ": Updated data[" << i << "] " << "with value " << genericRequest -> data[i] << ".\n" << logofs_flush; #endif } } nxcomp/ClientCache.h0000644000076400007640000002102611323113031014632 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ClientCache_H #define ClientCache_H #include "Misc.h" #include "IntCache.h" #include "CharCache.h" #include "OpcodeCache.h" #include "XidCache.h" #include "FreeCache.h" #include "TextCompressor.h" #include "ChannelCache.h" class ClientCache : public ChannelCache { public: ClientCache(); ~ClientCache(); // // Opcode prediction caches. // OpcodeCache opcodeCache; // // GC and drawables caches. // XidCache gcCache; FreeCache freeGCCache; XidCache drawableCache; FreeCache freeDrawableCache; XidCache windowCache; FreeCache freeWindowCache; // // General-purpose caches. // CharCache textCache[CLIENT_TEXT_CACHE_SIZE]; IntCache cursorCache; IntCache colormapCache; IntCache visualCache; CharCache depthCache; CharCache resourceCache; CharCache methodCache; unsigned int lastFont; // // AllocColor request. // IntCache *allocColorRGBCache[3]; // // ChangeProperty request. // CharCache changePropertyFormatCache; IntCache changePropertyPropertyCache; IntCache changePropertyTypeCache; IntCache changePropertyData32Cache; TextCompressor changePropertyTextCompressor; // // ClearArea request. // IntCache *clearAreaGeomCache[4]; // // ConfigureWindow request. // IntCache configureWindowBitmaskCache; IntCache *configureWindowAttrCache[7]; // // ConvertSelection request. // IntCache convertSelectionRequestorCache; IntCache* convertSelectionAtomCache[3]; unsigned int convertSelectionLastTimestamp; // // CopyArea request. // IntCache *copyAreaGeomCache[6]; // // CopyPlane request. // IntCache *copyPlaneGeomCache[6]; IntCache copyPlaneBitPlaneCache; // // CreateGC request. // IntCache createGCBitmaskCache; IntCache *createGCAttrCache[23]; // // CreatePixmap request. // IntCache createPixmapIdCache; unsigned int createPixmapLastId; IntCache createPixmapXCache; IntCache createPixmapYCache; // // CreateWindow request. // IntCache *createWindowGeomCache[6]; IntCache createWindowBitmaskCache; IntCache *createWindowAttrCache[15]; // // FillPoly request. // IntCache fillPolyNumPointsCache; IntCache *fillPolyXRelCache[10]; IntCache *fillPolyXAbsCache[10]; IntCache *fillPolyYRelCache[10]; IntCache *fillPolyYAbsCache[10]; unsigned int fillPolyRecentX[8]; unsigned int fillPolyRecentY[8]; unsigned int fillPolyIndex; // // GetSelectionOwner request. // IntCache getSelectionOwnerSelectionCache; // // GrabButton request (also used for GrabPointer). // IntCache grabButtonEventMaskCache; IntCache grabButtonConfineCache; CharCache grabButtonButtonCache; IntCache grabButtonModifierCache; // // GrabKeyboard request. // unsigned int grabKeyboardLastTimestamp; // // ImageText8/16 request. // IntCache imageTextLengthCache; unsigned int imageTextLastX; unsigned int imageTextLastY; IntCache imageTextCacheX; IntCache imageTextCacheY; TextCompressor imageTextTextCompressor; // // InternAtom request. // TextCompressor internAtomTextCompressor; // // OpenFont request. // TextCompressor openFontTextCompressor; // // PolyFillRectangle request. // IntCache *polyFillRectangleCacheX[4]; IntCache *polyFillRectangleCacheY[4]; IntCache *polyFillRectangleCacheWidth[4]; IntCache *polyFillRectangleCacheHeight[4]; // // PolyLine request. // IntCache *polyLineCacheX[2]; IntCache *polyLineCacheY[2]; // // PolyPoint request. // IntCache *polyPointCacheX[2]; IntCache *polyPointCacheY[2]; // // PolyRectangle request. // IntCache *polyRectangleGeomCache[4]; // // PolySegment request. // IntCache polySegmentCacheX; IntCache polySegmentCacheY; unsigned int polySegmentLastX[2]; unsigned int polySegmentLastY[2]; unsigned int polySegmentCacheIndex; // // PolyText8/16 request. // unsigned int polyTextLastX; unsigned int polyTextLastY; IntCache polyTextCacheX; IntCache polyTextCacheY; IntCache polyTextFontCache; CharCache polyTextDeltaCache; TextCompressor polyTextTextCompressor; // // PutImage request. // IntCache putImageWidthCache; IntCache putImageHeightCache; unsigned int putImageLastX; unsigned int putImageLastY; IntCache putImageXCache; IntCache putImageYCache; CharCache putImageLeftPadCache; // // GetImage request. // IntCache getImagePlaneMaskCache; // // QueryColors request. // unsigned int queryColorsLastPixel; // // SetClipRectangles request. // IntCache setClipRectanglesXCache; IntCache setClipRectanglesYCache; IntCache *setClipRectanglesGeomCache[4]; // // SetDashes request. // IntCache setDashesLengthCache; IntCache setDashesOffsetCache; CharCache setDashesDashCache_[2]; // // SetSelectionOwner request. // IntCache setSelectionOwnerCache; IntCache setSelectionOwnerTimestampCache; // // TranslateCoords request. // IntCache translateCoordsSrcCache; IntCache translateCoordsDstCache; IntCache translateCoordsXCache; IntCache translateCoordsYCache; // // SendEvent request. // IntCache sendEventMaskCache; CharCache sendEventCodeCache; CharCache sendEventByteDataCache; unsigned int sendEventLastSequence; IntCache sendEventIntDataCache; CharCache sendEventEventCache; // // PolyFillArc request. // IntCache *polyFillArcCacheX[2]; IntCache *polyFillArcCacheY[2]; IntCache *polyFillArcCacheWidth[2]; IntCache *polyFillArcCacheHeight[2]; IntCache *polyFillArcCacheAngle1[2]; IntCache *polyFillArcCacheAngle2[2]; // // PolyArc request. // IntCache *polyArcCacheX[2]; IntCache *polyArcCacheY[2]; IntCache *polyArcCacheWidth[2]; IntCache *polyArcCacheHeight[2]; IntCache *polyArcCacheAngle1[2]; IntCache *polyArcCacheAngle2[2]; // // PutPackedImage request. // IntCache putPackedImageSrcLengthCache; IntCache putPackedImageDstLengthCache; // // Shape extension requests. // CharCache shapeOpcodeCache; IntCache *shapeDataCache[8]; // // Generic requests. // CharCache genericRequestOpcodeCache; IntCache *genericRequestDataCache[8]; // // Render extension requests. // OpcodeCache renderOpcodeCache; CharCache renderOpCache; XidCache renderSrcPictureCache; XidCache renderMaskPictureCache; XidCache renderDstPictureCache; FreeCache renderFreePictureCache; IntCache renderGlyphSetCache; FreeCache renderFreeGlyphSetCache; IntCache renderIdCache; IntCache renderLengthCache; IntCache renderFormatCache; IntCache renderValueMaskCache; IntCache renderNumGlyphsCache; IntCache renderXCache; IntCache renderYCache; unsigned int renderLastX; unsigned int renderLastY; IntCache renderWidthCache; IntCache renderHeightCache; unsigned int renderLastId; IntCache *renderDataCache[16]; TextCompressor renderTextCompressor; IntCache renderGlyphXCache; IntCache renderGlyphYCache; unsigned int renderGlyphX; unsigned int renderGlyphY; IntCache *renderCompositeGlyphsDataCache[16]; unsigned int renderLastCompositeGlyphsData; IntCache *renderCompositeDataCache[3]; // // SetCacheParameters request. // IntCache setCacheParametersCache; // // Encode new XID values based // on the last value encoded. // IntCache lastIdCache; unsigned int lastId; }; #endif /* ClientCache_H */ nxcomp/OpcodeCache.h0000644000076400007640000000301611323113027014631 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef OpcodeCache_H #define OpcodeCache_H #include "CharCache.h" class OpcodeCache { friend class EncodeBuffer; friend class DecodeBuffer; public: OpcodeCache() { slot_ = 0; } ~OpcodeCache() { } private: CharCache base_[256]; unsigned char slot_; }; #endif /* OpcodeCache_H */ nxcomp/RenderTriangles.cpp0000644000076400007640000002543711323113030016124 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderTriangles.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding value " << ((size - MESSAGE_OFFSET) >> 2) << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); #ifdef DEBUG *logofs << name() << ": Decoded value " << size << ".\n" << logofs_flush; #endif size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), clientCache -> renderDstPictureCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, clientCache -> renderFormatCache); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian), clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian), clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> renderDstPictureCache); PutULONG(value, buffer + 12, bigEndian); decodeBuffer.decodeCachedValue(value, 32, clientCache -> renderFormatCache); PutULONG(value, buffer + 16, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { if (size > MESSAGE_OFFSET) { encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); } #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of text data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { if (size > MESSAGE_OFFSET) { decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); } #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.triangles.type = *(buffer + 1); renderExtension -> data.triangles.op = *(buffer + 4); renderExtension -> data.triangles.src_id = GetULONG(buffer + 8, bigEndian); renderExtension -> data.triangles.dst_id = GetULONG(buffer + 12, bigEndian); renderExtension -> data.triangles.format = GetULONG(buffer + 16, bigEndian); renderExtension -> data.triangles.src_x = GetUINT(buffer + 20, bigEndian); renderExtension -> data.triangles.src_y = GetUINT(buffer + 22, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.triangles.type << " size is " << renderExtension -> size_ << " identity size " << renderExtension -> i_size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.triangles.type; *(buffer + 4) = renderExtension -> data.triangles.op; PutULONG(renderExtension -> data.triangles.src_id, buffer + 8, bigEndian); PutULONG(renderExtension -> data.triangles.dst_id, buffer + 12, bigEndian); PutULONG(renderExtension -> data.triangles.format, buffer + 16, bigEndian); PutUINT(renderExtension -> data.triangles.src_x, buffer + 20, bigEndian); PutUINT(renderExtension -> data.triangles.src_y, buffer + 22, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.triangles.type << " size is " << renderExtension -> size_ << " identity size " << renderExtension -> i_size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { // // Include minor opcode, size and the // operator in the identity. // md5_append(md5_state, buffer + 1, 4); // // Also include the format but not the // x and y source. // md5_append(md5_state, buffer + 16, 4); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(renderExtension -> data.triangles.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.triangles.src_id = renderExtension -> data.triangles.src_id; encodeBuffer.encodeXidValue(renderExtension -> data.triangles.dst_id, clientCache -> renderDstPictureCache); cachedRenderExtension -> data.triangles.dst_id = renderExtension -> data.triangles.dst_id; // // The source x and y coordinates are // encoded as differerences in respect // to the previous cached value. // unsigned int value; unsigned int previous; value = renderExtension -> data.triangles.src_x; previous = cachedRenderExtension -> data.triangles.src_x; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderXCache, 11); cachedRenderExtension -> data.triangles.src_x = value; value = renderExtension -> data.triangles.src_y; previous = cachedRenderExtension -> data.triangles.src_y; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderYCache, 11); cachedRenderExtension -> data.triangles.src_y = value; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.triangles.type << " size is " << renderExtension -> size_ << " source x " << renderExtension -> data.triangles.src_x << " y " << renderExtension -> data.triangles.src_y << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.triangles.src_id, clientCache -> renderSrcPictureCache); decodeBuffer.decodeXidValue(renderExtension -> data.triangles.dst_id, clientCache -> renderDstPictureCache); unsigned int value; unsigned int previous; previous = renderExtension -> data.triangles.src_x; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderXCache, 11); renderExtension -> data.triangles.src_x = value; previous = renderExtension -> data.triangles.src_y; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderYCache, 11); renderExtension -> data.triangles.src_y = value; #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.triangles.type << " size is " << renderExtension -> size_ << " source x " << renderExtension -> data.triangles.src_x << " y " << renderExtension -> data.triangles.src_y << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/Pgn.cpp0000644000076400007640000004265711342773403013604 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // This file obviously supports PNG // decompression. It was renamed to // avoid name clashes on Windows. // #include #include #include #include #include "Unpack.h" #include "Pgn.h" #define PANIC #define WARNING #undef TEST #undef DEBUG #define RGB24_TO_PIXEL(bpp,r,g,b) \ ((((CARD##bpp)(r) & 0xFF) * srcRedMax2 + 127) / 255 \ << srcRedShift2 | \ (((CARD##bpp)(g) & 0xFF) * srcGreenMax2 + 127) / 255 \ << srcGreenShift2 | \ (((CARD##bpp)(b) & 0xFF) * srcBlueMax2 + 127) / 255 \ << srcBlueShift2) #define RGB24_TO_PIXEL32(r,g,b) \ (((CARD32)(r) & 0xFF) << srcRedShift2 | \ ((CARD32)(g) & 0xFF) << srcGreenShift2 | \ ((CARD32)(b) & 0xFF) << srcBlueShift2) // // Functions from Unpack.cpp // extern int Unpack32To32(const T_colormask *colormask, const unsigned int *data, unsigned int *out, unsigned int *end); extern int Unpack24To24(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); extern int Unpack16To16(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); // // Local functions used for the png decompression. // static int DecompressPng16(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder); static int DecompressPng24(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder); static int DecompressPng32(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder); static void PngReadData(png_structp png_ptr, png_bytep data, png_size_t length); // // Colormap stuff. // CARD16 srcRedMax2, srcGreenMax2, srcBlueMax2; CARD8 srcRedShift2, srcGreenShift2, srcBlueShift2; // // Attributes used for the png decompression. // static char *tmpBuf; static int tmpBufSize = 0; static int streamPos; int UnpackPng(T_geometry *geometry, unsigned char method, unsigned char *srcData, int srcSize, int dstBpp, int dstWidth, int dstHeight, unsigned char *dstData, int dstSize) { int byteOrder = geometry -> image_byte_order; // // Check if data is coming from a failed unsplit. // if (srcSize < 2 || (srcData[0] == SPLIT_PATTERN && srcData[1] == SPLIT_PATTERN)) { #ifdef WARNING *logofs << "UnpackPng: WARNING! Skipping unpack of dummy data.\n" << logofs_flush; #endif return -1; } #ifdef DEBUG *logofs << "UnpackPng: Decompressing image with " << "srcSize " << srcSize << " and bpp " << dstBpp << ".\n" << logofs_flush; #endif srcRedShift2 = ffs(geometry -> red_mask) - 1; srcGreenShift2 = ffs(geometry -> green_mask) - 1; srcBlueShift2 = ffs(geometry -> blue_mask) - 1; srcRedMax2 = geometry -> red_mask >> srcRedShift2; srcGreenMax2 = geometry -> green_mask >> srcGreenShift2; srcBlueMax2 = geometry -> blue_mask >> srcBlueShift2; // // Make enough space in the temporary // buffer to have one complete row of // the image with 3 bytes per pixel. // tmpBufSize = dstWidth * 3; tmpBuf = new char [tmpBufSize]; if (tmpBuf == NULL) { #ifdef PANIC *logofs << "UnpackPng: PANIC! Cannot allocate " << dstWidth * 3 << " bytes for PNG " << "decompressed data.\n" << logofs_flush; #endif delete [] tmpBuf; return -1; } int result = 1; switch (dstBpp) { case 8: { // // Simply move the data from srcData to dstData // taking into consideration the correct padding. // int row; unsigned char * dstBuff = dstData; unsigned char * srcBuff = srcData; for (row = 0; row < dstHeight; row++) { memcpy(dstBuff, srcBuff, dstWidth ); dstBuff += RoundUp4(dstWidth); srcBuff += dstWidth; } } case 16: { result = DecompressPng16(srcData, srcSize, dstWidth, dstHeight, dstData, byteOrder); break; } case 24: { result = DecompressPng24(srcData, srcSize, dstWidth, dstHeight, dstData, byteOrder); break; } case 32: { result = DecompressPng32(srcData, srcSize, dstWidth, dstHeight, dstData, byteOrder); break; } default: { #ifdef PANIC *logofs << "UnpackPng: PANIC! Error in PNG compression. " << " Unsupported Bpp value " << dstBpp << " for the PNG compression" << ".\n" << logofs_flush; #endif delete [] tmpBuf; result = -1; } } if (result == -1) { delete [] tmpBuf; return result; } // // Apply the correction for the brightness // int maskMethod; switch (method) { case PACK_PNG_8_COLORS: { maskMethod = MASK_8_COLORS; break; } case PACK_PNG_64_COLORS: { maskMethod = MASK_64_COLORS; break; } case PACK_PNG_256_COLORS: { maskMethod = MASK_256_COLORS; break; } case PACK_PNG_512_COLORS: { maskMethod = MASK_512_COLORS; break; } case PACK_PNG_4K_COLORS: { maskMethod = MASK_4K_COLORS; break; } case PACK_PNG_32K_COLORS: { maskMethod = MASK_32K_COLORS; break; } case PACK_PNG_64K_COLORS: { maskMethod = MASK_64K_COLORS; break; } case PACK_PNG_256K_COLORS: { maskMethod = MASK_256K_COLORS; break; } case PACK_PNG_2M_COLORS: { maskMethod = MASK_2M_COLORS; break; } case PACK_PNG_16M_COLORS: { maskMethod = MASK_16M_COLORS; break; } default: { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << " No matching decompression method.\n" << logofs_flush; #endif delete [] tmpBuf; return -1; } } const T_colormask *colorMask = MethodColorMask(maskMethod); unsigned char *dstBuff = dstData; switch (dstBpp) { case 16: { Unpack16To16(colorMask, dstBuff, dstBuff, dstBuff + dstSize); break; } case 24: { break; } case 32: { Unpack32To32(colorMask, (unsigned int *)dstBuff, (unsigned int *)dstBuff, (unsigned int *)(dstBuff + dstSize)); break; } default: { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << " No matching destination bits per plane.\n" << logofs_flush; #endif delete [] tmpBuf; return -1; } } delete [] tmpBuf; return 1; } // // Functions that actually do // the PNG decompression. // int DecompressPng16(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) { unsigned char *data; unsigned int dx, dy; png_structp pngPtr; png_infop infoPtr; png_bytep rowPointers; streamPos = 0; pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!pngPtr) { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << " Failed png_create_read_struct operation" << ".\n" << logofs_flush; #endif return -1; } infoPtr = png_create_info_struct(pngPtr); if (!infoPtr) { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << "Failed png_create_info_struct operation" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, NULL, NULL); return -1; } if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << "Error during IO initialization" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_set_read_fn(pngPtr, (void *)compressedData, PngReadData); if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << "Error during read of PNG header" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_read_info(pngPtr, infoPtr); if (infoPtr -> color_type == PNG_COLOR_TYPE_PALETTE) { png_set_expand(pngPtr); } // // data points to dstBuf which is // already padded correctly for the final // image to put // data = dstBuf; rowPointers = (png_byte *) tmpBuf; // // We use setjmp() to save our context. // The PNG library will call longjmp() // in case of error. // if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng16: PANIC! " << "Error during read of PNG rows" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } unsigned long pixel; for (dy = 0; dy < h; dy++) { png_read_row(pngPtr, rowPointers, NULL); for (dx = 0; dx < w; dx++) { pixel = RGB24_TO_PIXEL(16, tmpBuf[dx*3], tmpBuf[dx*3+1], tmpBuf[dx*3+2]); // // Follow the server byte order when arranging data. // if (byteOrder == LSBFirst) { data[0] = (unsigned char) (pixel & 0xff); data[1] = (unsigned char) ((pixel >> 8) & 0xff); } else { data[1] = (unsigned char) (pixel & 0xff); data[0] = (unsigned char) ((pixel >> 8) & 0xff); } data += 2; } // // Move pixelPtr at the beginning of the // next line. // data = data + (RoundUp4(w * 2) - w * 2); } png_destroy_read_struct(&pngPtr, &infoPtr,NULL); #ifdef DEBUG *logofs << "DecompressPng16: Decompression finished." << dy << " lines handled.\n" << logofs_flush; #endif return 1; } int DecompressPng24(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) { static CARD8 *pixelPtr = NULL; unsigned int dx, dy; png_structp pngPtr; png_infop infoPtr; png_bytep rowPointers; streamPos = 0; pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!pngPtr) { #ifdef PANIC *logofs << "DecompressPng24: PANIC! " << "Failed png_create_read_struct operation" << ".\n" << logofs_flush; #endif return -1; } infoPtr = png_create_info_struct(pngPtr); if (!infoPtr) { #ifdef PANIC *logofs << "DecompressPng24: PANIC! " << "Failed png_create_info_struct operation" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, NULL, NULL); return -1; } if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng24: PANIC! " << "Error during IO initialization" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_set_read_fn(pngPtr, (void *)compressedData, PngReadData); if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng24: PANIC! " << "Error during read of PNG header" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_read_info( pngPtr, infoPtr ) ; if (infoPtr -> color_type == PNG_COLOR_TYPE_PALETTE) { png_set_expand(pngPtr); } // // PixelPtr points to dstBuf which is // already padded correctly for the final // image to put // pixelPtr = (CARD8 *) dstBuf; rowPointers = (png_byte *)tmpBuf; if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng24: PANIC! " << "Error during read of PNG rows" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } for (dy = 0; dy < h; dy++) { png_read_row(pngPtr, rowPointers, NULL); for (dx = 0; dx < w; dx++) { // // Follow the server byte order when arranging data. // if (byteOrder == LSBFirst) { pixelPtr[0] = tmpBuf[dx * 3]; pixelPtr[1] = tmpBuf[dx * 3 + 1]; pixelPtr[2] = tmpBuf[dx * 3 + 2]; } else { pixelPtr[2] = tmpBuf[dx * 3]; pixelPtr[1] = tmpBuf[dx * 3 + 1]; pixelPtr[0] = tmpBuf[dx * 3 + 2]; } pixelPtr += 3; } // // Go to the next line. // pixelPtr = (CARD8 *) (((char *) pixelPtr) + (RoundUp4(w * 3) - w * 3)); } png_destroy_read_struct(&pngPtr, &infoPtr,NULL); #ifdef DEBUG *logofs << "DecompressPng24: Decompression finished." << dy << " lines handled.\n" << logofs_flush; #endif return 1; } int DecompressPng32(unsigned char *compressedData, int compressedLen, unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) { unsigned char *data; unsigned int dx, dy; png_structp pngPtr; png_infop infoPtr; png_bytep rowPointers; streamPos = 0; pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!pngPtr) { #ifdef PANIC *logofs << "DecompressPng32: PANIC! " << "Failed png_create_read_struct operation" << ".\n" << logofs_flush; #endif return -1; } infoPtr = png_create_info_struct(pngPtr); if (!infoPtr) { #ifdef PANIC *logofs << "DecompressPng32: PANIC! " << "Failed png_create_info_struct operation." << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, NULL, NULL); return -1; } if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng32: PANIC! " << "Error during IO initialization" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_set_read_fn(pngPtr, (void *)compressedData, PngReadData); if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng32: PANIC! " << "Error during read of PNG header" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } png_read_info(pngPtr, infoPtr) ; if (infoPtr -> color_type == PNG_COLOR_TYPE_PALETTE) { png_set_expand(pngPtr); } // // data points to dstBuf which is // already padded correctly for the final // image to put // data = dstBuf; rowPointers = (png_byte *) tmpBuf; if (setjmp(png_jmpbuf(pngPtr))) { #ifdef PANIC *logofs << "DecompressPng32: PANIC! " << "Error during read of PNG rows" << ".\n" << logofs_flush; #endif png_destroy_read_struct(&pngPtr, &infoPtr, NULL); return -1; } unsigned long pixel; int i; for (dy = 0; dy < h; dy++) { png_read_row(pngPtr, rowPointers, NULL); for (dx = 0; dx < w; dx++) { pixel = RGB24_TO_PIXEL(32, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1], tmpBuf[dx * 3 + 2]); // // Follow the server byte order when arranging data. // if (byteOrder == LSBFirst) { for (i = 0; i < 4; i++) { data[i] = (unsigned char)(pixel & 0xff); pixel >>= 8; } } else { for (i = 3; i >= 0; i--) { data[i] = (unsigned char) (pixel & 0xff); pixel >>= 8; } } data += 4; } } png_destroy_read_struct(&pngPtr, &infoPtr,NULL); #ifdef DEBUG *logofs << "DecompressPng32: Decompression finished." << dy << " lines handled.\n" << logofs_flush; #endif return 1; } static void PngReadData(png_structp png_ptr, png_bytep data, png_size_t length) { memcpy((char *) data, (char *) png_get_io_ptr(png_ptr) + streamPos, length); streamPos += length; } nxcomp/RenderPictureFilter.cpp0000644000076400007640000001752111323113030016750 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderPictureFilter.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding value " << ((size - MESSAGE_OFFSET) >> 2) << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); #ifdef DEBUG *logofs << name() << ": Decoded value " << size << ".\n" << logofs_flush; #endif size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeCachedValue(GetUINT(buffer + 8, bigEndian), 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 4, bigEndian); decodeBuffer.decodeCachedValue(value, 16, clientCache -> renderLengthCache, 5); PutUINT(value, buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.picture_filter.type = *(buffer + 1); renderExtension -> data.picture_filter.src_id = GetULONG(buffer + 4, bigEndian); renderExtension -> data.picture_filter.num_elm = GetUINT(buffer + 8, bigEndian); // // Clean the padding bytes. This // should be the purpose of the // filter. // #ifdef TEST *logofs << name() << ": Cleaning " << RoundUp4(renderExtension -> data.picture_filter.num_elm) - renderExtension -> data.picture_filter.num_elm << " bytes " << "at offset " << MESSAGE_OFFSET + renderExtension -> data.picture_filter.num_elm << " with " << renderExtension -> data.picture_filter.num_elm << " elements and size " << renderExtension -> size_ << ".\n" << logofs_flush; #endif if (size >= MESSAGE_OFFSET + renderExtension -> data.picture_filter.num_elm) { unsigned char *next = (unsigned char *) buffer + MESSAGE_OFFSET + renderExtension -> data.picture_filter.num_elm; while (next < buffer + size) { *next++ = '\0'; } } #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.picture_filter.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.picture_filter.type; PutULONG(renderExtension -> data.picture_filter.src_id, buffer + 4, bigEndian); PutUINT(renderExtension -> data.picture_filter.num_elm, buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.picture_filter.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { // // Include the length of the filter name // in the checksum. // md5_append(md5_state, buffer + 1, 3); md5_append(md5_state, buffer + 8, 2); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(renderExtension -> data.picture_filter.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.picture_filter.src_id = renderExtension -> data.picture_filter.src_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.picture_filter.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.picture_filter.src_id, clientCache -> renderSrcPictureCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.picture_filter.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/List.h0000644000076400007640000000361211323113031013404 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef List_H #define List_H #include "Misc.h" #include "Types.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Define this to log when lists are // allocated and deallocated. // #undef REFERENCES class List { public: List(); ~List(); int getSize() { return list_.size(); } T_list &getList() { return list_; } T_list copyList() { return list_; } void add(int value) { list_.push_back(value); } void remove(int value); void rotate(); private: // // The list container. // T_list list_; #ifdef REFERENCES static int references_; #endif }; #endif /* List_H */ nxcomp/Misc.h0000644000076400007640000001343611343677675013430 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Misc_H #define Misc_H #include #include #include #include #ifdef __sun #include #endif using namespace std; // // This is MD5 length. // #define MD5_LENGTH 16 // // Error handling macros. // #define ESET(e) (errno = (e)) #define EGET() (errno) #define ESTR() strerror(errno) // // TCP port offset applied to NX port specification. // extern const int DEFAULT_NX_PROXY_PORT_OFFSET; // // Default TCP port used by client proxy to listen // to X clients and by server proxy to connect to // remote. // extern const int DEFAULT_NX_PROXY_PORT; // // Default X display number that client // proxy imitates. // extern const int DEFAULT_NX_X_PORT; // // Establish the port offsets for the additional // services. // extern const int DEFAULT_NX_CUPS_PORT_OFFSET; extern const int DEFAULT_NX_SMB_PORT_OFFSET; extern const int DEFAULT_NX_MEDIA_PORT_OFFSET; extern const int DEFAULT_NX_AUX_PORT_OFFSET; extern const int DEFAULT_NX_HTTP_PORT_OFFSET; extern const int DEFAULT_NX_FONT_PORT_OFFSET; // // Slave channels can be originated by both sides // so they need to have different port offsets // in the case the user runs both proxies on the // same host. // extern const int DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET; extern const int DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET; // // Return strings containing various info. // const char *GetUsageInfo(); const char *GetCopyrightInfo(); const char *GetOtherCopyrightInfo(); // // Define this if you want immediate flush of // the log output. // #define FLUSH_LOGOFS // // Global objects providing shared functions. // class Auth; class Control; class Statistics; extern Auth *auth; extern Control *control; extern Statistics *statistics; // // Log file. // extern ostream *logofs; // // Cleanup code. // void HandleAbort() __attribute__((noreturn)); void HandleShutdown() __attribute__((noreturn)); extern "C" { void HandleCleanup(int code = 0) __attribute__((noreturn)); } // // Manage signal handlers. // void DisableSignals(); void EnableSignals(); // // Manage timers. // void SetTimer(int value); void ResetTimer(); // // Show a dialog asking the user if he/she // wants to close the current session. Look // in the alerts file for the known critical // events. // void HandleAlert(int code, int local); // // Run the callback registered by the proxy // or the agent. // void KeeperCallback(); void FlushCallback(int length); // // Return the string literal corresponding // the value. // const char *DumpSignal(int signal); const char *DumpPolicy(int type); const char *DumpControl(int code); const char *DumpSession(int code); const char *DumpAction(int type); const char *DumpState(int type); const char *DumpToken(int type); // // Print out content of buffer to log file. // You need to define DUMP or OPCODES in // the source to have these compiled. // const char *DumpOpcode(const int &opcode); const char *DumpChecksum(const void *checksum); void DumpData(const unsigned char *data, unsigned int length); void DumpHexData(const unsigned char *data, unsigned int length); void DumpChecksum(const unsigned char *data, unsigned int length); void DumpBlockChecksums(const unsigned char *data, unsigned int length, unsigned int block); // // Defines logofs_flush as an empty string to // avoid calling the corresponding ostream's // flush() function. // #ifdef FLUSH_LOGOFS #define logofs_flush "" ; logofs -> flush() #else #define logofs_flush "" #endif // // Is the host where local proxy is running // big-endian? // extern int _hostBigEndian; extern int _storeBigEndian; inline void setHostBigEndian(int flag) { _hostBigEndian = flag; } inline int hostBigEndian() { return _hostBigEndian; } inline int storeBigEndian() { return _storeBigEndian; } extern const unsigned int IntMask[33]; unsigned int GetUINT(unsigned const char *buffer, int bigEndian); unsigned int GetULONG(unsigned const char *buffer, int bigEndian); void PutUINT(unsigned int value, unsigned char *buffer, int bigEndian); void PutULONG(unsigned int value, unsigned char *buffer, int bigEndian); inline void CleanData(unsigned char *buffer, int size) { unsigned char *end = buffer + size; while (buffer < end) { *buffer++ = 0x00; } } int CheckData(istream *fs); int CheckData(ostream *fs); int PutData(ostream *fs, const unsigned char *buffer, int size); int GetData(istream *fs, unsigned char *buffer, int size); int FlushData(ostream *fs); unsigned int RoundUp2(unsigned int x); unsigned int RoundUp4(unsigned int x); unsigned int RoundUp8(unsigned int x); #endif /* Misc_H */ nxcomp/RenderFreePictureCompat.cpp0000644000076400007640000001177211323113027017560 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderFreePictureCompat.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian), clientCache -> renderLastId, 29, clientCache -> renderIdCache); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { unsigned int value; ClientCache *clientCache = (ClientCache *) channelCache; *(buffer + 1) = type; decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastId, 29, clientCache -> renderIdCache); PutULONG(value, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.free_picture.type = *(buffer + 1); renderExtension -> data.free_picture.src_id = GetULONG(buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.free_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.free_picture.type; PutULONG(renderExtension -> data.free_picture.src_id, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.free_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeDiffCachedValue(renderExtension -> data.free_picture.src_id, clientCache -> renderLastId, 29, clientCache -> renderIdCache); cachedRenderExtension -> data.free_picture.src_id = renderExtension -> data.free_picture.src_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.free_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeDiffCachedValue(renderExtension -> data.free_picture.src_id, clientCache -> renderLastId, 29, clientCache -> renderIdCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.free_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/ServerReadBuffer.h0000644000076400007640000000436511323113031015673 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ServerReadBuffer_H #define ServerReadBuffer_H #include "ReadBuffer.h" #include "Control.h" class ServerChannel; class ServerReadBuffer : public ReadBuffer { public: ServerReadBuffer(Transport *transport, ServerChannel *channel) : ReadBuffer(transport), firstMessage_(1), channel_(channel) { } virtual ~ServerReadBuffer() { } void setBigEndian(int flag) { bigEndian_ = flag; } unsigned char *peekMessage(unsigned int &offset, unsigned char opcode, unsigned short sequence); protected: virtual unsigned int suggestedLength(unsigned int pendingLength); virtual int locateMessage(const unsigned char *start, const unsigned char *end, unsigned int &controlLength, unsigned int &dataLength, unsigned int &trailerLength); int bigEndian_; int firstMessage_; ServerChannel *channel_; }; #endif /* ServerReadBuffer_H */ nxcomp/Vars.c0000644000076400007640000000344711323113031013405 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifdef __cplusplus extern "C" { #endif #include #include "NXvars.h" /* * Allocate here instances of variables and * pointers declared in NXvars.h. */ int _NXHandleDisplayError = 0; NXDisplayErrorPredicate _NXDisplayErrorFunction = NULL; int _NXUnsetLibraryPath = 0; NXLostSequenceHandler _NXLostSequenceFunction = NULL; NXDisplayBlockHandler _NXDisplayBlockFunction = NULL; NXDisplayWriteHandler _NXDisplayWriteFunction = NULL; NXDisplayFlushHandler _NXDisplayFlushFunction = NULL; NXDisplayStatisticsHandler _NXDisplayStatisticsFunction = NULL; #ifdef __cplusplus } #endif nxcomp/RenderCreatePicture.cpp0000644000076400007640000002106211323113027016727 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderCreatePicture.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> lastId, clientCache -> lastIdCache, clientCache -> renderSrcPictureCache, clientCache -> renderFreePictureCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> drawableCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 12, bigEndian), 32, clientCache -> renderFormatCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, clientCache -> renderValueMaskCache); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeNewXidValue(value, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> renderSrcPictureCache, clientCache -> renderFreePictureCache); PutULONG(value, buffer + 4, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeCachedValue(value, 32, clientCache -> renderFormatCache); PutULONG(value, buffer + 12, bigEndian); decodeBuffer.decodeCachedValue(value, 32, clientCache -> renderValueMaskCache); PutULONG(value, buffer + 16, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.create_picture.type = *(buffer + 1); renderExtension -> data.create_picture.src_id = GetULONG(buffer + 4, bigEndian); renderExtension -> data.create_picture.dst_id = GetULONG(buffer + 8, bigEndian); renderExtension -> data.create_picture.format = GetULONG(buffer + 12, bigEndian); renderExtension -> data.create_picture.mask = GetULONG(buffer + 16, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.create_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.create_picture.type; PutULONG(renderExtension -> data.create_picture.src_id, buffer + 4, bigEndian); PutULONG(renderExtension -> data.create_picture.dst_id, buffer + 8, bigEndian); PutULONG(renderExtension -> data.create_picture.format, buffer + 12, bigEndian); PutULONG(renderExtension -> data.create_picture.mask, buffer + 16, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.create_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); md5_append(md5_state, buffer + 12, 8); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding new id value " << renderExtension -> data.create_picture.src_id << ".\n"; #endif encodeBuffer.encodeNewXidValue(renderExtension -> data.create_picture.src_id, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> renderSrcPictureCache, clientCache -> renderFreePictureCache); cachedRenderExtension -> data.create_picture.src_id = renderExtension -> data.create_picture.src_id; encodeBuffer.encodeXidValue(renderExtension -> data.create_picture.dst_id, clientCache -> drawableCache); cachedRenderExtension -> data.create_picture.dst_id = renderExtension -> data.create_picture.dst_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.create_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeNewXidValue(renderExtension -> data.create_picture.src_id, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> renderSrcPictureCache, clientCache -> renderFreePictureCache); decodeBuffer.decodeXidValue(renderExtension -> data.create_picture.dst_id, clientCache -> drawableCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.create_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/PolyLine.cpp0000644000076400007640000001221011323113030014550 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "PolyLine.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int PolyLineStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { PolyLineMessage *polyLine = (PolyLineMessage *) message; // // Here is the fingerprint. // polyLine -> mode = *(buffer + 1); polyLine -> drawable = GetULONG(buffer + 4, bigEndian); polyLine -> gcontext = GetULONG(buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int PolyLineStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { PolyLineMessage *polyLine = (PolyLineMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = polyLine -> mode; PutULONG(polyLine -> drawable, buffer + 4, bigEndian); PutULONG(polyLine -> gcontext, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void PolyLineStore::dumpIdentity(const Message *message) const { #ifdef DUMP PolyLineMessage *polyLine = (PolyLineMessage *) message; *logofs << name() << ": Identity drawable " << polyLine -> drawable << ", gcontext " << polyLine -> gcontext << ", size " << polyLine -> size_ << ".\n" << logofs_flush; #endif } void PolyLineStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { if (control -> isProtoStep8() == 1) { md5_append(md5_state_, buffer + 1, 1); } } void PolyLineStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { PolyLineMessage *polyLine = (PolyLineMessage *) message; PolyLineMessage *cachedPolyLine = (PolyLineMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; if (control -> isProtoStep8() == 0) { encodeBuffer.encodeBoolValue((unsigned int) polyLine -> mode); } #ifdef TEST *logofs << name() << ": Encoding value " << polyLine -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyLine -> drawable, clientCache -> drawableCache); cachedPolyLine -> drawable = polyLine -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << polyLine -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyLine -> gcontext, clientCache -> gcCache); cachedPolyLine -> gcontext = polyLine -> gcontext; } void PolyLineStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { PolyLineMessage *polyLine = (PolyLineMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; if (control -> isProtoStep8() == 0) { decodeBuffer.decodeBoolValue(value); polyLine -> mode = value; } decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); polyLine -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyLine -> drawable << " as drawable field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); polyLine -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyLine -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } nxcomp/FreeCache.h0000644000076400007640000000263211323113026014303 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef FreeCache_H #define FreeCache_H #include "IntCache.h" class FreeCache : public IntCache { public: FreeCache(unsigned int size) : IntCache(size) { } }; #endif /* FreeCache_H */ nxcomp/RenderCompositeCompat.h0000644000076400007640000000466711323113027016757 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderCompositeCompat_H #define RenderCompositeCompat_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderCompositeCompat" #undef MESSAGE_STORE #define MESSAGE_STORE RenderCompositeCompatStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 24 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderCompositeCompat_H */ nxcomp/CreateGC.h0000644000076400007640000001106111323113026014107 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef CreateGC_H #define CreateGC_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define CREATEGC_ENABLE_CACHE 1 #define CREATEGC_ENABLE_DATA 0 #define CREATEGC_ENABLE_SPLIT 0 #define CREATEGC_ENABLE_COMPRESS 0 #define CREATEGC_DATA_LIMIT 144 #define CREATEGC_DATA_OFFSET 16 #define CREATEGC_CACHE_SLOTS 2000 #define CREATEGC_CACHE_THRESHOLD 2 #define CREATEGC_CACHE_LOWER_THRESHOLD 1 // // The message class. // class CreateGCMessage : public Message { friend class CreateGCStore; public: CreateGCMessage() { } ~CreateGCMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int gcontext; unsigned int drawable; unsigned int value_mask; }; class CreateGCStore : public MessageStore { // // Constructors and destructors. // public: CreateGCStore() : MessageStore() { enableCache = CREATEGC_ENABLE_CACHE; enableData = CREATEGC_ENABLE_DATA; enableSplit = CREATEGC_ENABLE_SPLIT; enableCompress = CREATEGC_ENABLE_COMPRESS; dataLimit = CREATEGC_DATA_LIMIT; dataOffset = CREATEGC_DATA_OFFSET; cacheSlots = CREATEGC_CACHE_SLOTS; cacheThreshold = CREATEGC_CACHE_THRESHOLD; cacheLowerThreshold = CREATEGC_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~CreateGCStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "CreateGC"; } virtual unsigned char opcode() const { return X_CreateGC; } virtual unsigned int storage() const { return sizeof(CreateGCMessage); } // // Message handling methods. // public: virtual Message *create() const { return new CreateGCMessage(); } virtual Message *create(const Message &message) const { return new CreateGCMessage((const CreateGCMessage &) message); } virtual void destroy(Message *message) const { delete (CreateGCMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* CreateGC_H */ nxcomp/NXrender.h0000644000076400007640000000526311323113027014227 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef NXrender_H #define NXrender_H #ifdef __cplusplus extern "C" { #endif /* * Import this from * to compile under old XFree86 distributions * when render extension was not present yet. */ #define X_RenderQueryVersion 0 #define X_RenderQueryPictFormats 1 #define X_RenderQueryPictIndexValues 2 #define X_RenderQueryDithers 3 #define X_RenderCreatePicture 4 #define X_RenderChangePicture 5 #define X_RenderSetPictureClipRectangles 6 #define X_RenderFreePicture 7 #define X_RenderComposite 8 #define X_RenderScale 9 #define X_RenderTrapezoids 10 #define X_RenderTriangles 11 #define X_RenderTriStrip 12 #define X_RenderTriFan 13 #define X_RenderColorTrapezoids 14 #define X_RenderColorTriangles 15 #define X_RenderTransform 16 #define X_RenderCreateGlyphSet 17 #define X_RenderReferenceGlyphSet 18 #define X_RenderFreeGlyphSet 19 #define X_RenderAddGlyphs 20 #define X_RenderAddGlyphsFromPicture 21 #define X_RenderFreeGlyphs 22 #define X_RenderCompositeGlyphs8 23 #define X_RenderCompositeGlyphs16 24 #define X_RenderCompositeGlyphs32 25 #define X_RenderFillRectangles 26 /* 0.5 */ #define X_RenderCreateCursor 27 /* 0.6 */ #define X_RenderSetPictureTransform 28 #define X_RenderQueryFilters 29 #define X_RenderSetPictureFilter 30 #define X_RenderCreateAnimCursor 31 #ifdef __cplusplus } #endif #endif /* NXrender_H */ nxcomp/CreatePixmap.h0000644000076400007640000001054211323113027015060 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef CreatePixmap_H #define CreatePixmap_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define CREATEPIXMAP_ENABLE_CACHE 1 #define CREATEPIXMAP_ENABLE_DATA 0 #define CREATEPIXMAP_ENABLE_SPLIT 0 #define CREATEPIXMAP_ENABLE_COMPRESS 0 #define CREATEPIXMAP_DATA_LIMIT 16 #define CREATEPIXMAP_DATA_OFFSET 16 #define CREATEPIXMAP_CACHE_SLOTS 1000 #define CREATEPIXMAP_CACHE_THRESHOLD 2 #define CREATEPIXMAP_CACHE_LOWER_THRESHOLD 1 // // The message class. // class CreatePixmapMessage : public Message { friend class CreatePixmapStore; public: CreatePixmapMessage() { } ~CreatePixmapMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char depth; unsigned int id; unsigned int drawable; unsigned short width; unsigned short height; }; class CreatePixmapStore : public MessageStore { public: CreatePixmapStore(); virtual ~CreatePixmapStore(); virtual const char *name() const { return "CreatePixmap"; } virtual unsigned char opcode() const { return X_CreatePixmap; } virtual unsigned int storage() const { return sizeof(CreatePixmapMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new CreatePixmapMessage(); } virtual Message *create(const Message &message) const { return new CreatePixmapMessage((const CreatePixmapMessage &) message); } virtual void destroy(Message *message) const { delete (CreatePixmapMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* CreatePixmap_H */ nxcomp/SetUnpackColormap.h0000644000076400007640000001137111323113027016071 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef SetUnpackColormap_H #define SetUnpackColormap_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define SETUNPACKCOLORMAP_ENABLE_CACHE 1 #define SETUNPACKCOLORMAP_ENABLE_DATA 1 #define SETUNPACKCOLORMAP_ENABLE_SPLIT 1 #define SETUNPACKCOLORMAP_ENABLE_COMPRESS 1 #define SETUNPACKCOLORMAP_DATA_LIMIT 4096 #define SETUNPACKCOLORMAP_DATA_OFFSET 8 #define SETUNPACKCOLORMAP_CACHE_SLOTS 2000 #define SETUNPACKCOLORMAP_CACHE_THRESHOLD 5 #define SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD 0 #define SETUNPACKCOLORMAP_DATA_OFFSET_IF_PROTO_STEP_7 16 #define SETUNPACKCOLORMAP_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 #define SETUNPACKCOLORMAP_ENABLE_SPLIT_IF_PROTO_STEP_8 0 // // The message class. // class SetUnpackColormapMessage : public Message { friend class SetUnpackColormapStore; public: SetUnpackColormapMessage() { } ~SetUnpackColormapMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char client; unsigned char method; unsigned int src_length; unsigned int dst_length; }; class SetUnpackColormapStore : public MessageStore { public: SetUnpackColormapStore(StaticCompressor *compressor); virtual ~SetUnpackColormapStore(); virtual const char *name() const { return "SetUnpackColormap"; } virtual unsigned char opcode() const { return X_NXSetUnpackColormap; } virtual unsigned int storage() const { return sizeof(SetUnpackColormapMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new SetUnpackColormapMessage(); } virtual Message *create(const Message &message) const { return new SetUnpackColormapMessage((const SetUnpackColormapMessage &) message); } virtual void destroy(Message *message) const { delete (SetUnpackColormapMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* SetUnpackColormap_H */ nxcomp/ChannelCache.h0000644000076400007640000000361311323113030014765 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ChannelCache_H #define ChannelCache_H // // Elements in array of caches used in TextCompressor. // const unsigned int CLIENT_TEXT_CACHE_SIZE = 9999; const unsigned int SERVER_TEXT_CACHE_SIZE = 9999; // // Sizes of optional fields for ConfigureWindow // request. // extern const unsigned int CONFIGUREWINDOW_FIELD_WIDTH[7]; // // Sizes of optional fields for CreateGC request. // extern const unsigned int CREATEGC_FIELD_WIDTH[23]; // // This is just needed to provide a pointer // to the base cache class in encoding and // decoding procedures of message stores. // class ChannelCache { public: ChannelCache() { } ~ChannelCache() { } }; #endif /* ChannelCache_H */ nxcomp/ChannelCache.cpp0000644000076400007640000000404311323113027015324 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ChannelCache.h" const unsigned int CONFIGUREWINDOW_FIELD_WIDTH[7] = { 16, // x 16, // y 16, // width 16, // height 16, // border width 29, // sibling window 3 // stack mode }; const unsigned int CREATEGC_FIELD_WIDTH[23] = { 4, // function 32, // plane mask 32, // foreground 32, // background 16, // line width 2, // line style 2, // cap style 2, // join style 2, // fill style 1, // fill rule 29, // tile 29, // stipple 16, // tile/stipple x origin 16, // tile/stipple y origin 29, // font 1, // subwindow mode 1, // graphics exposures 16, // clip x origin 16, // clip y origin 29, // clip mask 16, // card offset 8, // dashes 1 // arc mode }; nxcomp/SendEvent.cpp0000644000076400007640000002156411323113027014732 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "SendEvent.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "IntCache.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int SendEventStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { SendEventMessage *sendEvent = (SendEventMessage *) message; // // Here is the fingerprint. // sendEvent -> propagate = *(buffer + 1); sendEvent -> window = GetULONG(buffer + 4, bigEndian); sendEvent -> mask = GetULONG(buffer + 8, bigEndian); sendEvent -> code = *(buffer + 12); sendEvent -> byte_data = *(buffer + 13); sendEvent -> sequence = GetUINT(buffer + 14, bigEndian); sendEvent -> int_data = GetULONG(buffer + 16, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int SendEventStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { SendEventMessage *sendEvent = (SendEventMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = sendEvent -> propagate; PutULONG(sendEvent -> window, buffer + 4, bigEndian); PutULONG(sendEvent -> mask, buffer + 8, bigEndian); *(buffer + 12) = sendEvent -> code; *(buffer + 13) = sendEvent -> byte_data; PutUINT(sendEvent -> sequence, buffer + 14, bigEndian); PutULONG(sendEvent -> int_data, buffer + 16, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void SendEventStore::dumpIdentity(const Message *message) const { #ifdef DUMP SendEventMessage *sendEvent = (SendEventMessage *) message; *logofs << name() << ": Identity propagate " << (unsigned int) sendEvent -> propagate << ", window " << sendEvent -> window << ", mask " << sendEvent -> mask << ", code " << (unsigned int) sendEvent -> code << ", byte_data " << (unsigned int) sendEvent -> byte_data << ", sequence " << sendEvent -> sequence << ", int_data " << sendEvent -> int_data << ", size " << sendEvent -> size_ << ".\n" << logofs_flush; #endif } void SendEventStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { } void SendEventStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { SendEventMessage *sendEvent = (SendEventMessage *) message; SendEventMessage *cachedSendEvent = (SendEventMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << (unsigned int) sendEvent -> propagate << " as propagate field.\n" << logofs_flush; #endif encodeBuffer.encodeBoolValue(sendEvent -> propagate); cachedSendEvent -> propagate = sendEvent -> propagate; #ifdef TEST *logofs << name() << ": Encoding value " << sendEvent -> window << " as window field.\n" << logofs_flush; #endif if (sendEvent -> window == 0 || sendEvent -> window == 1) { encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeBoolValue(sendEvent -> window); } else { encodeBuffer.encodeBoolValue(0); encodeBuffer.encodeXidValue(sendEvent -> window, clientCache -> windowCache); } cachedSendEvent -> window = sendEvent -> window; #ifdef TEST *logofs << name() << ": Encoding value " << sendEvent -> mask << " as mask field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(sendEvent -> mask, 32, clientCache -> sendEventMaskCache); cachedSendEvent -> mask = sendEvent -> mask; #ifdef TEST *logofs << name() << ": Encoding value " << sendEvent -> code << " as code field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(sendEvent -> code, 8, clientCache -> sendEventCodeCache); cachedSendEvent -> code = sendEvent -> code; #ifdef TEST *logofs << name() << ": Encoding value " << sendEvent -> byte_data << " as byte_data field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(sendEvent -> byte_data, 8, clientCache -> sendEventByteDataCache); cachedSendEvent -> byte_data = sendEvent -> byte_data; #ifdef TEST *logofs << name() << ": Encoding value " << sendEvent -> sequence << " as sequence field.\n" << logofs_flush; #endif unsigned int diffSeq = sendEvent -> sequence - clientCache -> sendEventLastSequence; clientCache -> sendEventLastSequence = sendEvent -> sequence; encodeBuffer.encodeValue(diffSeq, 16, 4); cachedSendEvent -> sequence = sendEvent -> sequence; #ifdef TEST *logofs << name() << ": Encoding value " << sendEvent -> int_data << " as int_data field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(sendEvent -> int_data, 32, clientCache -> sendEventIntDataCache); cachedSendEvent -> int_data = sendEvent -> int_data; } void SendEventStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { SendEventMessage *sendEvent = (SendEventMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeBoolValue(value); sendEvent -> propagate = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << (unsigned int) sendEvent -> propagate << " as propagate field.\n" << logofs_flush; #endif decodeBuffer.decodeBoolValue(value); if (value) { decodeBuffer.decodeBoolValue(value); } else { decodeBuffer.decodeXidValue(value, clientCache -> windowCache); } sendEvent -> window = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << sendEvent -> window << " as window field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(sendEvent -> mask, 32, clientCache -> sendEventMaskCache); #ifdef DEBUG *logofs << name() << ": Decoded value " << sendEvent -> mask << " as mask field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(sendEvent -> code, 8, clientCache -> sendEventCodeCache); #ifdef DEBUG *logofs << name() << ": Decoded value " << sendEvent -> code << " as code field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(sendEvent -> byte_data, 8, clientCache -> sendEventByteDataCache); #ifdef DEBUG *logofs << name() << ": Decoded value " << sendEvent -> byte_data << " as byte_data field.\n" << logofs_flush; #endif decodeBuffer.decodeValue(value, 16, 4); clientCache -> sendEventLastSequence += value; clientCache -> sendEventLastSequence &= 0xffff; sendEvent -> sequence = clientCache -> sendEventLastSequence; #ifdef DEBUG *logofs << name() << ": Decoded value " << sendEvent -> sequence << " as sequence field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(sendEvent -> int_data, 32, clientCache -> sendEventIntDataCache); #ifdef DEBUG *logofs << name() << ": Decoded value " << sendEvent -> int_data << " as int_data field.\n" << logofs_flush; #endif } nxcomp/RenderCreatePicture.h0000644000076400007640000000465511323113030016377 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderCreatePicture_H #define RenderCreatePicture_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderCreatePicture" #undef MESSAGE_STORE #define MESSAGE_STORE RenderCreatePictureStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 20 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderCreatePicture_H */ nxcomp/ProxyReadBuffer.h0000644000076400007640000000362411323113030015542 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ProxyReadBuffer_H #define ProxyReadBuffer_H #include "ReadBuffer.h" #include "Control.h" class ProxyReadBuffer : public ReadBuffer { public: ProxyReadBuffer(Transport *transport) : ReadBuffer(transport) { } virtual ~ProxyReadBuffer() { } protected: virtual unsigned int suggestedLength(unsigned int pendingLength); virtual int locateMessage(const unsigned char *start, const unsigned char *end, unsigned int &controlLength, unsigned int &dataLength, unsigned int &trailerLength); }; #endif /* ProxyReadBuffer_H */ nxcomp/ClientReadBuffer.h0000644000076400007640000000406211323113026015641 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ClientReadBuffer_H #define ClientReadBuffer_H #include "Control.h" #include "ReadBuffer.h" class ClientChannel; class ClientReadBuffer : public ReadBuffer { public: ClientReadBuffer(Transport *transport, ClientChannel *channel) : ReadBuffer(transport), firstMessage_(1), channel_(channel) { } virtual ~ClientReadBuffer() { } protected: virtual unsigned int suggestedLength(unsigned int pendingLength); virtual int locateMessage(const unsigned char *start, const unsigned char *end, unsigned int &controlLength, unsigned int &dataLength, unsigned int &trailerLength); int bigEndian_; int firstMessage_; ClientChannel *channel_; }; #endif /* ClientReadBuffer_H */ nxcomp/ConfigureWindow.h0000644000076400007640000001063211323113030015601 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ConfigureWindow_H #define ConfigureWindow_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define CONFIGUREWINDOW_ENABLE_CACHE 1 #define CONFIGUREWINDOW_ENABLE_DATA 0 #define CONFIGUREWINDOW_ENABLE_SPLIT 0 #define CONFIGUREWINDOW_ENABLE_COMPRESS 0 #define CONFIGUREWINDOW_DATA_LIMIT 32 #define CONFIGUREWINDOW_DATA_OFFSET 12 #define CONFIGUREWINDOW_CACHE_SLOTS 3000 #define CONFIGUREWINDOW_CACHE_THRESHOLD 5 #define CONFIGUREWINDOW_CACHE_LOWER_THRESHOLD 1 // // The message class. // class ConfigureWindowMessage : public Message { friend class ConfigureWindowStore; public: ConfigureWindowMessage() { } ~ConfigureWindowMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int window; unsigned short value_mask; }; class ConfigureWindowStore : public MessageStore { // // Constructors and destructors. // public: ConfigureWindowStore() : MessageStore() { enableCache = CONFIGUREWINDOW_ENABLE_CACHE; enableData = CONFIGUREWINDOW_ENABLE_DATA; enableSplit = CONFIGUREWINDOW_ENABLE_SPLIT; enableCompress = CONFIGUREWINDOW_ENABLE_COMPRESS; dataLimit = CONFIGUREWINDOW_DATA_LIMIT; dataOffset = CONFIGUREWINDOW_DATA_OFFSET; cacheSlots = CONFIGUREWINDOW_CACHE_SLOTS; cacheThreshold = CONFIGUREWINDOW_CACHE_THRESHOLD; cacheLowerThreshold = CONFIGUREWINDOW_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~ConfigureWindowStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "ConfigureWindow"; } virtual unsigned char opcode() const { return X_ConfigureWindow; } virtual unsigned int storage() const { return sizeof(ConfigureWindowMessage); } // // Message handling methods. // public: virtual Message *create() const { return new ConfigureWindowMessage(); } virtual Message *create(const Message &message) const { return new ConfigureWindowMessage((const ConfigureWindowMessage &) message); } virtual void destroy(Message *message) const { delete (ConfigureWindowMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* ConfigureWindow_H */ nxcomp/GenericRequest.h0000644000076400007640000001117411323113030015417 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef GenericRequest_H #define GenericRequest_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define GENERICREQUEST_ENABLE_CACHE 1 #define GENERICREQUEST_ENABLE_DATA 1 #define GENERICREQUEST_ENABLE_SPLIT 0 #define GENERICREQUEST_ENABLE_COMPRESS 1 #define GENERICREQUEST_DATA_LIMIT 262144 - 20 #define GENERICREQUEST_DATA_OFFSET 20 #define GENERICREQUEST_CACHE_SLOTS 400 #define GENERICREQUEST_CACHE_THRESHOLD 5 #define GENERICREQUEST_CACHE_LOWER_THRESHOLD 1 #define GENERICREQUEST_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 // // The message class. // class GenericRequestMessage : public Message { friend class GenericRequestStore; public: GenericRequestMessage() { } ~GenericRequestMessage() { } // // Note that we consider for this message a data offset // of 4 (or 20 starting from protocol 3). Bytes from 9 // to 20, if present, are taken as part of identity and // encoded through an array of int caches. // private: unsigned char opcode; unsigned short data[8]; }; class GenericRequestStore : public MessageStore { public: GenericRequestStore(StaticCompressor *compressor); virtual ~GenericRequestStore(); virtual const char *name() const { return "GenericRequest"; } virtual unsigned char opcode() const { return X_NXInternalGenericRequest; } virtual unsigned int storage() const { return sizeof(GenericRequestMessage); } // // Message handling methods. // public: virtual Message *create() const { return new GenericRequestMessage(); } virtual Message *create(const Message &message) const { return new GenericRequestMessage((const GenericRequestMessage &) message); } virtual void destroy(Message *message) const { delete (GenericRequestMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* GenericRequest_H */ nxcomp/RenderPictureClip.cpp0000644000076400007640000002137211323113027016417 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderPictureClip.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { // // The data is constituted by a number of // rectangles. Each rectangle is in the // format x, y, width, height with 2 bytes // per each field, so each request is at // least 12 + 8 = 20 bytes long. // ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 8, bigEndian), clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 10, bigEndian), clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 4, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); PutUINT(clientCache -> renderLastX, buffer + 8, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); PutUINT(clientCache -> renderLastY, buffer + 10, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.picture_clip.type = *(buffer + 1); renderExtension -> data.picture_clip.src_id = GetULONG(buffer + 4, bigEndian); renderExtension -> data.picture_clip.src_x = GetUINT(buffer + 8, bigEndian); renderExtension -> data.picture_clip.src_y = GetUINT(buffer + 10, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.picture_clip.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.picture_clip.type; PutULONG(renderExtension -> data.picture_clip.src_id, buffer + 4, bigEndian); PutUINT(renderExtension -> data.picture_clip.src_x, buffer + 8, bigEndian); PutUINT(renderExtension -> data.picture_clip.src_y, buffer + 10, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.picture_clip.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { // // Encode the picture id and the // source x and y differentially. // md5_append(md5_state, buffer + 1, 3); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(renderExtension -> data.picture_clip.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.picture_clip.src_id = renderExtension -> data.picture_clip.src_id; // // The source x and y coordinates are // encoded as differerences in respect // to the previous cached value. // unsigned int value; unsigned int previous; value = renderExtension -> data.picture_clip.src_x; previous = cachedRenderExtension -> data.picture_clip.src_x; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderXCache, 11); cachedRenderExtension -> data.picture_clip.src_x = value; value = renderExtension -> data.picture_clip.src_y; previous = cachedRenderExtension -> data.picture_clip.src_y; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderYCache, 11); cachedRenderExtension -> data.picture_clip.src_y = value; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.picture_clip.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.picture_clip.src_id, clientCache -> renderSrcPictureCache); unsigned int value; unsigned int previous; previous = renderExtension -> data.picture_clip.src_x; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderXCache, 11); renderExtension -> data.picture_clip.src_x = value; previous = renderExtension -> data.picture_clip.src_y; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderYCache, 11); renderExtension -> data.picture_clip.src_y = value; #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.picture_clip.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/GetPropertyReply.cpp0000644000076400007640000002037211323113030016325 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "GetPropertyReply.h" #include "ServerCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // GetPropertyReplyStore::GetPropertyReplyStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = GETPROPERTYREPLY_ENABLE_CACHE; enableData = GETPROPERTYREPLY_ENABLE_DATA; enableSplit = GETPROPERTYREPLY_ENABLE_SPLIT; enableCompress = GETPROPERTYREPLY_ENABLE_COMPRESS; if (control -> isProtoStep7() == 1) { enableCompress = GETPROPERTYREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7; } dataLimit = GETPROPERTYREPLY_DATA_LIMIT; dataOffset = GETPROPERTYREPLY_DATA_OFFSET; cacheSlots = GETPROPERTYREPLY_CACHE_SLOTS; cacheThreshold = GETPROPERTYREPLY_CACHE_THRESHOLD; cacheLowerThreshold = GETPROPERTYREPLY_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } GetPropertyReplyStore::~GetPropertyReplyStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int GetPropertyReplyStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ServerCache *serverCache = (ServerCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif unsigned char format = (unsigned int) *(buffer + 1); encodeBuffer.encodeCachedValue(format, 8, serverCache -> getPropertyFormatCache); unsigned int numBytes = GetULONG(buffer + 16, bigEndian); encodeBuffer.encodeValue(numBytes, 32, 9); if (format == 16) { numBytes <<= 1; } else if (format == 32) { numBytes <<= 2; } encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 29, serverCache -> getPropertyTypeCache, 9); encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9); #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int GetPropertyReplyStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ServerCache *serverCache = (ServerCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif unsigned char format; decodeBuffer.decodeCachedValue(format, 8, serverCache -> getPropertyFormatCache); unsigned int length; decodeBuffer.decodeValue(length, 32, 9); unsigned int numBytes = length; if (format == 16) { numBytes <<= 1; } else if (format == 32) { numBytes <<= 2; } size = 32 + RoundUp4(numBytes); buffer = writeBuffer -> addMessage(size); *(buffer + 1) = format; PutULONG(length, buffer + 16, bigEndian); unsigned int value; decodeBuffer.decodeCachedValue(value, 29, serverCache -> getPropertyTypeCache, 9); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeValue(value, 32, 9); PutULONG(value, buffer + 12, bigEndian); #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int GetPropertyReplyStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message; getPropertyReply -> format = *(buffer + 1); getPropertyReply -> type = GetULONG(buffer + 8, bigEndian); getPropertyReply -> after = GetULONG(buffer + 12, bigEndian); getPropertyReply -> items = GetULONG(buffer + 16, bigEndian); // // Cleanup the padding bytes. // unsigned int uiLengthInBytes; unsigned int uiFormat; if ((int) size > GETPROPERTYREPLY_DATA_OFFSET) { uiLengthInBytes = getPropertyReply -> items; uiFormat = *(buffer + 1); #ifdef DEBUG *logofs << name() << ": length " << uiLengthInBytes << ", format " << uiFormat << ", size " << size << ".\n" << logofs_flush; #endif if (uiFormat == 16) { uiLengthInBytes <<= 1; } else if (uiFormat == 32) { uiLengthInBytes <<= 2; } unsigned char *end = ((unsigned char *) buffer) + size; unsigned char *pad = ((unsigned char *) buffer) + GETPROPERTYREPLY_DATA_OFFSET + uiLengthInBytes; CleanData((unsigned char *) pad, end - pad); } #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int GetPropertyReplyStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message; *(buffer + 1) = getPropertyReply -> format; PutULONG(getPropertyReply -> type, buffer + 8, bigEndian); PutULONG(getPropertyReply -> after, buffer + 12, bigEndian); PutULONG(getPropertyReply -> items, buffer + 16, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void GetPropertyReplyStore::dumpIdentity(const Message *message) const { #ifdef DUMP GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message; *logofs << name() << ": Identity format " << (unsigned) getPropertyReply -> format << ", type " << getPropertyReply -> type << ", after " << getPropertyReply -> after << ", items " << getPropertyReply -> items << ", size " << getPropertyReply -> size_ << ".\n"; #endif } void GetPropertyReplyStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // Fields format, type, after, items. // md5_append(md5_state_, buffer + 1, 1); md5_append(md5_state_, buffer + 8, 12); } void GetPropertyReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { } void GetPropertyReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { } nxcomp/SetClipRectangles.cpp0000644000076400007640000001211511323113031016375 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "SetClipRectangles.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int SetClipRectanglesStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message; // // Here is the fingerprint. // setClipRectangles -> ordering = *(buffer + 1); setClipRectangles -> gcontext = GetULONG(buffer + 4, bigEndian); setClipRectangles -> x_origin = GetUINT(buffer + 8, bigEndian); setClipRectangles -> y_origin = GetUINT(buffer + 10, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int SetClipRectanglesStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = setClipRectangles -> ordering; PutULONG(setClipRectangles -> gcontext, buffer + 4, bigEndian); PutUINT(setClipRectangles -> x_origin, buffer + 8, bigEndian); PutUINT(setClipRectangles -> y_origin, buffer + 10, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void SetClipRectanglesStore::dumpIdentity(const Message *message) const { #ifdef DUMP SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message; *logofs << name() << ": Identity ordering " << (unsigned int) setClipRectangles -> ordering << ", gcontext " << setClipRectangles -> gcontext << ", x_origin " << setClipRectangles -> x_origin << ", y_origin " << setClipRectangles -> y_origin << ", size " << setClipRectangles -> size_ << ".\n" << logofs_flush; #endif } void SetClipRectanglesStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 1, 1); md5_append(md5_state_, buffer + 8, 4); } void SetClipRectanglesStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message; SetClipRectanglesMessage *cachedSetClipRectangles = (SetClipRectanglesMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << setClipRectangles -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(setClipRectangles -> gcontext, clientCache -> gcCache); cachedSetClipRectangles -> gcontext = setClipRectangles -> gcontext; } void SetClipRectanglesStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> gcCache); setClipRectangles -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << setClipRectangles -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } nxcomp/RenderChangePicture.h0000644000076400007640000000465411323113031016361 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderChangePicture_H #define RenderChangePicture_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderChangePicture" #undef MESSAGE_STORE #define MESSAGE_STORE RenderChangePictureStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 8 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderChangePicture_H */ nxcomp/ServerProxy.cpp0000644000076400007640000003543311571456433015367 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include "NXalert.h" #include "Socket.h" #include "ServerProxy.h" #include "ServerChannel.h" #include "GenericChannel.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Log the operations related to sending // and receiving the control tokens. // #undef TOKEN ServerProxy::ServerProxy(int proxyFd) : Proxy(proxyFd) { xServerAddrFamily_ = -1; xServerAddrLength_ = 0; xServerAddr_ = NULL; xServerDisplay_ = NULL; cupsServerPort_ = -1; smbServerPort_ = -1; mediaServerPort_ = -1; httpServerPort_ = -1; fontServerPort_ = NULL; #ifdef DEBUG *logofs << "ServerProxy: Created new object at " << this << ".\n" << logofs_flush; #endif } ServerProxy::~ServerProxy() { delete xServerAddr_; delete [] xServerDisplay_; delete [] fontServerPort_; #ifdef DEBUG *logofs << "ServerProxy: Deleted object at " << this << ".\n" << logofs_flush; #endif } void ServerProxy::handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily, sockaddr *xServerAddr, unsigned int xServerAddrLength) { delete xServerAddr_; xServerAddr_ = xServerAddr; xServerAddrFamily_ = xServerAddrFamily; xServerAddrLength_ = xServerAddrLength; delete [] xServerDisplay_; xServerDisplay_ = new char[strlen(xServerDisplay) + 1]; strcpy(xServerDisplay_, xServerDisplay); #ifdef DEBUG *logofs << "ServerProxy: Set display configuration to display '" << xServerDisplay_ << "'.\n" << logofs_flush; #endif } void ServerProxy::handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort, int httpServerPort, const char *fontServerPort) { cupsServerPort_ = cupsServerPort; smbServerPort_ = smbServerPort; mediaServerPort_ = mediaServerPort; httpServerPort_ = httpServerPort; delete [] fontServerPort_; fontServerPort_ = new char[strlen(fontServerPort) + 1]; strcpy(fontServerPort_, fontServerPort); #ifdef DEBUG *logofs << "ServerProxy: Set port configuration to CUPS " << cupsServerPort_ << ", SMB " << smbServerPort_ << ", media " << mediaServerPort_ << ", HTTP " << httpServerPort_ << ".\n" << logofs_flush; #endif } int ServerProxy::handleNewConnection(T_channel_type type, int clientFd) { switch (type) { case channel_font: { return handleNewGenericConnection(clientFd, channel_font, "font"); } case channel_slave: { return handleNewSlaveConnection(clientFd); } default: { #ifdef PANIC *logofs << "ServerProxy: PANIC! Unsupported channel with type '" << getTypeName(type) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Unsupported channel with type '" << getTypeName(type) << "'.\n"; return -1; } } } int ServerProxy::handleNewConnectionFromProxy(T_channel_type type, int channelId) { switch (type) { case channel_x11: { return handleNewXConnectionFromProxy(channelId); } case channel_cups: { return handleNewGenericConnectionFromProxy(channelId, channel_cups, "localhost", cupsServerPort_, "CUPS"); } case channel_smb: { return handleNewGenericConnectionFromProxy(channelId, channel_smb, getComputerName(), smbServerPort_, "SMB"); } case channel_media: { return handleNewGenericConnectionFromProxy(channelId, channel_media, "localhost", mediaServerPort_, "media"); } case channel_http: { return handleNewGenericConnectionFromProxy(channelId, channel_http, getComputerName(), httpServerPort_, "HTTP"); } case channel_slave: { return handleNewSlaveConnectionFromProxy(channelId); } default: { #ifdef PANIC *logofs << "ServerProxy: PANIC! Unsupported channel with type '" << getTypeName(type) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Unsupported channel with type '" << getTypeName(type) << "'.\n"; return -1; } } } int ServerProxy::handleNewAgentConnection(Agent *agent) { #ifdef PANIC *logofs << "ServerProxy: PANIC! Can't create an agent " << "connection at this side.\n" << logofs_flush; #endif cerr << "Error" << ": Can't create an agent " << "connection at this side.\n"; return -1; } int ServerProxy::handleNewXConnection(int clientFd) { #ifdef PANIC *logofs << "ServerProxy: PANIC! Can't create a new X channel " << "with FD#" << clientFd << " at this side.\n" << logofs_flush; #endif cerr << "Error" << ": Can't create a new X channel " << "with FD#" << clientFd << " at this side.\n"; return -1; } int ServerProxy::handleNewXConnectionFromProxy(int channelId) { // // Connect to the real X server. // int retryConnect = control -> OptionServerRetryConnect; int xServerFd; for (;;) { xServerFd = socket(xServerAddrFamily_, SOCK_STREAM, PF_UNSPEC); if (xServerFd < 0) { #ifdef PANIC *logofs << "ServerProxy: PANIC! Call to socket failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to socket failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST *logofs << "ServerProxy: Trying to connect to X server '" << xServerDisplay_ << "'.\n" << logofs_flush; #endif int result = connect(xServerFd, xServerAddr_, xServerAddrLength_); getNewTimestamp(); if (result < 0) { #ifdef WARNING *logofs << "ServerProxy: WARNING! Connection to '" << xServerDisplay_ << "' failed with error '" << ESTR() << "'. Retrying.\n" << logofs_flush; #endif close(xServerFd); if (--retryConnect == 0) { #ifdef PANIC *logofs << "ServerProxy: PANIC! Connection to '" << xServerDisplay_ << "' for channel ID#" << channelId << " failed. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Connection to '" << xServerDisplay_ << "' failed. Error is " << EGET() << " '" << ESTR() << "'.\n"; close(xServerFd); return -1; } if (activeChannels_.getSize() == 0) { sleep(2); } else { sleep(1); } } else { break; } } assignChannelMap(channelId, xServerFd); #ifdef TEST *logofs << "ServerProxy: X server descriptor FD#" << xServerFd << " mapped to channel ID#" << channelId << ".\n" << logofs_flush; #endif // // Turn queuing off for path proxy-to-X-server. // if (control -> OptionServerNoDelay == 1) { SetNoDelay(xServerFd, control -> OptionServerNoDelay); } // // If requested, set the size of the TCP send // and receive buffers. // if (control -> OptionServerSendBuffer != -1) { SetSendBuffer(xServerFd, control -> OptionServerSendBuffer); } if (control -> OptionServerReceiveBuffer != -1) { SetReceiveBuffer(xServerFd, control -> OptionServerReceiveBuffer); } if (allocateTransport(xServerFd, channelId) < 0) { return -1; } // // Starting from protocol level 3 client and server // caches are created in proxy and shared between all // channels. If remote proxy has older protocol level // pointers are NULL and channels must create their // own instances. // channels_[channelId] = new ServerChannel(transports_[channelId], compressor_); if (channels_[channelId] == NULL) { deallocateTransport(channelId); return -1; } increaseChannels(channelId); // // Propagate channel stores and caches to the new // channel. // channels_[channelId] -> setOpcodes(opcodeStore_); channels_[channelId] -> setStores(clientStore_, serverStore_); channels_[channelId] -> setCaches(clientCache_, serverCache_); int port = atoi(fontServerPort_); if (port > 0) { channels_[channelId] -> setPorts(port); } // // Let channel configure itself according // to control parameters. // channels_[channelId] -> handleConfiguration(); // // Check if we have successfully loaded the // selected cache and, if not, remove it // from disk. // handleCheckLoad(); return 1; } // // Check if we still need to drop a channel. We need // to check this explicitly at the time we receive a // request to load or save the cache because we could // receive the control message before having entered // the function handling the channel events. // int ServerProxy::handleCheckDrop() { T_list channelList = activeChannels_.copyList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] != NULL && (channels_[channelId] -> getDrop() == 1 || channels_[channelId] -> getClosing() == 1)) { #ifdef TEST *logofs << "ServerProxy: Dropping the descriptor FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif handleDrop(channelId); } } return 1; } int ServerProxy::handleCheckLoad() { // // Check if we just created the first X channel // but the client side didn't tell us to load // the cache selected at the session negotiation. // This is very likely because the load operation // failed at the remote side, for example because // the cache was invalid or corrupted. // int channelCount = getChannels(channel_x11); if (channelCount != 1) { return 0; } if (control -> PersistentCacheEnableLoad == 1 && control -> PersistentCachePath != NULL && control -> PersistentCacheName != NULL && isTimestamp(timeouts_.loadTs) == 0) { #ifdef WARNING *logofs << "ServerProxy: WARNING! Cache file '" << control -> PersistentCachePath << "/" << control -> PersistentCacheName << "' not loaded.\n" << logofs_flush; #endif // // Remove the cache file. // #ifdef WARNING *logofs << "ServerProxy: WARNING! Removing supposedly " << "incompatible cache '" << control -> PersistentCachePath << "/" << control -> PersistentCacheName << "'.\n" << logofs_flush; #endif handleResetPersistentCache(); } return 1; } int ServerProxy::handleLoadFromProxy() { // // Be sure we drop any confirmed channel. // handleCheckDrop(); // // Check that either no X channel is // remaining or we are inside a reset. // int channelCount = getChannels(channel_x11); if (channelCount > 0) { #ifdef PANIC *logofs << "ServerProxy: PANIC! Protocol violation " << "in command load with " << channelCount << " channels.\n" << logofs_flush; #endif cerr << "Error" << ": Protocol violation " << "in command load from proxy.\n"; return -1; } else if (handleLoadStores() < 0) { #ifdef WARNING *logofs << "ServerProxy: WARNING! Failed to load content " << "of persistent cache.\n" << logofs_flush; #endif return -1; } return 1; } int ServerProxy::handleSaveFromProxy() { // // Be sure we drop any confirmed channel. // handleCheckDrop(); // // Now verify that all channels are gone. // int channelCount = getChannels(channel_x11); if (channelCount > 0) { #ifdef PANIC *logofs << "ServerProxy: PANIC! Protocol violation " << "in command save with " << channelCount << " channels.\n" << logofs_flush; #endif cerr << "Error" << ": Protocol violation " << "in command save from proxy.\n"; return -1; } else if (handleSaveStores() < 0) { #ifdef PANIC *logofs << "ServerProxy: PANIC! Failed to save stores " << "to persistent cache.\n" << logofs_flush; #endif return -1; } return 1; } int ServerProxy::handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient) const { if (clientStore_ -> saveRequestStores(cachefs, md5StateStream, md5StateClient, discard_checksum, use_data) < 0) { return -1; } else if (serverStore_ -> saveReplyStores(cachefs, md5StateStream, md5StateClient, use_checksum, discard_data) < 0) { return -1; } else if (serverStore_ -> saveEventStores(cachefs, md5StateStream, md5StateClient, use_checksum, discard_data) < 0) { return -1; } return 1; } int ServerProxy::handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const { if (clientStore_ -> loadRequestStores(cachefs, md5StateStream, discard_checksum, use_data) < 0) { return -1; } else if (serverStore_ -> loadReplyStores(cachefs, md5StateStream, use_checksum, discard_data) < 0) { return -1; } else if (serverStore_ -> loadEventStores(cachefs, md5StateStream, use_checksum, discard_data) < 0) { return -1; } return 1; } nxcomp/ChangeGCCompat.h0000644000076400007640000001056011323113031015234 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ChangeGCCompat_H #define ChangeGCCompat_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define CHANGEGC_ENABLE_CACHE 1 #define CHANGEGC_ENABLE_DATA 0 #define CHANGEGC_ENABLE_SPLIT 0 #define CHANGEGC_ENABLE_COMPRESS 0 #define CHANGEGC_DATA_LIMIT 144 #define CHANGEGC_DATA_OFFSET 12 #define CHANGEGC_CACHE_SLOTS 3000 #define CHANGEGC_CACHE_THRESHOLD 3 #define CHANGEGC_CACHE_LOWER_THRESHOLD 1 // // The message class. // class ChangeGCCompatMessage : public Message { friend class ChangeGCCompatStore; public: ChangeGCCompatMessage() { } ~ChangeGCCompatMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int gcontext; unsigned int value_mask; }; class ChangeGCCompatStore : public MessageStore { // // Constructors and destructors. // public: ChangeGCCompatStore() : MessageStore() { enableCache = CHANGEGC_ENABLE_CACHE; enableData = CHANGEGC_ENABLE_DATA; enableSplit = CHANGEGC_ENABLE_SPLIT; enableCompress = CHANGEGC_ENABLE_COMPRESS; dataLimit = CHANGEGC_DATA_LIMIT; dataOffset = CHANGEGC_DATA_OFFSET; cacheSlots = CHANGEGC_CACHE_SLOTS; cacheThreshold = CHANGEGC_CACHE_THRESHOLD; cacheLowerThreshold = CHANGEGC_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~ChangeGCCompatStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "ChangeGCCompat"; } virtual unsigned char opcode() const { return X_ChangeGC; } virtual unsigned int storage() const { return sizeof(ChangeGCCompatMessage); } // // Message handling methods. // public: virtual Message *create() const { return new ChangeGCCompatMessage(); } virtual Message *create(const Message &message) const { return new ChangeGCCompatMessage((const ChangeGCCompatMessage &) message); } virtual void destroy(Message *message) const { delete (ChangeGCCompatMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* ChangeGCCompat_H */ nxcomp/Keeper.h0000644000076400007640000000641511323113030013707 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Keeper_H #define Keeper_H #include "Misc.h" #include "Types.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Define this to check how many file // nodes are allocated and deallocated. // #undef REFERENCES class Keeper; class File { friend class Keeper; public: File(); ~File(); // // Allow sort by time and size. If time // is the same, keep the bigger element. // bool compare(File *b) const; private: char *name_; int size_; time_t time_; #ifdef REFERENCES static int references_; #endif }; class Keeper { public: Keeper(int caches, int images, const char *root, int sleep, int parent); ~Keeper(); // // Call this just once. // int cleanupCaches(); // // Call this at any given interval. // int cleanupImages(); // // Call this if it's time to exit. // void setSignal(int signal) { signal_ = signal; } int getSignal() { return signal_; } int getParent() { return parent_; } private: // // Get a list of files in directory. // int collect(const char *path); // // Sort the collected files according to // last modification time and delete the // older ones until disk size is below // the threshold. // int cleanup(int threshold); // // Empty the files repository. // void empty(); // // Size in bytes of total allowed // storage for persistent caches. // int caches_; // // Size in bytes of total allowed // storage for images cache. // int images_; // // Path of the NX root directory. // char *root_; // // The little delay to be introduced // before reading a new entry. // int sleep_; // // Total size of files in repository. // int total_; // // The parent process, so we can exit // if it is gone. // int parent_; // // Set if we need to give up because // of a signal. // int signal_; // // Repository where to collect files. // T_files *files_; }; #endif /* Keeper_H */ nxcomp/RenderChangePicture.cpp0000644000076400007640000001503711323113026016715 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderChangePicture.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> renderSrcPictureCache); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { #ifdef DEBUG if (size == MESSAGE_OFFSET + 4) { *logofs << name() << ": Mask is " << GetULONG(buffer + 8, bigEndian) << " value is " << GetULONG(buffer + 12, bigEndian) << ".\n" << logofs_flush; } else { *logofs << name() << ": WARNING! Unexpected size. Mask is " << GetULONG(buffer + 8, bigEndian) << ".\n" << logofs_flush; } #endif encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.change_picture.type = *(buffer + 1); renderExtension -> data.change_picture.src_id = GetULONG(buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.change_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.change_picture.type; PutULONG(renderExtension -> data.change_picture.src_id, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.change_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(renderExtension -> data.change_picture.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.change_picture.src_id = renderExtension -> data.change_picture.src_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.change_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.change_picture.src_id, clientCache -> renderSrcPictureCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.change_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/RenderPictureClipCompat.cpp0000644000076400007640000001640711323113030017560 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderPictureClipCompat.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 8, bigEndian), clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 10, bigEndian), clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 4, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); PutUINT(clientCache -> renderLastX, buffer + 8, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); PutUINT(clientCache -> renderLastY, buffer + 10, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.picture_clip.type = *(buffer + 1); renderExtension -> data.picture_clip.src_id = GetULONG(buffer + 4, bigEndian); renderExtension -> data.picture_clip.src_x = GetUINT(buffer + 8, bigEndian); renderExtension -> data.picture_clip.src_y = GetUINT(buffer + 10, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.picture_clip.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.picture_clip.type; PutULONG(renderExtension -> data.picture_clip.src_id, buffer + 4, bigEndian); PutUINT(renderExtension -> data.picture_clip.src_x, buffer + 8, bigEndian); PutUINT(renderExtension -> data.picture_clip.src_y, buffer + 10, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.picture_clip.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); md5_append(md5_state, buffer + 8, 4); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(renderExtension -> data.picture_clip.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.picture_clip.src_id = renderExtension -> data.picture_clip.src_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.picture_clip.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.picture_clip.src_id, clientCache -> renderSrcPictureCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.picture_clip.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/GetImageReply.h0000644000076400007640000000777311323113030015202 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef GetImageReply_H #define GetImageReply_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define GETIMAGEREPLY_ENABLE_CACHE 1 #define GETIMAGEREPLY_ENABLE_DATA 1 #define GETIMAGEREPLY_ENABLE_SPLIT 0 #define GETIMAGEREPLY_ENABLE_COMPRESS 1 #define GETIMAGEREPLY_DATA_LIMIT 1048576 - 32 #define GETIMAGEREPLY_DATA_OFFSET 32 #define GETIMAGEREPLY_CACHE_SLOTS 1000 #define GETIMAGEREPLY_CACHE_THRESHOLD 20 #define GETIMAGEREPLY_CACHE_LOWER_THRESHOLD 2 #define GETIMAGEREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 // // The message class. // class GetImageReplyMessage : public Message { friend class GetImageReplyStore; public: GetImageReplyMessage() { } ~GetImageReplyMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char depth; unsigned int visual; }; class GetImageReplyStore : public MessageStore { public: GetImageReplyStore(StaticCompressor *compressor); virtual ~GetImageReplyStore(); virtual const char *name() const { return "GetImageReply"; } virtual unsigned char opcode() const { return X_GetImage; } virtual unsigned int storage() const { return sizeof(GetImageReplyMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new GetImageReplyMessage(); } virtual Message *create(const Message &message) const { return new GetImageReplyMessage((const GetImageReplyMessage &) message); } virtual void destroy(Message *message) const { delete (GetImageReplyMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* GetImageReply_H */ nxcomp/ReadBuffer.h0000644000076400007640000000636611323113027014514 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ReadBuffer_H #define ReadBuffer_H #include "Misc.h" #include "Timestamp.h" #include "Transport.h" #define READ_BUFFER_DEFAULT_SIZE 8192 #define PANIC #define WARNING #undef TEST #undef DEBUG class ReadBuffer { public: ReadBuffer(Transport *transport); virtual ~ReadBuffer(); int readMessage(); void readMessage(const unsigned char *message, unsigned int length); const unsigned char *getMessage(unsigned int &dataLength) { unsigned int controlLength; return getMessage(controlLength, dataLength); } const unsigned char *getMessage(unsigned int &controlLength, unsigned int &dataLength); unsigned int getLength() const { return length_; } unsigned int getRemaining() const { return remaining_; } int setSize(int initialReadSize, int initialbufferSize); void fullReset(); // // Check if there is a complete // message in the buffer. // int checkMessage() { if (length_ == 0) { return 0; } else { unsigned int controlLength; unsigned int dataLength; unsigned int trailerLength; return (locateMessage(buffer_ + start_, buffer_ + start_ + length_, controlLength, dataLength, trailerLength)); } } protected: virtual unsigned int suggestedLength(unsigned int pendingLength) = 0; virtual int locateMessage(const unsigned char *start, const unsigned char *end, unsigned int &controlLength, unsigned int &dataLength, unsigned int &trailerLength) = 0; unsigned char *allocateBuffer(unsigned int newSize); void appendBuffer(const unsigned char *message, unsigned int length); void convertBuffer(); Transport *transport_; unsigned char *buffer_; unsigned int length_; unsigned int size_; unsigned int start_; unsigned int remaining_; int owner_; unsigned int initialReadSize_; unsigned int maximumBufferSize_; }; #endif /* ReadBuffer_H */ nxcomp/QueryFontReply.h0000644000076400007640000000706211323113031015444 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef QueryFontReply_H #define QueryFontReply_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define QUERYFONTREPLY_ENABLE_CACHE 1 #define QUERYFONTREPLY_ENABLE_DATA 1 #define QUERYFONTREPLY_ENABLE_SPLIT 0 #define QUERYFONTREPLY_ENABLE_COMPRESS 1 #define QUERYFONTREPLY_DATA_LIMIT 1048576 - 32 #define QUERYFONTREPLY_DATA_OFFSET 8 #define QUERYFONTREPLY_CACHE_SLOTS 200 #define QUERYFONTREPLY_CACHE_THRESHOLD 20 #define QUERYFONTREPLY_CACHE_LOWER_THRESHOLD 5 #define QUERYFONTREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 // // The message class. // class QueryFontReplyMessage : public Message { friend class QueryFontReplyStore; public: QueryFontReplyMessage() { } ~QueryFontReplyMessage() { } }; class QueryFontReplyStore : public MessageStore { // // Constructors and destructors. // public: QueryFontReplyStore(StaticCompressor *compressor); virtual ~QueryFontReplyStore(); virtual const char *name() const { return "QueryFontReply"; } virtual unsigned char opcode() const { return X_QueryFont; } virtual unsigned int storage() const { return sizeof(QueryFontReplyMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new QueryFontReplyMessage(); } virtual Message *create(const Message &message) const { return new QueryFontReplyMessage((const QueryFontReplyMessage &) message); } virtual void destroy(Message *message) const { delete (QueryFontReplyMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* QueryFontReply_H */ nxcomp/Proxy.cpp0000644000076400007640000044416711571461425014205 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include #include "Misc.h" #if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun) #include #endif #ifndef __CYGWIN32__ #include #endif #include #include #include #if defined(__EMX__ ) || defined(__CYGWIN32__) struct sockaddr_un { u_short sun_family; char sun_path[108]; }; #endif #include "NXalert.h" #include "NXvars.h" #include "Proxy.h" #include "Socket.h" #include "Channel.h" #include "Statistics.h" #include "ClientChannel.h" #include "ServerChannel.h" #include "GenericChannel.h" // // We need to adjust some values related // to these messages at the time the mes- // sage stores are reconfigured. // #include "PutImage.h" #include "ChangeGC.h" #include "PolyFillRectangle.h" #include "PutPackedImage.h" // // This is from the main loop. // extern void CleanupListeners(); // // Default size of string buffers. // #define DEFAULT_STRING_LENGTH 512 // // Set the verbosity level. You also need // to define DUMP in Misc.cpp if DUMP is // defined here. // #define WARNING #define PANIC #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 // // Log the operations related to sending // and receiving the control tokens. // #undef TOKEN // // Log the operations related to setting // the token limits. // #undef LIMIT // // Log a warning if no data is written by // the proxy within a timeout. // #undef TIME // // Log the operation related to generating // the ping message at idle time. // #undef PING Proxy::Proxy(int fd) : transport_(new ProxyTransport(fd)), fd_(fd), readBuffer_(transport_) { for (int channelId = 0; channelId < CONNECTIONS_LIMIT; channelId++) { channels_[channelId] = NULL; transports_[channelId] = NULL; congestions_[channelId] = 0; fdMap_[channelId] = nothing; channelMap_[channelId] = nothing; } inputChannel_ = nothing; outputChannel_ = nothing; controlLength_ = 0; operation_ = operation_in_negotiation; draining_ = 0; priority_ = 0; finish_ = 0; shutdown_ = 0; congestion_ = 0; timer_ = 0; alert_ = 0; agent_ = nothing; // // Set null timeouts. This will require // a new link configuration. // timeouts_.split = 0; timeouts_.motion = 0; timeouts_.readTs = getTimestamp(); timeouts_.writeTs = getTimestamp(); timeouts_.loopTs = getTimestamp(); timeouts_.pingTs = getTimestamp(); timeouts_.alertTs = nullTimestamp(); timeouts_.loadTs = nullTimestamp(); timeouts_.splitTs = nullTimestamp(); timeouts_.motionTs = nullTimestamp(); // // Initialize the token counters. This // will require a new link configuration. // for (int i = token_control; i <= token_data; i++) { tokens_[i].size = 0; tokens_[i].limit = 0; tokens_[i].bytes = 0; tokens_[i].remaining = 0; } tokens_[token_control].request = code_control_token_request; tokens_[token_control].reply = code_control_token_reply; tokens_[token_control].type = token_control; tokens_[token_split].request = code_split_token_request; tokens_[token_split].reply = code_split_token_reply; tokens_[token_split].type = token_split; tokens_[token_data].request = code_data_token_request; tokens_[token_data].reply = code_data_token_reply; tokens_[token_data].type = token_data; currentStatistics_ = NULL; // // Create compressor and decompressor // for image and data payload. // compressor_ = new StaticCompressor(control -> LocalDataCompressionLevel, control -> LocalDataCompressionThreshold); // // Create object storing NX specific // opcodes. // opcodeStore_ = new OpcodeStore(); // // Create the message stores. // clientStore_ = new ClientStore(compressor_); serverStore_ = new ServerStore(compressor_); // // Older proxies will refuse to store // messages bigger than 262144 bytes. // if (control -> isProtoStep7() == 0) { #ifdef TEST *logofs << "Proxy: WARNING! Limiting the maximum " << "message size to " << 262144 << ".\n" << logofs_flush; #endif control -> MaximumMessageSize = 262144; } clientCache_ = new ClientCache(); serverCache_ = new ServerCache(); if (clientCache_ == NULL || serverCache_ == NULL) { #ifdef PANIC *logofs << "Proxy: PANIC! Failed to create the channel cache.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to create the channel cache.\n"; HandleCleanup(); } // // Prepare for image decompression. // UnpackInit(); #ifdef DEBUG *logofs << "Proxy: Created new object at " << this << ".\n" << logofs_flush; #endif } Proxy::~Proxy() { for (int channelId = 0; channelId < CONNECTIONS_LIMIT; channelId++) { if (channels_[channelId] != NULL) { deallocateTransport(channelId); delete channels_[channelId]; channels_[channelId] = NULL; } } delete transport_; delete compressor_; // // Delete storage shared among channels. // delete opcodeStore_; delete clientStore_; delete serverStore_; delete clientCache_; delete serverCache_; // // Get rid of the image decompression // resources. // UnpackDestroy(); #ifdef DEBUG *logofs << "Proxy: Deleted proxy object at " << this << ".\n" << logofs_flush; #endif } int Proxy::setOperational() { #ifdef TEST *logofs << "Proxy: Entering operational mode.\n" << logofs_flush; #endif operation_ = operation_in_messages; return 1; } int Proxy::setReadDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax) { // // Set the initial timeout to the time of // the next ping. If the congestion count // is greater than zero, anyway, use a // shorter timeout to force a congestion // update. // if (agent_ != nothing && congestions_[agent_] == 0 && statistics -> getCongestionInFrame() >= 1 && tokens_[token_control].remaining >= (tokens_[token_control].limit - 1)) { setMinTimestamp(tsMax, control -> IdleTimeout); #ifdef TEST *logofs << "Proxy: Initial timeout is " << tsMax.tv_sec << " S and " << (double) tsMax.tv_usec / 1000 << " Ms with congestion " << statistics -> getCongestionInFrame() << ".\n" << logofs_flush; #endif } else { setMinTimestamp(tsMax, control -> PingTimeout); #ifdef TEST *logofs << "Proxy: Initial timeout is " << tsMax.tv_sec << " S and " << (double) tsMax.tv_usec / 1000 << " Ms.\n" << logofs_flush; #endif } int fd = -1; if (isTimeToRead() == 1) { // // If we don't have split tokens available // don't set the timeout. // if (tokens_[token_split].remaining > 0 && isTimestamp(timeouts_.splitTs) == 1) { int diffTs = getTimeToNextSplit(); #if defined(TEST) || defined(INFO) || \ defined(FLUSH) || defined(SPLIT) if (diffTimestamp(timeouts_.splitTs, getTimestamp()) > timeouts_.split) { *logofs << "Proxy: FLUSH! SPLIT! WARNING! Running with " << diffTimestamp(timeouts_.splitTs, getTimestamp()) << " Ms elapsed since the last split.\n" << logofs_flush; } *logofs << "Proxy: FLUSH! SPLIT! Requesting timeout of " << diffTs << " Ms as there are splits to send.\n" << logofs_flush; #endif setMinTimestamp(tsMax, diffTs); } #if defined(TEST) || defined(INFO) else if (isTimestamp(timeouts_.splitTs) == 1) { *logofs << "Proxy: WARNING! Not requesting a split " << "timeout with " << tokens_[token_split].remaining << " split tokens remaining.\n" << logofs_flush; } #endif // // Loop through the valid channels and set // the descriptors selected for read and // the timeout. // T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] == NULL) { continue; } fd = getFd(channelId); if (channels_[channelId] -> getFinish() == 0 && (channels_[channelId] -> getType() == channel_x11 || tokens_[token_data].remaining > 0) && congestions_[channelId] == 0) { FD_SET(fd, fdSet); if (fd >= fdMax) { fdMax = fd + 1; } #ifdef TEST *logofs << "Proxy: Descriptor FD#" << fd << " selected for read with buffer length " << transports_[channelId] -> length() << ".\n" << logofs_flush; #endif // // Wakeup the proxy if there are motion // events to flush. // if (isTimestamp(timeouts_.motionTs) == 1) { int diffTs = getTimeToNextMotion(); #if defined(TEST) || defined(INFO) if (diffTimestamp(timeouts_.motionTs, getTimestamp()) > timeouts_.motion) { *logofs << "Proxy: FLUSH! WARNING! Running with " << diffTimestamp(timeouts_.motionTs, getTimestamp()) << " Ms elapsed since the last motion.\n" << logofs_flush; } *logofs << "Proxy: FLUSH! Requesting timeout of " << diffTs << " Ms as FD#" << fd << " has motion " << "events to send.\n" << logofs_flush; #endif setMinTimestamp(tsMax, diffTs); } } #if defined(TEST) || defined(INFO) else { if (channels_[channelId] -> getType() != channel_x11 && tokens_[token_data].remaining <= 0) { *logofs << "Proxy: WARNING! Descriptor FD#" << fd << " not selected for read with " << tokens_[token_data].remaining << " data " << "tokens remaining.\n" << logofs_flush; } } #endif } } #if defined(TEST) || defined(INFO) else { *logofs << "Proxy: WARNING! Disabled reading from channels.\n" << logofs_flush; *logofs << "Proxy: WARNING! Congestion is " << congestion_ << " pending " << transport_ -> pending() << " blocked " << transport_ -> blocked() << " length " << transport_ -> length() << ".\n" << logofs_flush; } #endif // // Include the proxy descriptor. // FD_SET(fd_, fdSet); if (fd_ >= fdMax) { fdMax = fd_ + 1; } #ifdef TEST *logofs << "Proxy: Proxy descriptor FD#" << fd_ << " selected for read with buffer length " << transport_ -> length() << ".\n" << logofs_flush; #endif return 1; } // // Add to the mask the file descriptors of all // X connections to write to. // int Proxy::setWriteDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax) { int fd = -1; T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] != NULL) { fd = getFd(channelId); if (transports_[channelId] -> length() > 0) { FD_SET(fd, fdSet); #ifdef TEST *logofs << "Proxy: Descriptor FD#" << fd << " selected " << "for write with blocked " << transports_[channelId] -> blocked() << " and length " << transports_[channelId] -> length() << ".\n" << logofs_flush; #endif if (fd >= fdMax) { fdMax = fd + 1; } } #ifdef TEST else { *logofs << "Proxy: Descriptor FD#" << fd << " not selected " << "for write with blocked " << transports_[channelId] -> blocked() << " and length " << transports_[channelId] -> length() << ".\n" << logofs_flush; } #endif #if defined(TEST) || defined(INFO) if (transports_[channelId] -> getType() != transport_agent && transports_[channelId] -> length() > 0 && transports_[channelId] -> blocked() != 1) { *logofs << "Proxy: PANIC! Descriptor FD#" << fd << " has data to write but blocked flag is " << transports_[channelId] -> blocked() << ".\n" << logofs_flush; cerr << "Error" << ": Descriptor FD#" << fd << " has data to write but blocked flag is " << transports_[channelId] -> blocked() << ".\n"; HandleCleanup(); } #endif } } // // Check if the proxy transport has data // from a previous blocking write. // if (transport_ -> blocked() == 1) { FD_SET(fd_, fdSet); #ifdef TEST *logofs << "Proxy: Proxy descriptor FD#" << fd_ << " selected for write. Blocked is " << transport_ -> blocked() << " length is " << transport_ -> length() << ".\n" << logofs_flush; #endif if (fd_ >= fdMax) { fdMax = fd_ + 1; } } #ifdef TEST else { *logofs << "Proxy: Proxy descriptor FD#" << fd_ << " not selected for write. Blocked is " << transport_ -> blocked() << " length is " << transport_ -> length() << ".\n" << logofs_flush; } #endif // // We are entering the main select. Save // the timestamp of the last loop so that // we can detect the clock drifts. // timeouts_.loopTs = getTimestamp(); return 1; } int Proxy::getChannels(T_channel_type type) { int channels = 0; T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] != NULL && (type == channel_none || type == channels_[channelId] -> getType())) { channels++; } } return channels; } T_channel_type Proxy::getType(int fd) { int channelId = getChannel(fd); if (channelId < 0 || channels_[channelId] == NULL) { return channel_none; } return channels_[channelId] -> getType(); } const char *Proxy::getTypeName(T_channel_type type) { switch (type) { case channel_x11: { return "X"; } case channel_cups: { return "CUPS"; } case channel_smb: { return "SMB"; } case channel_media: { return "media"; } case channel_http: { return "HTTP"; } case channel_font: { return "font"; } case channel_slave: { return "slave"; } default: { return "unknown"; } } } const char *Proxy::getComputerName() { // // Strangely enough, under some Windows OSes SMB // service doesn't bind to localhost. Fall back // to localhost if can't find computer name in // the environment. In future we should try to // bind to localhost and then try the other IPs. // const char *hostname = NULL; #ifdef __CYGWIN32__ hostname = getenv("COMPUTERNAME"); #endif if (hostname == NULL) { hostname = "localhost"; } return hostname; } // // Handle data from channels selected for read. // int Proxy::handleRead(int &resultFds, fd_set &readSet) { #ifdef DEBUG *logofs << "Proxy: Checking descriptors selected for read.\n" << logofs_flush; #endif T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { #ifdef DEBUG *logofs << "Proxy: Looping with current channel " << *j << ".\n" << logofs_flush; #endif int fd = getFd(*j); if (fd >= 0 && resultFds > 0 && FD_ISSET(fd, &readSet)) { #ifdef DEBUG *logofs << "Proxy: Going to read messages from FD#" << fd << ".\n" << logofs_flush; #endif int result = handleRead(fd); if (result < 0) { #ifdef TEST *logofs << "Proxy: Failure reading messages from FD#" << fd << ".\n" << logofs_flush; #endif return -1; } #ifdef DEBUG *logofs << "Proxy: Clearing the read descriptor " << "for FD#" << fd << ".\n" << logofs_flush; #endif FD_CLR(fd, &readSet); resultFds--; } } if (resultFds > 0 && FD_ISSET(fd_, &readSet)) { #ifdef DEBUG *logofs << "Proxy: Going to read messages from " << "proxy FD#" << fd_ << ".\n" << logofs_flush; #endif if (handleRead() < 0) { #ifdef TEST *logofs << "Proxy: Failure reading from proxy FD#" << fd_ << ".\n" << logofs_flush; #endif return -1; } #ifdef DEBUG *logofs << "Proxy: Clearing the read descriptor " << "for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif FD_CLR(fd_, &readSet); resultFds--; } return 1; } // // Perform flush on descriptors selected for write. // int Proxy::handleFlush(int &resultFds, fd_set &writeSet) { #ifdef DEBUG *logofs << "Proxy: Checking descriptors selected for write.\n" << logofs_flush; #endif if (resultFds > 0 && FD_ISSET(fd_, &writeSet)) { #ifdef TEST *logofs << "Proxy: FLUSH! Proxy descriptor FD#" << fd_ << " reported to be writable.\n" << logofs_flush; #endif if (handleFlush() < 0) { #ifdef TEST *logofs << "Proxy: Failure flushing the writable " << "proxy FD#" << fd_ << ".\n" << logofs_flush; #endif return -1; } #ifdef DEBUG *logofs << "Proxy: Clearing the write descriptor " << "for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif FD_CLR(fd_, &writeSet); resultFds--; } T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); resultFds > 0 && j != channelList.end(); j++) { #ifdef DEBUG *logofs << "Proxy: Looping with current channel " << *j << ".\n" << logofs_flush; #endif int fd = getFd(*j); if (fd >= 0 && FD_ISSET(fd, &writeSet)) { #ifdef TEST *logofs << "Proxy: X descriptor FD#" << fd << " reported to be writable.\n" << logofs_flush; #endif // // It can happen that, in handling reads, we // have destroyed the buffer associated to a // closed socket, so don't complain about // the errors. // handleFlush(fd); // // Clear the descriptor from the mask so // we don't confuse the agent if it's // not checking only its own descriptors. // #ifdef DEBUG *logofs << "Proxy: Clearing the write descriptor " << "for FD#" << fd << ".\n" << logofs_flush; #endif FD_CLR(fd, &writeSet); resultFds--; } } return 1; } int Proxy::handleRead() { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Decoding data from proxy FD#" << fd_ << ".\n" << logofs_flush; #endif // // Decode all the available messages from // the remote proxy until is not possible // to read more. // for (;;) { int result = readBuffer_.readMessage(); #if defined(TEST) || defined(DEBUG) || defined(INFO) *logofs << "Proxy: Read result on proxy FD#" << fd_ << " is " << result << ".\n" << logofs_flush; #endif if (result < 0) { if (shutdown_ == 0) { if (finish_ == 0) { #ifdef PANIC *logofs << "Proxy: PANIC! Failure reading from the " << "peer proxy on FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failure reading from the " << "peer proxy.\n"; } } #ifdef TEST else { *logofs << "Proxy: Closure of the proxy link detected " << "after clean shutdown.\n" << logofs_flush; } #endif priority_ = 0; finish_ = 1; congestion_ = 0; return -1; } else if (result == 0) { #if defined(TEST) || defined(DEBUG) || defined(INFO) *logofs << "Proxy: No data read from proxy FD#" << fd_ << "\n" << logofs_flush; #endif return 0; } // // We read some data from the remote. If we set // the congestion flag because we couldn't read // before the timeout and have tokens available, // then reset the congestion flag. // if (congestion_ == 1 && tokens_[token_control].remaining > 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Exiting congestion due to " << "proxy data with " << tokens_[token_control].remaining << " tokens.\n" << logofs_flush; #endif congestion_ = 0; } // // Set the timestamp of the last read // operation from the remote proxy and // enable again showing the 'no data // received' dialog at the next timeout. // timeouts_.readTs = getTimestamp(); if (alert_ != 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Displacing the dialog " << "for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif HandleAlert(DISPLACE_MESSAGE_ALERT, 1); } timeouts_.alertTs = nullTimestamp(); #if defined(TEST) || defined(INFO) *logofs << "Proxy: Getting messages from proxy FD#" << fd_ << " with " << readBuffer_.getLength() << " bytes " << "in the read buffer.\n" << logofs_flush; #endif unsigned int controlLength; unsigned int dataLength; const unsigned char *message; while ((message = readBuffer_.getMessage(controlLength, dataLength)) != NULL) { statistics -> addFrameIn(); if (controlLength == 3 && *message == 0 && *(message + 1) < code_last_tag) { if (handleControlFromProxy(message) < 0) { return -1; } } else if (operation_ == operation_in_messages) { int channelId = inputChannel_; #if defined(TEST) || defined(INFO) *logofs << "Proxy: Identified message of " << dataLength << " bytes for FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif if (channelId >= 0 && channelId < CONNECTIONS_LIMIT && channels_[channelId] != NULL) { int finish = channels_[channelId] -> getFinish(); #ifdef WARNING if (finish == 1) { *logofs << "Proxy: WARNING! Handling data for finishing " << "FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; } #endif // // We need to decode all the data to preserve // the consistency of the cache, so can't re- // turn as soon as the first error is encount- // ered. Check if this is the first time that // the failure is detected. // int result = channels_[channelId] -> handleWrite(message, dataLength); if (result < 0 && finish == 0) { #ifdef TEST *logofs << "Proxy: Failed to write proxy data to FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif if (handleFinish(channelId) < 0) { return -1; } } // // Check if we have splits or motion // events to send. // setSplitTimeout(channelId); setMotionTimeout(channelId); } #ifdef WARNING else { *logofs << "Proxy: WARNING! Received data for " << "invalid channel ID#" << channelId << ".\n" << logofs_flush; } #endif } else if (operation_ == operation_in_statistics) { #ifdef TEST *logofs << "Proxy: Received statistics data from remote proxy.\n" << logofs_flush; #endif if (handleStatisticsFromProxy(message, dataLength) < 0) { return -1; } operation_ = operation_in_messages; } else if (operation_ == operation_in_negotiation) { #ifdef TEST *logofs << "Proxy: Received new negotiation data from remote proxy.\n" << logofs_flush; #endif if (handleNegotiationFromProxy(message, dataLength) < 0) { return -1; } } // // if (controlLength == 3 && *message == 0 && ...) ... // else if (operation_ == operation_in_statistics) ... // else if (operation_ == operation_in_messages) ... // else if (operation_ == operation_in_negotiation) ... // else ... // else { #ifdef PANIC *logofs << "Proxy: PANIC! Unrecognized message received on proxy FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Unrecognized message received on proxy FD#" << fd_ << ".\n"; return -1; } } // while ((message = readBuffer_.getMessage(controlLength, dataLength)) != NULL) ... // // Reset the read buffer. // readBuffer_.fullReset(); // // Give up if no data is readable. // if (transport_ -> readable() == 0) { break; } } // End of for (;;) ... return 1; } int Proxy::handleControlFromProxy(const unsigned char *message) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Received message '" << DumpControl(*(message + 1)) << "' at " << strMsTimestamp() << " with data ID#" << (int) *(message + 2) << ".\n" << logofs_flush; #endif T_channel_type channelType = channel_none; switch (*(message + 1)) { case code_switch_connection: { int channelId = *(message + 2); // // If channel is invalid further messages will // be ignored. The acknowledged shutdown of // channels should prevent this. // inputChannel_ = channelId; break; } case code_begin_congestion: { // // Set the congestion state for the // channel reported by the remote. // int channelId = *(message + 2); if (channels_[channelId] != NULL) { congestions_[channelId] = 1; #if defined(TEST) || defined(INFO) *logofs << "Proxy: Received a begin congestion " << "for channel id ID#" << channelId << ".\n" << logofs_flush; #endif if (channelId == agent_ && congestions_[agent_] != 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Forcing an update of the congestion " << "counter with agent congested.\n" << logofs_flush; #endif statistics -> updateCongestion(-tokens_[token_control].remaining, tokens_[token_control].limit); } } #ifdef WARNING else { *logofs << "Proxy: WARNING! Received a begin congestion " << "for invalid channel id ID#" << channelId << ".\n" << logofs_flush; } #endif break; } case code_end_congestion: { // // Attend again to the channel. // int channelId = *(message + 2); if (channels_[channelId] != NULL) { congestions_[channelId] = 0; #if defined(TEST) || defined(INFO) *logofs << "Proxy: Received an end congestion " << "for channel id ID#" << channelId << ".\n" << logofs_flush; #endif if (channelId == agent_ && congestions_[agent_] != 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Forcing an update of the congestion " << "counter with agent decongested.\n" << logofs_flush; #endif statistics -> updateCongestion(tokens_[token_control].remaining, tokens_[token_control].limit); } } #ifdef WARNING else { *logofs << "Proxy: WARNING! Received an end congestion " << "for invalid channel id ID#" << channelId << ".\n" << logofs_flush; } #endif break; } case code_control_token_request: { T_proxy_token &token = tokens_[token_control]; if (handleTokenFromProxy(token, *(message + 2)) < 0) { return -1; } break; } case code_split_token_request: { T_proxy_token &token = tokens_[token_split]; if (handleTokenFromProxy(token, *(message + 2)) < 0) { return -1; } break; } case code_data_token_request: { T_proxy_token &token = tokens_[token_data]; if (handleTokenFromProxy(token, *(message + 2)) < 0) { return -1; } break; } case code_control_token_reply: { T_proxy_token &token = tokens_[token_control]; if (handleTokenReplyFromProxy(token, *(message + 2)) < 0) { return -1; } break; } case code_split_token_reply: { T_proxy_token &token = tokens_[token_split]; if (handleTokenReplyFromProxy(token, *(message + 2)) < 0) { return -1; } break; } case code_data_token_reply: { T_proxy_token &token = tokens_[token_data]; if (handleTokenReplyFromProxy(token, *(message + 2)) < 0) { return -1; } break; } case code_new_x_connection: { // // Opening the channel is handled later. // channelType = channel_x11; break; } case code_new_cups_connection: { channelType = channel_cups; break; } case code_new_aux_connection: { // // Starting from version 1.5.0 we create real X // connections for the keyboard channel. We need // to refuse old auxiliary X connections because // they would be unable to leverage the new fake // authorization cookie. // #ifdef WARNING *logofs << "Proxy: WARNING! Can't open outdated auxiliary X " << "channel for code " << *(message + 1) << ".\n" << logofs_flush; #endif cerr << "Warning" << ": Can't open outdated auxiliary X " << "channel for code " << *(message + 1) << ".\n"; if (handleControl(code_drop_connection, *(message + 2)) < 0) { return -1; } break; } case code_new_smb_connection: { channelType = channel_smb; break; } case code_new_media_connection: { channelType = channel_media; break; } case code_new_http_connection: { channelType = channel_http; break; } case code_new_font_connection: { channelType = channel_font; break; } case code_new_slave_connection: { channelType = channel_slave; break; } case code_drop_connection: { int channelId = *(message + 2); if (channelId >= 0 && channelId < CONNECTIONS_LIMIT && channels_[channelId] != NULL) { handleDropFromProxy(channelId); } #ifdef WARNING else { *logofs << "Proxy: WARNING! Received a drop message " << "for invalid channel id ID#" << channelId << ".\n" << logofs_flush; } #endif break; } case code_finish_connection: { int channelId = *(message + 2); if (channelId >= 0 && channelId < CONNECTIONS_LIMIT && channels_[channelId] != NULL) { // // Force the finish state on the channel. // We can receive this message while in // the read loop, so we only mark the // channel for deletion. // #ifdef TEST *logofs << "Proxy: Received a finish message for FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif handleFinishFromProxy(channelId); } #ifdef WARNING else { *logofs << "Proxy: WARNING! Received a finish message " << "for invalid channel id ID#" << channelId << ".\n" << logofs_flush; } #endif break; } case code_finish_listeners: { // // This is from the main loop. // #ifdef TEST *logofs << "Proxy: Closing down all local listeners.\n" << logofs_flush; #endif CleanupListeners(); finish_ = 1; break; } case code_reset_request: { #ifdef PANIC *logofs << "Proxy: PANIC! Proxy reset not supported " << "in this version.\n" << logofs_flush; #endif cerr << "Error" << ": Proxy reset not supported " << "in this version.\n"; HandleCleanup(); } case code_shutdown_request: { // // Time to rest in peace. // shutdown_ = 1; break; } case code_load_request: { if (handleLoadFromProxy() < 0) { return -1; } break; } case code_save_request: { // // Don't abort the connection // if can't write to disk. // handleSaveFromProxy(); break; } case code_statistics_request: { int type = *(message + 2); if (handleStatisticsFromProxy(type) < 0) { return -1; } break; } case code_statistics_reply: { operation_ = operation_in_statistics; break; } case code_alert_request: { HandleAlert(*(message + 2), 1); break; } case code_sync_request: { int channelId = *(message + 2); if (handleSyncFromProxy(channelId) < 0) { return -1; } break; } case code_sync_reply: { // // We are not the one that issued // the request. // #if defined(TEST) || defined(INFO) *logofs << "Proxy: PANIC! Received an unexpected " << "synchronization reply.\n" << logofs_flush; #endif cerr << "Error" << ": Received an unexpected " << "synchronization reply.\n"; HandleCleanup(); } default: { #ifdef PANIC *logofs << "Proxy: PANIC! Received bad control message number " << (unsigned int) *(message + 1) << " with attribute " << (unsigned int) *(message + 2) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Received bad control message number " << (unsigned int) *(message + 1) << " with attribute " << (unsigned int) *(message + 2) << ".\n"; HandleCleanup(); } } // End of switch (*(message + 1)) ... if (channelType == channel_none) { return 1; } // // Handle the channel allocation that we // left from the main switch case. // int channelId = *(message + 2); // // Check if the channel has been dropped. // if (channels_[channelId] != NULL && (channels_[channelId] -> getDrop() == 1 || channels_[channelId] -> getClosing() == 1)) { #ifdef TEST *logofs << "Proxy: Dropping the descriptor FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif handleDrop(channelId); } // // Check if the channel is in the valid // range. // int result = checkChannelMap(channelId); if (result >= 0) { result = handleNewConnectionFromProxy(channelType, channelId); } if (result < 0) { // // Realization of new channel failed. // Send channel shutdown message to // the peer proxy. // if (handleControl(code_drop_connection, channelId) < 0) { return -1; } } else { int fd = getFd(channelId); if (getReadable(fd) > 0) { #ifdef TEST *logofs << "Proxy: Trying to read immediately " << "from descriptor FD#" << fd << ".\n" << logofs_flush; #endif if (handleRead(fd) < 0) { return -1; } } #ifdef TEST *logofs << "Proxy: Nothing to read immediately " << "from descriptor FD#" << fd << ".\n" << logofs_flush; #endif } return 1; } int Proxy::handleRead(int fd, const char *data, int size) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Handling data for connection on FD#" << fd << ".\n" << logofs_flush; #endif if (canRead(fd) == 0) { #if defined(TEST) || defined(INFO) if (getChannel(fd) < 0) { *logofs << "Proxy: PANIC! Can't read from invalid FD#" << fd << ".\n" << logofs_flush; HandleCleanup(); } else { *logofs << "Proxy: WARNING! Read method called for FD#" << fd << " but operation is not possible.\n" << logofs_flush; } #endif return 0; } int channelId = getChannel(fd); // // Let the channel object read all the new data from // its file descriptor, isolate messages, compress // those messages, and append the compressed form to // the encode buffer. // #if defined(TEST) || defined(INFO) *logofs << "Proxy: Reading messages from FD#" << fd << " channel ID#" << channelId << ".\n" << logofs_flush; #endif int result = channels_[channelId] -> handleRead(encodeBuffer_, (const unsigned char *) data, (unsigned int) size); // // Even in the case of a failure, write the produced // data to the proxy connection. To keep the stores // synchronized, the remote side needs to decode any // message encoded by this side, also if the X socket // was closed in the meanwhile. If this is the case, // the decompressed output will be silently discarded. // if (result < 0) { #ifdef TEST *logofs << "Proxy: Failed to read data from connection FD#" << fd << " channel ID#" << channelId << ".\n" << logofs_flush; #endif if (handleFinish(channelId) < 0) { return -1; } } // // Check if there are new splits or // motion events to send. // setSplitTimeout(channelId); setMotionTimeout(channelId); return 1; } int Proxy::handleEvents() { #ifdef TEST *logofs << "Proxy: Going to check the events on channels.\n" << logofs_flush; #endif // // Check if we can safely write to the // proxy link. // int read = isTimeToRead(); // // Loop on channels and send the pending // events. We must copy the list because // channels can be removed in the middle // of the loop. // T_list channelList = activeChannels_.copyList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] == NULL) { continue; } // // Check if we need to drop the channel. // if (channels_[channelId] -> getDrop() == 1 || channels_[channelId] -> getClosing() == 1) { #ifdef TEST *logofs << "Proxy: Dropping the descriptor FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif if (handleDrop(channelId) < 0) { return -1; } continue; } else if (channels_[channelId] -> getFinish() == 1) { #ifdef TEST *logofs << "Proxy: Skipping finishing " << "descriptor FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif continue; } // // If the proxy link or the channel is // in congestion state, don't handle // the further events. // if (read == 0 || congestions_[channelId] == 1) { #ifdef TEST if (read == 0) { *logofs << "Proxy: Can't handle events for FD#" << getFd(channelId) << " channel ID#" << channelId << " with proxy not available.\n" << logofs_flush; } else { *logofs << "Proxy: Can't handle events for FD#" << getFd(channelId) << " channel ID#" << channelId << " with channel congested.\n" << logofs_flush; } #endif continue; } // // Handle the timeouts on the channel // operations. // int result = 0; // // Handle the motion events. // if (result >= 0 && channels_[channelId] -> needMotion() == 1) { if (isTimeToMotion() == 1) { #if defined(TEST) || defined(INFO) || defined(FLUSH) *logofs << "Proxy: FLUSH! Motion timeout expired after " << diffTimestamp(timeouts_.motionTs, getTimestamp()) << " Ms.\n" << logofs_flush; #endif result = channels_[channelId] -> handleMotion(encodeBuffer_); #ifdef TEST if (result < 0) { *logofs << "Proxy: Failed to handle motion events for FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; } #endif timeouts_.motionTs = nullTimestamp(); setMotionTimeout(channelId); } #if defined(TEST) || defined(INFO) else if (isTimestamp(timeouts_.motionTs) == 1) { *logofs << "Proxy: Running with " << diffTimestamp(timeouts_.motionTs, getTimestamp()) << " Ms elapsed since the last motion.\n" << logofs_flush; } #endif } if (result >= 0 && channels_[channelId] -> needSplit() == 1) { // // Check if it is time to send more splits // and how many bytes are going to be sent. // if (isTimeToSplit() == 1) { #if defined(TEST) || defined(INFO) || defined(SPLIT) *logofs << "Proxy: SPLIT! Split timeout expired after " << diffTimestamp(timeouts_.splitTs, getTimestamp()) << " Ms.\n" << logofs_flush; #endif #if defined(TEST) || defined(INFO) || defined(SPLIT) *logofs << "Proxy: SPLIT! Encoding splits for FD#" << getFd(channelId) << " at " << strMsTimestamp() << " with " << clientStore_ -> getSplitTotalStorageSize() << " total bytes and " << control -> SplitDataPacketLimit << " bytes " << "to write.\n" << logofs_flush; #endif result = channels_[channelId] -> handleSplit(encodeBuffer_); #ifdef TEST if (result < 0) { *logofs << "Proxy: Failed to handle splits for FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; } #endif timeouts_.splitTs = nullTimestamp(); setSplitTimeout(channelId); } #if defined(TEST) || defined(INFO) || defined(SPLIT) else if (channels_[channelId] -> needSplit() == 1 && isTimestamp(timeouts_.splitTs) == 0) { *logofs << "Proxy: SPLIT! WARNING! Channel for FD#" << getFd(channelId) << " has split to send but " << "there is no timeout.\n" << logofs_flush; } else if (isTimestamp(timeouts_.splitTs) == 1) { *logofs << "Proxy: SPLIT! Running with " << diffTimestamp(timeouts_.splitTs, getTimestamp()) << " Ms elapsed since the last split.\n" << logofs_flush; } #endif } if (result < 0) { #ifdef TEST *logofs << "Proxy: Error handling events for FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif if (handleFinish(channelId) < 0) { return -1; } } } return 1; } int Proxy::handleFrame(T_frame_type type) { // // Write any outstanding control message, followed by the // content of the encode buffer, to the proxy transport. // // This code assumes that the encode buffer data is at an // offset several bytes from start of the buffer, so that // the length header and any necessary control bytes can // be inserted in front of the data already in the buffer. // This is the easiest way to encapsulate header and data // together in a single frame. // // The way framing is implemented is very efficient but // inherently limited and does not allow for getting the // best performance, especially when running over a fast // link. Framing should be rewritten to include the length // of the packets in a fixed size header and, possibly, // to incapsulate the control messages and the channel's // data in a pseudo X protocol message, so that the proxy // itself would be treated like any other channel. // #if defined(TEST) || defined(INFO) if (congestion_ == 1) { // // This can happen because there may be control // messages to send, like a proxy shutdown mes- // sage or a statistics request. All the other // cases should be considered an error. // #ifdef WARNING *logofs << "Proxy: WARNING! Data is to be sent while " << "congestion is " << congestion_ << ".\n" << logofs_flush; #endif } #endif // // Check if there is any data available on // the socket. Recent Linux kernels are very // picky. They require that we read often or // they assume that the process is non-inter- // active. // if (handleAsyncEvents() < 0) { return -1; } // // Check if this is a ping, not a data frame. // if (type == frame_ping) { if (handleToken(frame_ping) < 0) { return -1; } } unsigned int dataLength = encodeBuffer_.getLength(); #ifdef DEBUG *logofs << "Proxy: Data length is " << dataLength << " control length is " << controlLength_ << ".\n" << logofs_flush; #endif if (dataLength > 0) { // // If this is a generic channel we need // to add the completion bits. Data can // also have been encoded because of a // statistics request, even if no output // channel was currently selected. // if (outputChannel_ != -1) { #if defined(TEST) || defined(INFO) if (channels_[outputChannel_] == NULL) { *logofs << "Proxy: PANIC! A new frame was requested " << "but the channel is invalid.\n" << logofs_flush; HandleCleanup(); } #endif channels_[outputChannel_] -> handleCompletion(encodeBuffer_); dataLength = encodeBuffer_.getLength(); } } else if (controlLength_ == 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: PANIC! A new frame was requested " << "but there is no data to write.\n" << logofs_flush; HandleCleanup(); #endif return 0; } #ifdef DEBUG *logofs << "Proxy: Data length is now " << dataLength << " control length is " << controlLength_ << ".\n" << logofs_flush; #endif // // Check if this frame needs to carry a new // token request. // if (type == frame_data) { if (handleToken(frame_data) < 0) { return -1; } } #ifdef DEBUG *logofs << "Proxy: Adding a new frame for the remote proxy.\n" << logofs_flush; #endif unsigned char temp[5]; unsigned int lengthLength = 0; unsigned int shift = dataLength; while (shift) { temp[lengthLength++] = (unsigned char) (shift & 0x7f); shift >>= 7; } unsigned char *data = encodeBuffer_.getData(); unsigned char *outputMessage = data - (controlLength_ + lengthLength); unsigned char *nextDest = outputMessage; for (int i = 0; i < controlLength_; i++) { *nextDest++ = controlCodes_[i]; } for (int j = lengthLength - 1; j > 0; j--) { *nextDest++ = (temp[j] | 0x80); } if (lengthLength) { *nextDest++ = temp[0]; } unsigned int outputLength = dataLength + controlLength_ + lengthLength; #if defined(TEST) || defined(INFO) *logofs << "Proxy: Produced plain output for " << dataLength << "+" << controlLength_ << "+" << lengthLength << " out of " << outputLength << " bytes.\n" << logofs_flush; #endif #if defined(TEST) || defined(INFO) || defined(FLUSH) || defined(TIME) T_timestamp nowTs = getTimestamp(); *logofs << "Proxy: FLUSH! Immediate with blocked " << transport_ -> blocked() << " length " << transport_ -> length() << " new " << outputLength << " flushable " << transport_ -> flushable() << " tokens " << tokens_[token_control].remaining << " after " << diffTimestamp(timeouts_.writeTs, nowTs) << " Ms.\n" << logofs_flush; *logofs << "Proxy: FLUSH! Immediate flush to proxy FD#" << fd_ << " of " << outputLength << " bytes at " << strMsTimestamp() << " with priority " << priority_ << ".\n" << logofs_flush; *logofs << "Proxy: FLUSH! Current bitrate is " << statistics -> getBitrateInShortFrame() << " with " << statistics -> getBitrateInLongFrame() << " in the " << "long frame and top " << statistics -> getTopBitrate() << ".\n" << logofs_flush; #endif statistics -> addWriteOut(); int result = transport_ -> write(write_immediate, outputMessage, outputLength); #ifdef TIME if (diffTimestamp(timeouts_.writeTs, nowTs) > 50) { *logofs << "Proxy: WARNING! TIME! Data written to proxy FD#" << fd_ << " at " << strMsTimestamp() << " after " << diffTimestamp(timeouts_.writeTs, nowTs) << " Ms.\n" << logofs_flush; } #endif #ifdef DUMP *logofs << "Proxy: Sent " << outputLength << " bytes of data " << "with checksum "; DumpChecksum(outputMessage, outputLength); *logofs << " on proxy FD#" << fd_ << ".\n" << logofs_flush; #endif #ifdef DUMP *logofs << "Proxy: Partial checksums are:\n"; DumpBlockChecksums(outputMessage, outputLength, 256); *logofs << logofs_flush; #endif // // Clean up the encode buffer and // bring it to the initial size. // encodeBuffer_.fullReset(); // // Close the connection if we got // an error. // if (result < 0) { #ifdef TEST *logofs << "Proxy: Failed write to proxy FD#" << fd_ << ".\n" << logofs_flush; #endif return -1; } // // Account for the data frame and the // framing overhead. // if (dataLength > 0) { statistics -> addFrameOut(); } statistics -> addFramingBits((controlLength_ + lengthLength) << 3); controlLength_ = 0; // // Reset all buffers, counters and the // priority flag. // handleResetFlush(); // // Check if more data became available // after writing. // if (handleAsyncEvents() < 0) { return -1; } // // Drain the proxy link if we are in // congestion state. // // if (needDrain() == 1 && draining_ == 0) // { // if (handleDrain() < 0) // { // return -1; // } // } // return result; } int Proxy::handleFlush() { // // We can have data in the encode buffer or // control bytes to send. In the case make // up a new frame. // if (encodeBuffer_.getLength() + controlLength_ > 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Flushing data in the encode buffer.\n" << logofs_flush; #endif priority_ = 1; if (handleFrame(frame_data) < 0) { return -1; } } // // Check if we have something to write. // if (transport_ -> length() + transport_ -> flushable() == 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Nothing else to flush for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif return 0; } #if defined(TEST) || defined(INFO) if (transport_ -> blocked() == 0) { #ifdef PANIC *logofs << "Proxy: PANIC! Proxy descriptor FD#" << fd_ << " has data to flush but the transport " << "is not blocked.\n" << logofs_flush; #endif cerr << "Error" << ": Proxy descriptor FD#" << fd_ << " has data to flush but the transport " << "is not blocked.\n"; HandleCleanup(); } #endif #if defined(TEST) || defined(INFO) || defined(FLUSH) *logofs << "Proxy: FLUSH! Deferred with blocked " << transport_ -> blocked() << " length " << transport_ -> length() << " flushable " << transport_ -> flushable() << " tokens " << tokens_[token_control].remaining << ".\n" << logofs_flush; *logofs << "Proxy: FLUSH! Deferred flush to proxy FD#" << fd_ << " of " << transport_ -> length() + transport_ -> flushable() << " bytes at " << strMsTimestamp() << " with priority " << priority_ << ".\n" << logofs_flush; *logofs << "Proxy: FLUSH! Current bitrate is " << statistics -> getBitrateInShortFrame() << " with " << statistics -> getBitrateInLongFrame() << " in the " << "long frame and top " << statistics -> getTopBitrate() << ".\n" << logofs_flush; #endif statistics -> addWriteOut(); int result = transport_ -> flush(); if (result < 0) { return -1; } // // Reset the counters and update the // timestamp of the last write. // handleResetFlush(); return result; } int Proxy::handleDrain() { // // If the proxy is run in the same process // as SSH, we can't block or the program // would not have a chance to read or write // its data. // if (control -> LinkEncrypted == 1) { return 0; } if (needDrain() == 0 || draining_ == 1) { #if defined(TEST) || defined(INFO) if (draining_ == 1) { *logofs << "Proxy: WARNING! Already draining proxy FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; } else { *logofs << "Proxy: WARNING! No need to drain proxy FD#" << fd_ << " with congestion " << congestion_ << " length " << transport_ -> length() << " and blocked " << transport_ -> blocked() << ".\n" << logofs_flush; } #endif return 0; } draining_ = 1; #if defined(TEST) || defined(INFO) *logofs << "Proxy: Going to drain the proxy FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif int timeout = control -> PingTimeout / 2; T_timestamp startTs = getNewTimestamp(); T_timestamp nowTs = startTs; int remaining; int result; // // Keep draining the proxy socket while // reading the incoming messages until // the timeout is expired. // for (;;) { remaining = timeout - diffTimestamp(startTs, nowTs); if (remaining <= 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Timeout raised while draining " << "FD#" << fd_ << " at " << strMsTimestamp() << " after " << diffTimestamp(startTs, nowTs) << " Ms.\n" << logofs_flush; #endif result = 0; goto ProxyDrainEnd; } if (transport_ -> length() > 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Trying to write to FD#" << fd_ << " at " << strMsTimestamp() << " with length " << transport_ -> length() << " and " << remaining << " Ms remaining.\n" << logofs_flush; #endif result = transport_ -> drain(0, remaining); if (result == -1) { result = -1; goto ProxyDrainEnd; } else if (result == 0 && transport_ -> readable() > 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Decoding more data from proxy FD#" << fd_ << " at " << strMsTimestamp() << " with " << transport_ -> length() << " bytes to write and " << transport_ -> readable() << " readable.\n" << logofs_flush; #endif if (handleRead() < 0) { result = -1; goto ProxyDrainEnd; } } #if defined(TEST) || defined(INFO) else if (result == 1) { *logofs << "Proxy: Transport for proxy FD#" << fd_ << " drained down to " << transport_ -> length() << " bytes.\n" << logofs_flush; } #endif } else { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Waiting for more data from proxy " << "FD#" << fd_ << " at " << strMsTimestamp() << " with " << remaining << " Ms remaining.\n" << logofs_flush; #endif result = transport_ -> wait(remaining); if (result == -1) { result = -1; goto ProxyDrainEnd; } else if (result > 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Decoding more data from proxy FD#" << fd_ << " at " << strMsTimestamp() << " with " << transport_ -> readable() << " bytes readable.\n" << logofs_flush; #endif if (handleRead() < 0) { result = -1; goto ProxyDrainEnd; } } } // // Check if we finally got the tokens // that would allow us to come out of // the congestion state. // if (needDrain() == 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Got decongestion for proxy FD#" << fd_ << " at " << strMsTimestamp() << " after " << diffTimestamp(startTs, getTimestamp()) << " Ms.\n" << logofs_flush; #endif result = 1; goto ProxyDrainEnd; } nowTs = getNewTimestamp(); } ProxyDrainEnd: draining_ = 0; return result; } int Proxy::handleFlush(int fd) { int channelId = getChannel(fd); if (channelId < 0 || channels_[channelId] == NULL) { #ifdef TEST *logofs << "Proxy: WARNING! Skipping flush on invalid " << "descriptor FD#" << fd << " channel ID#" << channelId << ".\n" << logofs_flush; #endif return 0; } else if (channels_[channelId] -> getFinish() == 1) { #ifdef TEST *logofs << "Proxy: Skipping flush on finishing " << "descriptor FD#" << fd << " channel ID#" << channelId << ".\n" << logofs_flush; #endif return 0; } #ifdef TEST *logofs << "Proxy: Going to flush FD#" << fd << " with blocked " << transports_[channelId] -> blocked() << " length " << transports_[channelId] -> length() << ".\n" << logofs_flush; #endif if (channels_[channelId] -> handleFlush() < 0) { #ifdef TEST *logofs << "Proxy: Failed to flush data to FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif handleFinish(channelId); return -1; } return 1; } int Proxy::handleStatistics(int type, ostream *stream) { if (stream == NULL || control -> EnableStatistics == 0) { #ifdef WARNING *logofs << "Proxy: WARNING! Cannot produce statistics " << " for proxy FD#" << fd_ << ". Invalid settings " << "for statistics or stream.\n" << logofs_flush; #endif return 0; } else if (currentStatistics_ != NULL) { // // Need to update the stream pointer as the // previous one could have been destroyed. // #ifdef WARNING *logofs << "Proxy: WARNING! Replacing stream while producing " << "statistics in stream at " << currentStatistics_ << " for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif } currentStatistics_ = stream; // // Get statistics of remote peer. // if (handleControl(code_statistics_request, type) < 0) { return -1; } return 1; } int Proxy::handleStatisticsFromProxy(int type) { if (needFlush() == 1) { #if defined(TEST) || defined(INFO) || defined(FLUSH) *logofs << "Proxy: WARNING! Data for the previous " << "channel ID#" << outputChannel_ << " flushed in statistics.\n" << logofs_flush; #endif if (handleFrame(frame_data) < 0) { return -1; } } if (control -> EnableStatistics == 1) { // // Allocate a buffer for the output. // char *buffer = new char[STATISTICS_LENGTH]; *buffer = '\0'; if (control -> ProxyMode == proxy_client) { #ifdef TEST *logofs << "Proxy: Producing " << (type == TOTAL_STATS ? "total" : "partial") << " client statistics for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif statistics -> getClientProtocolStats(type, buffer); statistics -> getClientOverallStats(type, buffer); } else { #ifdef TEST *logofs << "Proxy: Producing " << (type == TOTAL_STATS ? "total" : "partial") << " server statistics for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif statistics -> getServerProtocolStats(type, buffer); } if (type == PARTIAL_STATS) { statistics -> resetPartialStats(); } unsigned int length = strlen((char *) buffer) + 1; encodeBuffer_.encodeValue(type, 8); encodeBuffer_.encodeValue(length, 32); #ifdef TEST *logofs << "Proxy: Encoding " << length << " bytes of statistics data for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif encodeBuffer_.encodeMemory((unsigned char *) buffer, length); // // Account statistics data as framing bits. // statistics -> addFramingBits(length << 3); delete [] buffer; } else { #ifdef WARNING *logofs << "Proxy: WARNING! Got statistics request " << "but local statistics are disabled.\n" << logofs_flush; #endif cerr << "Warning" << ": Got statistics request " << "but local statistics are disabled.\n"; type = NO_STATS; encodeBuffer_.encodeValue(type, 8); #ifdef TEST *logofs << "Proxy: Sending error code to remote proxy on FD#" << fd_ << ".\n" << logofs_flush; #endif } // // The next write will flush the statistics // data and the control message. // if (handleControl(code_statistics_reply, type) < 0) { return -1; } return 1; } int Proxy::handleStatisticsFromProxy(const unsigned char *message, unsigned int length) { if (currentStatistics_ == NULL) { #ifdef WARNING *logofs << "Proxy: WARNING! Unexpected statistics data received " << "from remote proxy on FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Warning" << ": Unexpected statistics data received " << "from remote proxy.\n"; return 0; } // // Allocate the decode buffer and at least // the 'type' field to see if there was an // error. // DecodeBuffer decodeBuffer(message, length); unsigned int type; decodeBuffer.decodeValue(type, 8); if (type == NO_STATS) { #ifdef PANIC *logofs << "Proxy: PANIC! Couldn't get statistics from remote " << "proxy on FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Couldn't get statistics from remote proxy.\n"; } else if (type != TOTAL_STATS && type != PARTIAL_STATS) { #ifdef PANIC *logofs << "Proxy: PANIC! Cannot produce statistics " << "with qualifier '" << type << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot produce statistics " << "with qualifier '" << type << "'.\n"; return -1; } else { unsigned int size; decodeBuffer.decodeValue(size, 32); char *buffer = new char[STATISTICS_LENGTH]; *buffer = '\0'; if (control -> EnableStatistics == 1) { if (control -> ProxyMode == proxy_client) { #ifdef TEST *logofs << "Proxy: Finalizing " << (type == TOTAL_STATS ? "total" : "partial") << " client statistics for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif statistics -> getClientCacheStats(type, buffer); #ifdef TEST *logofs << "Proxy: Decoding " << size << " bytes of statistics data for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size); statistics -> getClientProtocolStats(type, buffer); statistics -> getClientOverallStats(type, buffer); } else { #ifdef TEST *logofs << "Proxy: Finalizing " << (type == TOTAL_STATS ? "total" : "partial") << " server statistics for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif statistics -> getServerCacheStats(type, buffer); statistics -> getServerProtocolStats(type, buffer); #ifdef TEST *logofs << "Proxy: Decoding " << size << " bytes of statistics data for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size); } if (type == PARTIAL_STATS) { statistics -> resetPartialStats(); } *currentStatistics_ << buffer; // // Mark the end of text to help external parsing. // *currentStatistics_ << '\4'; *currentStatistics_ << flush; } else { // // It can be that statistics were enabled at the time // we issued the request (otherwise we could not have // set the stream), but now they have been disabled // by user. We must decode statistics data if we want // to keep the connection. // #ifdef TEST *logofs << "Proxy: Discarding " << size << " bytes of statistics data for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size); } delete [] buffer; } currentStatistics_ = NULL; return 1; } int Proxy::handleNegotiation(const unsigned char *message, unsigned int length) { #ifdef PANIC *logofs << "Proxy: PANIC! Writing data during proxy " << "negotiation is not implemented.\n" << logofs_flush; #endif cerr << "Error" << ": Writing data during proxy " << "negotiation is not implemented.\n"; return -1; } int Proxy::handleNegotiationFromProxy(const unsigned char *message, unsigned int length) { #ifdef PANIC *logofs << "Proxy: PANIC! Reading data during proxy " << "negotiation is not implemented.\n" << logofs_flush; #endif cerr << "Error" << ": Reading data during proxy " << "negotiation is not implemented.\n"; return -1; } int Proxy::handleAlert(int alert) { if (handleControl(code_alert_request, alert) < 0) { return -1; } return 1; } int Proxy::handleCloseConnection(int clientFd) { int channelId = getChannel(clientFd); if (channels_[channelId] != NULL && channels_[channelId] -> getFinish() == 0) { #ifdef TEST *logofs << "Proxy: Closing down the channel for FD#" << clientFd << ".\n" << logofs_flush; #endif if (handleFinish(channelId) < 0) { return -1; } return 1; } return 0; } int Proxy::handleCloseAllXConnections() { #ifdef TEST *logofs << "Proxy: Closing down any remaining X channel.\n" << logofs_flush; #endif T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] != NULL && channels_[channelId] -> getType() == channel_x11 && channels_[channelId] -> getFinish() == 0) { #ifdef TEST *logofs << "Proxy: Closing down the channel for FD#" << getFd(channelId) << ".\n" << logofs_flush; #endif if (handleFinish(channelId) < 0) { return -1; } } } return 1; } int Proxy::handleCloseAllListeners() { if (control -> isProtoStep7() == 1) { if (finish_ == 0) { #ifdef TEST *logofs << "Proxy: Closing down all remote listeners.\n" << logofs_flush; #endif if (handleControl(code_finish_listeners) < 0) { return -1; } finish_ = 1; } } else { #ifdef TEST *logofs << "Proxy: WARNING! Not sending unsupported " << "'code_finish_listeners' message.\n" << logofs_flush; #endif finish_ = 1; } return 1; } void Proxy::handleResetAlert() { if (alert_ != 0) { #ifdef TEST *logofs << "Proxy: The proxy alert '" << alert_ << "' was displaced.\n" << logofs_flush; #endif alert_ = 0; } T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] != NULL) { channels_[channelId] -> handleResetAlert(); } } } int Proxy::handleFinish(int channelId) { // // Send any outstanding encoded data and // do any finalization needed on the // channel. // if (needFlush(channelId) == 1) { if (channels_[channelId] -> getFinish() == 1) { #ifdef WARNING *logofs << "Proxy: WARNING! The finishing channel ID#" << channelId << " has data to flush.\n" << logofs_flush; #endif } #if defined(TEST) || defined(INFO) || defined(FLUSH) *logofs << "Proxy: WARNING! Flushing data for the " << "finishing channel ID#" << channelId << ".\n" << logofs_flush; #endif if (handleFrame(frame_data) < 0) { return -1; } } // // Reset the congestion state and the // timeouts, if needed. // congestions_[channelId] = 0; setSplitTimeout(channelId); setMotionTimeout(channelId); if (channels_[channelId] -> getFinish() == 0) { channels_[channelId] -> handleFinish(); // // Force a failure in the case somebody // would try to read from the channel. // shutdown(getFd(channelId), SHUT_RD); // // If the failure was not originated by // the remote, send a channel shutdown // message. // if (channels_[channelId] -> getClosing() == 0) { #ifdef TEST *logofs << "Proxy: Finishing channel for FD#" << getFd(channelId) << " channel ID#" << channelId << " because of failure.\n" << logofs_flush; #endif if (handleControl(code_finish_connection, channelId) < 0) { return -1; } } } return 1; } int Proxy::handleFinishFromProxy(int channelId) { // // Check if this channel has pending // data to send. // if (needFlush(channelId) == 1) { #if defined(TEST) || defined(INFO) || defined(FLUSH) *logofs << "Proxy: WARNING! Flushing data for the " << "finishing channel ID#" << channelId << ".\n" << logofs_flush; #endif if (handleFrame(frame_data) < 0) { return -1; } } // // Mark the channel. We will free its // resources at the next loop and will // send the drop message to the remote. // if (channels_[channelId] -> getClosing() == 0) { #ifdef TEST *logofs << "Proxy: Marking channel for FD#" << getFd(channelId) << " channel ID#" << channelId << " as closing.\n" << logofs_flush; #endif channels_[channelId] -> handleClosing(); } if (channels_[channelId] -> getFinish() == 0) { #ifdef TEST *logofs << "Proxy: Finishing channel for FD#" << getFd(channelId) << " channel ID#" << channelId << " because of proxy.\n" << logofs_flush; #endif channels_[channelId] -> handleFinish(); } if (handleFinish(channelId) < 0) { return -1; } return 1; } int Proxy::handleDropFromProxy(int channelId) { // // Only mark the channel. // #ifdef TEST *logofs << "Proxy: Marking channel for FD#" << getFd(channelId) << " channel ID#" << channelId << " as being dropped.\n" << logofs_flush; #endif if (channels_[channelId] -> getDrop() == 0) { channels_[channelId] -> handleDrop(); } return 1; } // // Close the channel and deallocate all its // resources. // int Proxy::handleDrop(int channelId) { // // Check if this channel has pending // data to send. // if (needFlush(channelId) == 1) { if (channels_[channelId] -> getFinish() == 1) { #ifdef WARNING *logofs << "Proxy: WARNING! The dropping channel ID#" << channelId << " has data to flush.\n" << logofs_flush; #endif } #if defined(TEST) || defined(INFO) || defined(FLUSH) *logofs << "Proxy: WARNING! Flushing data for the " << "dropping channel ID#" << channelId << ".\n" << logofs_flush; #endif if (handleFrame(frame_data) < 0) { return -1; } } #ifdef TEST *logofs << "Proxy: Dropping channel for FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif if (channels_[channelId] -> getFinish() == 0) { #ifdef WARNING *logofs << "Proxy: WARNING! The channel for FD#" << getFd(channelId) << " channel ID#" << channelId << " was not marked as " << "finishing.\n" << logofs_flush; #endif cerr << "Warning" << ": The channel for FD#" << getFd(channelId) << " channel ID#" << channelId << " was not marked as " << "finishing.\n"; channels_[channelId] -> handleFinish(); } // // Send the channel shutdown message // to the peer proxy. // if (channels_[channelId] -> getClosing() == 1) { if (handleControl(code_drop_connection, channelId) < 0) { return -1; } } // // Get rid of the channel. // if (channels_[channelId] -> getType() != channel_x11) { #ifdef TEST *logofs << "Proxy: Closed connection to " << getTypeName(channels_[channelId] -> getType()) << " server.\n" << logofs_flush; #endif cerr << "Info" << ": Closed connection to " << getTypeName(channels_[channelId] -> getType()) << " server.\n"; } delete channels_[channelId]; channels_[channelId] = NULL; cleanupChannelMap(channelId); // // Get rid of the transport. // deallocateTransport(channelId); congestions_[channelId] = 0; decreaseChannels(channelId); // // Check if the channel was the // one currently selected for // output. // if (outputChannel_ == channelId) { outputChannel_ = -1; } return 1; } // // Send an empty message to the remote peer // to verify if the link is alive and let // the remote proxy detect a congestion. // int Proxy::handlePing() { T_timestamp nowTs = getTimestamp(); #if defined(DEBUG) || defined(PING) *logofs << "Proxy: Checking ping at " << strMsTimestamp(nowTs) << logofs_flush; *logofs << " with last loop at " << strMsTimestamp(timeouts_.loopTs) << ".\n" << logofs_flush; *logofs << "Proxy: Last bytes in at " << strMsTimestamp(timeouts_.readTs) << logofs_flush; *logofs << " last bytes out at " << strMsTimestamp(timeouts_.writeTs) << ".\n" << logofs_flush; *logofs << "Proxy: Last ping at " << strMsTimestamp(timeouts_.pingTs) << ".\n" << logofs_flush; #endif // // Be sure we take into account any clock drift. This // can be caused by the user changing the system timer // or by small adjustments introduced by the operating // system making the clock go backward. // if (checkDiffTimestamp(timeouts_.loopTs, nowTs) == 0) { #ifdef WARNING *logofs << "Proxy: WARNING! Detected drift in system " << "timer. Resetting to current time.\n" << logofs_flush; #endif timeouts_.pingTs = nowTs; timeouts_.readTs = nowTs; timeouts_.writeTs = nowTs; } // // Check timestamp of last read from remote proxy. It can // happen that we stayed in the main loop long enough to // have idle timeout expired, for example if the proxy was // stopped and restarted or because of an extremely high // load of the system. In this case we don't complain if // there is something new to read from the remote. // int diffIn = diffTimestamp(timeouts_.readTs, nowTs); if (diffIn >= (control -> PingTimeout * 2) - control -> LatencyTimeout) { // // Force a read to detect whether the remote proxy // aborted the connection. // int result = handleRead(); if (result < 0) { #if defined(TEST) || defined(INFO) || defined(PING) *logofs << "Proxy: WARNING! Detected shutdown waiting " << "for the ping after " << diffIn / 1000 << " seconds.\n" << logofs_flush; #endif return -1; } else if (result > 0) { diffIn = diffTimestamp(timeouts_.readTs, nowTs); if (handleFlush() < 0) { return -1; } } } if (diffIn >= (control -> PingTimeout * 2) - control -> LatencyTimeout) { #if defined(TEST) || defined(INFO) || defined(PING) *logofs << "Proxy: Detected congestion at " << strMsTimestamp() << " with " << diffIn / 1000 << " seconds since the last read.\n" << logofs_flush; #endif // // There are two types of proxy congestion. The first, // affecting the ability of the proxy to write the // encoded data to the network, is controlled by the // congestion_ flag. The flag is raised when no data // is received from the remote proxy within a timeout. // On the X client side, the flag is also raised when // the proxy runs out of tokens. // if (control -> ProxyMode == proxy_server) { // // At X server side we must return to read data // from the channels after a while, because we // need to give a chance to the channel to read // the key sequence CTRL+ALT+SHIFT+ESC. // if (congestion_ == 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Forcibly entering congestion due to " << "timeout with " << tokens_[token_control].remaining << " tokens.\n" << logofs_flush; #endif congestion_ = 1; } else { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Forcibly exiting congestion due to " << "timeout with " << tokens_[token_control].remaining << " tokens.\n" << logofs_flush; #endif congestion_ = 0; } } else { #if defined(TEST) || defined(INFO) if (congestion_ == 0) { *logofs << "Proxy: Entering congestion due to timeout " << "with " << tokens_[token_control].remaining << " tokens.\n" << logofs_flush; } #endif congestion_ = 1; } if (control -> ProxyTimeout > 0 && diffIn >= (control -> ProxyTimeout - control -> LatencyTimeout)) { #ifdef PANIC *logofs << "Proxy: PANIC! No data received from " << "remote proxy on FD#" << fd_ << " within " << (diffIn + control -> LatencyTimeout) / 1000 << " seconds.\n" << logofs_flush; #endif cerr << "Error" << ": No data received from remote " << "proxy within " << (diffIn + control -> LatencyTimeout) / 1000 << " seconds.\n"; HandleAbort(); } else { #if defined(TEST) || defined(INFO) *logofs << "Proxy: WARNING! No data received from " << "remote proxy on FD#" << fd_ << " since " << diffIn << " Ms.\n" << logofs_flush; #endif if (control -> ProxyTimeout > 0 && isTimestamp(timeouts_.alertTs) == 0 && diffIn >= (control -> ProxyTimeout - control -> LatencyTimeout) / 4) { // // If we are in the middle of a shutdown // procedure but the remote is not resp- // onding, force the closure of the link. // if (finish_ != 0) { #ifdef PANIC *logofs << "Proxy: PANIC! No response received from " << "the remote proxy on FD#" << fd_ << " while " << "waiting for the shutdown.\n" << logofs_flush; #endif cerr << "Error" << ": No response received from remote " << "proxy while waiting for the shutdown.\n"; HandleAbort(); } else { cerr << "Warning" << ": No data received from remote " << "proxy within " << (diffIn + control -> LatencyTimeout) / 1000 << " seconds.\n"; if (alert_ == 0) { if (control -> ProxyMode == proxy_client) { alert_ = CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT; } else { alert_ = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT; } HandleAlert(alert_, 1); } timeouts_.alertTs = nowTs; } } } } // // Check if we need to update the congestion // counter. // int diffOut = diffTimestamp(timeouts_.writeTs, nowTs); if (agent_ != nothing && congestions_[agent_] == 0 && statistics -> getCongestionInFrame() >= 1 && diffOut >= (control -> IdleTimeout - control -> LatencyTimeout * 5)) { #if defined(TEST) || defined(INFO) || defined(PING) *logofs << "Proxy: Forcing an update of the " << "congestion counter after timeout.\n" << logofs_flush; #endif statistics -> updateCongestion(tokens_[token_control].remaining, tokens_[token_control].limit); } // // Send a new token if we didn't send any data to // the remote for longer than the ping timeout. // The client side sends a token, the server side // responds with a token reply. // // VMWare virtual machines can have the system // timer deadly broken. Try to send a ping regard- // less we are the client or the server proxy to // force a write by the remote. // if (control -> ProxyMode == proxy_client || diffIn >= (control -> PingTimeout * 4) - control -> LatencyTimeout) { // // We need to send a new ping even if we didn't // receive anything from the remote within the // ping timeout. The server side will respond // to our ping, so we use the ping to force the // remote end to send some data. // if (diffIn >= (control -> PingTimeout - control -> LatencyTimeout * 5) || diffOut >= (control -> PingTimeout - control -> LatencyTimeout * 5)) { int diffPing = diffTimestamp(timeouts_.pingTs, nowTs); if (diffPing < 0 || diffPing >= (control -> PingTimeout - control -> LatencyTimeout * 5)) { #if defined(TEST) || defined(INFO) || defined(PING) *logofs << "Proxy: Sending a new ping at " << strMsTimestamp() << " with " << tokens_[token_control].remaining << " tokens and elapsed in " << diffIn << " out " << diffOut << " ping " << diffPing << ".\n" << logofs_flush; #endif if (handleFrame(frame_ping) < 0) { return -1; } timeouts_.pingTs = nowTs; } #if defined(TEST) || defined(INFO) || defined(PING) else { *logofs << "Proxy: Not sending a new ping with " << "elapsed in " << diffIn << " out " << diffOut << " ping " << diffPing << ".\n" << logofs_flush; } #endif } } return 1; } int Proxy::handleSyncFromProxy(int channelId) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: WARNING! Received a synchronization " << "request from the remote proxy.\n" << logofs_flush; #endif if (handleControl(code_sync_reply, channelId) < 0) { return -1; } return 1; } int Proxy::handleResetStores() { // // Recreate the message stores. // delete clientStore_; delete serverStore_; clientStore_ = new ClientStore(compressor_); serverStore_ = new ServerStore(compressor_); timeouts_.loadTs = nullTimestamp(); // // Replace message stores in channels. // T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] != NULL) { if (channels_[channelId] -> setStores(clientStore_, serverStore_) < 0) { #ifdef PANIC *logofs << "Proxy: PANIC! Failed to replace message stores in " << "channel for FD#" << getFd(channelId) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failed to replace message stores in " << "channel for FD#" << getFd(channelId) << ".\n"; return -1; } #ifdef TEST else { *logofs << "Proxy: Replaced message stores in channel " << "for FD#" << getFd(channelId) << ".\n" << logofs_flush; } #endif } } return 1; } int Proxy::handleResetPersistentCache() { char *fullName = new char[strlen(control -> PersistentCachePath) + strlen(control -> PersistentCacheName) + 2]; strcpy(fullName, control -> PersistentCachePath); strcat(fullName, "/"); strcat(fullName, control -> PersistentCacheName); #ifdef TEST *logofs << "Proxy: Going to remove persistent cache file '" << fullName << "'\n" << logofs_flush; #endif unlink(fullName); delete [] fullName; delete [] control -> PersistentCacheName; control -> PersistentCacheName = NULL; return 1; } void Proxy::handleResetFlush() { #ifdef TEST *logofs << "Proxy: Going to reset flush counters " << "for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif // // Reset the proxy priority flag. // priority_ = 0; // // Restore buffers to their initial // size. // transport_ -> partialReset(); // // Update the timestamp of the last // write operation performed on the // socket. // timeouts_.writeTs = getTimestamp(); } int Proxy::handleFinish() { // // Reset the timestamps to give the proxy // another chance to show the 'no response' // dialog if the shutdown message doesn't // come in time. // timeouts_.readTs = getTimestamp(); timeouts_.alertTs = nullTimestamp(); finish_ = 1; return 1; } int Proxy::handleShutdown() { // // Send shutdown message to remote proxy. // shutdown_ = 1; handleControl(code_shutdown_request); #ifdef TEST *logofs << "Proxy: Starting shutdown procedure " << "for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif // // Ensure that all the data accumulated // in the transport buffer is flushed // to the network layer. // for (int i = 0; i < 100; i++) { if (canFlush() == 1) { handleFlush(); } else { break; } usleep(100000); } // // Now wait for the network layers to // consume all the data. // for (int i = 0; i < 100; i++) { if (transport_ -> queued() <= 0) { break; } usleep(100000); } // // Give time to the remote end to read // the shutdown message and close the // connection. // transport_ -> wait(10000); #ifdef TEST *logofs << "Proxy: Ending shutdown procedure " << "for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif return 1; } int Proxy::handleChannelConfiguration() { if (activeChannels_.getSize() == 0) { #ifdef TEST *logofs << "Proxy: Going to initialize the static " << "members in channels for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif Channel::setReferences(); ClientChannel::setReferences(); ServerChannel::setReferences(); GenericChannel::setReferences(); } return 1; } int Proxy::handleSocketConfiguration() { // // Set linger mode on proxy to correctly // get shutdown notification. // SetLingerTimeout(fd_, 30); // // Set keep-alive on socket so that if remote link // terminates abnormally (as killed hard or because // of a power-off) process will get a SIGPIPE. In // practice this is useless as proxies already ping // each other every few seconds. // if (control -> OptionProxyKeepAlive == 1) { SetKeepAlive(fd_); } // // Set 'priority' flag at TCP layer for path // proxy-to-proxy. Look at IPTOS_LOWDELAY in // man 7 ip. // if (control -> OptionProxyLowDelay == 1) { SetLowDelay(fd_); } // // Update size of TCP send and receive buffers. // if (control -> OptionProxySendBuffer != -1) { SetSendBuffer(fd_, control -> OptionProxySendBuffer); } if (control -> OptionProxyReceiveBuffer != -1) { SetReceiveBuffer(fd_, control -> OptionProxyReceiveBuffer); } // // Update TCP_NODELAY settings. Note that on old Linux // kernels turning off the Nagle algorithm didn't work // when proxy was run through a PPP link. Trying to do // so caused the kernel to stop delivering data to us // if a serious network congestion was encountered. // if (control -> ProxyMode == proxy_client) { if (control -> OptionProxyClientNoDelay != -1) { SetNoDelay(fd_, control -> OptionProxyClientNoDelay); } } else { if (control -> OptionProxyServerNoDelay != -1) { SetNoDelay(fd_, control -> OptionProxyServerNoDelay); } } return 1; } int Proxy::handleLinkConfiguration() { #ifdef TEST *logofs << "Proxy: Propagating parameters to " << "channels' read buffers.\n" << logofs_flush; #endif T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] != NULL) { channels_[channelId] -> handleConfiguration(); } } #ifdef TEST *logofs << "Proxy: Propagating parameters to " << "proxy buffers.\n" << logofs_flush; #endif readBuffer_.setSize(control -> ProxyInitialReadSize, control -> ProxyMaximumBufferSize); encodeBuffer_.setSize(control -> TransportProxyBufferSize, control -> TransportProxyBufferThreshold, control -> TransportMaximumBufferSize); transport_ -> setSize(control -> TransportProxyBufferSize, control -> TransportProxyBufferThreshold, control -> TransportMaximumBufferSize); #ifdef TEST *logofs << "Proxy: Configuring the proxy timeouts.\n" << logofs_flush; #endif timeouts_.split = control -> SplitTimeout; timeouts_.motion = control -> MotionTimeout; #ifdef TEST *logofs << "Proxy: Configuring the proxy tokens.\n" << logofs_flush; #endif tokens_[token_control].size = control -> TokenSize; tokens_[token_control].limit = control -> TokenLimit; if (tokens_[token_control].limit < 1) { tokens_[token_control].limit = 1; } #if defined(TEST) || defined(INFO) || defined(LIMIT) *logofs << "Proxy: TOKEN! LIMIT! Setting token [" << DumpToken(token_control) << "] size to " << tokens_[token_control].size << " and limit to " << tokens_[token_control].limit << ".\n" << logofs_flush; #endif tokens_[token_split].size = control -> TokenSize; tokens_[token_split].limit = control -> TokenLimit / 2; if (tokens_[token_split].limit < 1) { tokens_[token_split].limit = 1; } #if defined(TEST) || defined(INFO) || defined(LIMIT) *logofs << "Proxy: TOKEN! LIMIT! Setting token [" << DumpToken(token_split) << "] size to " << tokens_[token_split].size << " and limit to " << tokens_[token_split].limit << ".\n" << logofs_flush; #endif tokens_[token_data].size = control -> TokenSize; tokens_[token_data].limit = control -> TokenLimit / 4; if (tokens_[token_data].limit < 1) { tokens_[token_data].limit = 1; } #if defined(TEST) || defined(INFO) || defined(LIMIT) *logofs << "Proxy: TOKEN! LIMIT! Setting token [" << DumpToken(token_data) << "] size to " << tokens_[token_data].size << " and limit to " << tokens_[token_data].limit << ".\n" << logofs_flush; #endif for (int i = token_control; i <= token_data; i++) { tokens_[i].remaining = tokens_[i].limit; } #if defined(TEST) || defined(INFO) || defined(LIMIT) *logofs << "Proxy: LIMIT! Using client bitrate " << "limit " << control -> ClientBitrateLimit << " server bitrate limit " << control -> ServerBitrateLimit << " with local limit " << control -> LocalBitrateLimit << ".\n" << logofs_flush; #endif // // Set the other parameters based on // the token size. // int base = control -> TokenSize; control -> SplitDataThreshold = base * 4; control -> SplitDataPacketLimit = base / 2; #if defined(TEST) || defined(INFO) *logofs << "Proxy: LIMIT! Setting split data threshold " << "to " << control -> SplitDataThreshold << " split packet limit to " << control -> SplitDataPacketLimit << " with base " << base << ".\n" << logofs_flush; #endif // // Set the number of bytes read from the // data channels at each loop. This will // basically determine the maximum band- // width available for the generic chan- // nels. // control -> GenericInitialReadSize = base / 2; control -> GenericMaximumBufferSize = base / 2; #if defined(TEST) || defined(INFO) *logofs << "Proxy: LIMIT! Setting generic channel " << "initial read size to " << control -> GenericInitialReadSize << " maximum read " << "size to " << control -> GenericMaximumBufferSize << " with base " << base << ".\n" << logofs_flush; #endif return 1; } int Proxy::handleCacheConfiguration() { #ifdef TEST *logofs << "Proxy: Configuring cache according to pack parameters.\n" << logofs_flush; #endif // // Further adjust the cache parameters. If // packing of the images is enabled, reduce // the size available for plain images. // if (control -> SessionMode == session_agent) { if (control -> PackMethod != NO_PACK) { clientStore_ -> getRequestStore(X_PutImage) -> cacheThreshold = PUTIMAGE_CACHE_THRESHOLD_IF_PACKED; clientStore_ -> getRequestStore(X_PutImage) -> cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED; } } // // If this is a shadow session increase the // size of the image cache. // if (control -> SessionMode == session_shadow) { if (control -> PackMethod != NO_PACK) { clientStore_ -> getRequestStore(X_NXPutPackedImage) -> cacheThreshold = PUTPACKEDIMAGE_CACHE_THRESHOLD_IF_PACKED_SHADOW; clientStore_ -> getRequestStore(X_NXPutPackedImage) -> cacheLowerThreshold = PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED_SHADOW; } else { clientStore_ -> getRequestStore(X_PutImage) -> cacheThreshold = PUTIMAGE_CACHE_THRESHOLD_IF_SHADOW; clientStore_ -> getRequestStore(X_PutImage) -> cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_SHADOW; } } return 1; } int Proxy::handleSaveStores() { // // Save content of stores on disk. // char *cacheToAdopt = NULL; if (control -> PersistentCacheEnableSave) { #ifdef TEST *logofs << "Proxy: Going to save content of client store.\n" << logofs_flush; #endif cacheToAdopt = handleSaveAllStores(control -> PersistentCachePath); } #ifdef TEST else { if (control -> ProxyMode == proxy_client) { *logofs << "Proxy: Saving persistent cache to disk disabled.\n" << logofs_flush; } else { *logofs << "Proxy: PANIC! Protocol violation in command save.\n" << logofs_flush; cerr << "Error" << ": Protocol violation in command save.\n"; HandleCleanup(); } } #endif if (cacheToAdopt != NULL) { // // Do we have a cache already? // if (control -> PersistentCacheName != NULL) { // // Check if old and new cache are the same. // In this case don't remove the old cache. // if (strcasecmp(control -> PersistentCacheName, cacheToAdopt) != 0) { handleResetPersistentCache(); } delete [] control -> PersistentCacheName; } #ifdef TEST *logofs << "Proxy: Setting current persistent cache file to '" << cacheToAdopt << "'\n" << logofs_flush; #endif control -> PersistentCacheName = cacheToAdopt; return 1; } #ifdef TEST else { *logofs << "Proxy: No cache file produced from message stores.\n" << logofs_flush; } #endif // // It can be that we didn't generate a new cache // because store was too small or persistent cache // was disabled. This is not an error. // return 0; } int Proxy::handleLoadStores() { // // Restore the content of the client store // from disk if a valid cache was negotiated // at session startup. // if (control -> PersistentCacheEnableLoad == 1 && control -> PersistentCachePath != NULL && control -> PersistentCacheName != NULL) { #ifdef TEST *logofs << "Proxy: Going to load content of client store.\n" << logofs_flush; #endif // // Returns the same string passed as name of // the cache, or NULL if it was not possible // to load the cache from disk. // if (handleLoadAllStores(control -> PersistentCachePath, control -> PersistentCacheName) == NULL) { // // The corrupted cache should have been // removed from disk. Get rid of the // reference so we don't try to delete // it once again. // if (control -> PersistentCacheName != NULL) { delete [] control -> PersistentCacheName; } control -> PersistentCacheName = NULL; return -1; } // // Set timestamp of last time cache // was loaded from data on disk. // timeouts_.loadTs = getTimestamp(); return 1; } #ifdef TEST else { if (control -> ProxyMode == proxy_client) { *logofs << "Proxy: Loading of cache disabled or no cache file selected.\n" << logofs_flush; } else { *logofs << "Proxy: PANIC! Protocol violation in command load.\n" << logofs_flush; cerr << "Error" << ": Protocol violation in command load.\n"; HandleCleanup(); } } #endif return 0; } int Proxy::handleControl(T_proxy_code code, int data) { // // Send the given control messages // to the remote proxy. // #if defined(TEST) || defined(INFO) if (data != -1) { if (code == code_control_token_reply || code == code_split_token_reply || code == code_data_token_reply) { *logofs << "Proxy: TOKEN! Sending message '" << DumpControl(code) << "' at " << strMsTimestamp() << " with count " << data << ".\n" << logofs_flush; } else { *logofs << "Proxy: Sending message '" << DumpControl(code) << "' at " << strMsTimestamp() << " with data ID#" << data << ".\n" << logofs_flush; } } else { *logofs << "Proxy: Sending message '" << DumpControl(code) << "' at " << strMsTimestamp() << ".\n" << logofs_flush; } #endif // // Add the control message and see if the // data has to be flushed immediately. // if (addControlCodes(code, data) < 0) { return -1; } switch (code) { // // Append the first data read from the opened // channel to the control code. // case code_new_x_connection: case code_new_cups_connection: case code_new_aux_connection: case code_new_smb_connection: case code_new_media_connection: case code_new_http_connection: case code_new_font_connection: case code_new_slave_connection: // // Do we send the token reply immediately? // The control messages are put at the begin- // ning of the of the encode buffer, so we may // reply to multiple tokens before having the // chance of handling the actual frame data. // On the other hand, the sooner we reply, the // sooner the remote proxy is restarted. // case code_control_token_reply: case code_split_token_reply: case code_data_token_reply: { break; } // // Also send the congestion control codes // immediately. // // case code_begin_congestion: // case code_end_congestion: // default: { priority_ = 1; break; } } if (priority_ == 1) { if (handleFrame(frame_data) < 0) { return -1; } } return 1; } int Proxy::handleSwitch(int channelId) { // // If data is for a different channel than last // selected for output, prepend to the data the // new channel id. // #ifdef DEBUG *logofs << "Proxy: Requested a switch with " << "current channel ID#" << outputChannel_ << " new channel ID#" << channelId << ".\n" << logofs_flush; #endif if (channelId != outputChannel_) { if (needFlush() == 1) { #if defined(TEST) || defined(INFO) || defined(FLUSH) *logofs << "Proxy: WARNING! Flushing data for the " << "previous channel ID#" << outputChannel_ << ".\n" << logofs_flush; #endif if (handleFrame(frame_data) < 0) { return -1; } } #if defined(TEST) || defined(INFO) *logofs << "Proxy: Sending message '" << DumpControl(code_switch_connection) << "' at " << strMsTimestamp() << " with FD#" << getFd(channelId) << " channel ID#" << channelId << ".\n" << logofs_flush; #endif if (addControlCodes(code_switch_connection, channelId) < 0) { return -1; } outputChannel_ = channelId; } return 1; } int Proxy::addTokenCodes(T_proxy_token &token) { #if defined(TEST) || defined(INFO) || defined(TOKEN) *logofs << "Proxy: TOKEN! Sending token [" << DumpToken(token.type) << "] with " << token.bytes << " bytes accumulated size " << token.size << " and " << token.remaining << " available.\n" << logofs_flush; #endif // // Give a 'weight' to the token. The tokens // remaining can become negative if we sent // a packet that was exceptionally big. // int count = 0; if (control -> isProtoStep7() == 1) { count = token.bytes / token.size; if (count > 255) { count = 255; } } // // Force a count of 1, for example // if this is a ping. // if (count < 1) { count = 1; token.bytes = 0; } else { // // Let the next token account for the // remaining bytes. // token.bytes %= token.size; } #if defined(TEST) || defined(INFO) || defined(TOKEN) *logofs << "Proxy: Sending message '" << DumpControl(token.request) << "' at " << strMsTimestamp() << " with count " << count << ".\n" << logofs_flush; #endif controlCodes_[controlLength_++] = 0; controlCodes_[controlLength_++] = (unsigned char) token.request; controlCodes_[controlLength_++] = (unsigned char) count; statistics -> addFrameOut(); token.remaining -= count; return 1; } int Proxy::handleToken(T_frame_type type) { #if defined(TEST) || defined(INFO) || defined(TOKEN) *logofs << "Proxy: TOKEN! Checking tokens with " << "frame type ["; *logofs << (type == frame_ping ? "frame_ping" : "frame_data"); *logofs << "] with stream ratio " << statistics -> getStreamRatio() << ".\n" << logofs_flush; #endif if (type == frame_data) { if (control -> isProtoStep7() == 1) { // // Send a distinct token for each data type. // We don't want to slow down the sending of // the X events, X replies and split confir- // mation events on the X server side, so // take care only of the generic data token. // if (control -> ProxyMode == proxy_client) { statistics -> updateControlToken(tokens_[token_control].bytes); if (tokens_[token_control].bytes > tokens_[token_control].size) { if (addTokenCodes(tokens_[token_control]) < 0) { return -1; } #if defined(TEST) || defined(INFO) || defined(TOKEN) T_proxy_token &token = tokens_[token_control]; *logofs << "Proxy: TOKEN! Token class [" << DumpToken(token.type) << "] has now " << token.bytes << " bytes accumulated and " << token.remaining << " tokens remaining.\n" << logofs_flush; #endif } statistics -> updateSplitToken(tokens_[token_split].bytes); if (tokens_[token_split].bytes > tokens_[token_split].size) { if (addTokenCodes(tokens_[token_split]) < 0) { return -1; } #if defined(TEST) || defined(INFO) || defined(TOKEN) T_proxy_token &token = tokens_[token_split]; *logofs << "Proxy: TOKEN! Token class [" << DumpToken(token.type) << "] has now " << token.bytes << " bytes accumulated and " << token.remaining << " tokens remaining.\n" << logofs_flush; #endif } } statistics -> updateDataToken(tokens_[token_data].bytes); if (tokens_[token_data].bytes > tokens_[token_data].size) { if (addTokenCodes(tokens_[token_data]) < 0) { return -1; } #if defined(TEST) || defined(INFO) || defined(TOKEN) T_proxy_token &token = tokens_[token_data]; *logofs << "Proxy: TOKEN! Token class [" << DumpToken(token.type) << "] has now " << token.bytes << " bytes accumulated and " << token.remaining << " tokens remaining.\n" << logofs_flush; #endif } } else { // // Sum everything to the control token. // if (control -> ProxyMode == proxy_client) { statistics -> updateControlToken(tokens_[token_control].bytes); statistics -> updateSplitToken(tokens_[token_control].bytes); statistics -> updateDataToken(tokens_[token_control].bytes); if (tokens_[token_control].bytes > tokens_[token_control].size) { if (addTokenCodes(tokens_[token_control]) < 0) { return -1; } #if defined(TEST) || defined(INFO) || defined(TOKEN) T_proxy_token &token = tokens_[token_control]; *logofs << "Proxy: TOKEN! Token class [" << DumpToken(token.type) << "] has now " << token.bytes << " bytes accumulated and " << token.remaining << " tokens remaining.\n" << logofs_flush; #endif } } } } else { if (addTokenCodes(tokens_[token_control]) < 0) { return -1; } // // Reset all counters on a ping. // tokens_[token_control].bytes = 0; tokens_[token_split].bytes = 0; tokens_[token_data].bytes = 0; #if defined(TEST) || defined(INFO) || defined(TOKEN) T_proxy_token &token = tokens_[token_control]; *logofs << "Proxy: TOKEN! Token class [" << DumpToken(token.type) << "] has now " << token.bytes << " bytes accumulated and " << token.remaining << " tokens remaining.\n" << logofs_flush; #endif } // // Check if we have entered in // congestion state. // if (congestion_ == 0 && tokens_[token_control].remaining <= 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Entering congestion with " << tokens_[token_control].remaining << " tokens remaining.\n" << logofs_flush; #endif congestion_ = 1; } statistics -> updateCongestion(tokens_[token_control].remaining, tokens_[token_control].limit); return 1; } int Proxy::handleTokenFromProxy(T_proxy_token &token, int count) { #if defined(TEST) || defined(INFO) || defined(TOKEN) *logofs << "Proxy: TOKEN! Received token [" << DumpToken(token.type) << "] request at " << strMsTimestamp() << " with count " << count << ".\n" << logofs_flush; #endif if (control -> isProtoStep7() == 0) { if (control -> ProxyMode == proxy_client || token.request != code_control_token_request) { #ifdef PANIC *logofs << "Proxy: PANIC! Invalid token request received from remote.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid token request received from remote.\n"; HandleCleanup(); } } // // Add our token reply. // if (handleControl(token.reply, count) < 0) { return -1; } return 1; } int Proxy::handleTokenReplyFromProxy(T_proxy_token &token, int count) { #if defined(TEST) || defined(INFO) || defined(TOKEN) *logofs << "Proxy: TOKEN! Received token [" << DumpToken(token.type) << "] reply at " << strMsTimestamp() << " with count " << count << ".\n" << logofs_flush; #endif // // Increment the available tokens. // if (control -> isProtoStep7() == 0) { if (token.reply != code_control_token_reply) { #ifdef PANIC *logofs << "Proxy: PANIC! Invalid token reply received from remote.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid token reply received from remote.\n"; HandleCleanup(); } count = 1; } token.remaining += count; if (token.remaining > token.limit) { #ifdef PANIC *logofs << "Proxy: PANIC! Token overflow handling messages.\n" << logofs_flush; #endif cerr << "Error" << ": Token overflow handling messages.\n"; HandleCleanup(); } #if defined(TEST) || defined(INFO) || defined(TOKEN) *logofs << "Proxy: TOKEN! Token class [" << DumpToken(token.type) << "] has now " << token.bytes << " bytes accumulated and " << token.remaining << " tokens remaining.\n" << logofs_flush; #endif // // Check if we can jump out of the // congestion state. // if (congestion_ == 1 && tokens_[token_control].remaining > 0) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: Exiting congestion with " << tokens_[token_control].remaining << " tokens remaining.\n" << logofs_flush; #endif congestion_ = 0; } statistics -> updateCongestion(tokens_[token_control].remaining, tokens_[token_control].limit); return 1; } void Proxy::handleFailOnSave(const char *fullName, const char *failContext) const { #ifdef WARNING *logofs << "Proxy: WARNING! Error saving stores to cache file " << "in context [" << failContext << "].\n" << logofs_flush; #endif cerr << "Warning" << ": Error saving stores to cache file " << "in context [" << failContext << "].\n"; #ifdef WARNING *logofs << "Proxy: WARNING! Removing invalid cache '" << fullName << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Removing invalid cache '" << fullName << "'.\n"; unlink(fullName); } void Proxy::handleFailOnLoad(const char *fullName, const char *failContext) const { #ifdef WARNING *logofs << "Proxy: WARNING! Error loading stores from cache file " << "in context [" << failContext << "].\n" << logofs_flush; #endif cerr << "Warning" << ": Error loading stores from cache file " << "in context [" << failContext << "].\n"; #ifdef WARNING *logofs << "Proxy: WARNING! Removing invalid cache '" << fullName << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Removing invalid cache '" << fullName << "'.\n"; unlink(fullName); } int Proxy::handleSaveVersion(unsigned char *buffer, int &major, int &minor, int &patch) const { if (control -> isProtoStep8() == 1) { major = 3; minor = 0; patch = 0; } else if (control -> isProtoStep7() == 1) { major = 2; minor = 0; patch = 0; } else { major = 1; minor = 4; patch = 0; } *(buffer + 0) = major; *(buffer + 1) = minor; PutUINT(patch, buffer + 2, storeBigEndian()); return 1; } int Proxy::handleLoadVersion(const unsigned char *buffer, int &major, int &minor, int &patch) const { major = *(buffer + 0); minor = *(buffer + 1); patch = GetUINT(buffer + 2, storeBigEndian()); // // Force the proxy to discard the // incompatible caches. // if (control -> isProtoStep8() == 1) { if (major < 3) { return -1; } } else if (control -> isProtoStep7() == 1) { if (major < 2) { return -1; } } else { if (major != 1 && minor != 4) { return -1; } } return 1; } char *Proxy::handleSaveAllStores(const char *savePath) const { int cumulativeSize = MessageStore::getCumulativeTotalStorageSize(); if (cumulativeSize < control -> PersistentCacheThreshold) { #ifdef TEST *logofs << "Proxy: Cache not saved as size is " << cumulativeSize << " with threshold set to " << control -> PersistentCacheThreshold << ".\n" << logofs_flush; #endif return NULL; } else if (savePath == NULL) { #ifdef PANIC *logofs << "Proxy: PANIC! No name provided for save path.\n" << logofs_flush; #endif cerr << "Error" << ": No name provided for save path.\n"; return NULL; } #ifdef TEST *logofs << "Proxy: Going to save content of message stores.\n" << logofs_flush; #endif // // Our parent process is likely going to terminate. // Until we finish saving cache we must ignore its // SIGIPE. // DisableSignals(); ofstream *cachefs = NULL; md5_state_t *md5StateStream = NULL; md5_byte_t *md5DigestStream = NULL; md5_state_t *md5StateClient = NULL; md5_byte_t *md5DigestClient = NULL; char *tempName = NULL; char md5String[MD5_LENGTH * 2 + 2]; char fullName[strlen(savePath) + MD5_LENGTH * 2 + 4]; if (control -> ProxyMode == proxy_client) { tempName = tempnam(savePath, "Z-C-"); } else { tempName = tempnam(savePath, "Z-S-"); } // // Change the mask to make the file only // readable by the user, then restore the // old mask. // mode_t fileMode = umask(0077); cachefs = new ofstream(tempName, ios::out | ios::binary); umask(fileMode); if (tempName == NULL || cachefs == NULL) { #ifdef PANIC *logofs << "Proxy: PANIC! Can't create temporary file in '" << savePath << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't create temporary file in '" << savePath << "'.\n"; if (tempName != NULL) { free(tempName); } if (cachefs != NULL) { delete cachefs; } EnableSignals(); return NULL; } md5StateStream = new md5_state_t(); md5DigestStream = new md5_byte_t[MD5_LENGTH]; md5_init(md5StateStream); // // First write the proxy version. // unsigned char version[4]; int major; int minor; int patch; handleSaveVersion(version, major, minor, patch); #ifdef TEST *logofs << "Proxy: Saving cache using version '" << major << "." << minor << "." << patch << "'.\n" << logofs_flush; #endif if (PutData(cachefs, version, 4) < 0) { handleFailOnSave(tempName, "A"); delete cachefs; delete md5StateStream; delete [] md5DigestStream; free(tempName); EnableSignals(); return NULL; } // // Make space for the calculated MD5 so we // can later rewind the file and write it // at this position. // if (PutData(cachefs, md5DigestStream, MD5_LENGTH) < 0) { handleFailOnSave(tempName, "B"); delete cachefs; delete md5StateStream; delete [] md5DigestStream; free(tempName); EnableSignals(); return NULL; } md5StateClient = new md5_state_t(); md5DigestClient = new md5_byte_t[MD5_LENGTH]; md5_init(md5StateClient); #ifdef DUMP ofstream *cacheDump = NULL; ofstream *tempfs = (ofstream*) logofs; char cacheDumpName[DEFAULT_STRING_LENGTH]; if (control -> ProxyMode == proxy_client) { snprintf(cacheDumpName, DEFAULT_STRING_LENGTH - 1, "%s/client-cache-dump", control -> TempPath); } else { snprintf(cacheDumpName, DEFAULT_STRING_LENGTH - 1, "%s/server-cache-dump", control -> TempPath); } *(cacheDumpName + DEFAULT_STRING_LENGTH - 1) = '\0'; mode_t fileMode = umask(0077); cacheDump = new ofstream(cacheDumpName, ios::out); umask(fileMode); logofs = cacheDump; #endif // // Use the virtual method of the concrete proxy class. // int allSaved = handleSaveAllStores(cachefs, md5StateStream, md5StateClient); #ifdef DUMP logofs = tempfs; delete cacheDump; #endif if (allSaved == 0) { handleFailOnSave(tempName, "C"); delete cachefs; delete md5StateStream; delete [] md5DigestStream; delete md5StateClient; delete [] md5DigestClient; free(tempName); EnableSignals(); return NULL; } md5_finish(md5StateClient, md5DigestClient); for (unsigned int i = 0; i < MD5_LENGTH; i++) { sprintf(md5String + (i * 2), "%02X", md5DigestClient[i]); } strcpy(fullName, (control -> ProxyMode == proxy_client) ? "C-" : "S-"); strcat(fullName, md5String); md5_append(md5StateStream, (const md5_byte_t *) fullName, strlen(fullName)); md5_finish(md5StateStream, md5DigestStream); // // Go to the beginning of file plus // the integer where we wrote our // proxy version. // cachefs -> seekp(4); if (PutData(cachefs, md5DigestStream, MD5_LENGTH) < 0) { handleFailOnSave(tempName, "D"); delete cachefs; delete md5StateStream; delete [] md5DigestStream; delete md5StateClient; delete [] md5DigestClient; free(tempName); EnableSignals(); return NULL; } delete cachefs; // // Copy the resulting cache name without // the path so that we can return it to // the caller. // char *cacheName = new char[MD5_LENGTH * 2 + 4]; strcpy(cacheName, fullName); // // Add the path to the full name and move // the cache into the path. // strcpy(fullName, savePath); strcat(fullName, (control -> ProxyMode == proxy_client) ? "/C-" : "/S-"); strcat(fullName, md5String); #ifdef TEST *logofs << "Proxy: Renaming cache file from '" << tempName << "' to '" << fullName << "'.\n" << logofs_flush; #endif rename(tempName, fullName); delete md5StateStream; delete [] md5DigestStream; delete md5StateClient; delete [] md5DigestClient; free(tempName); // // Restore the original handlers. // EnableSignals(); #ifdef TEST *logofs << "Proxy: Successfully saved cache file '" << cacheName << "'.\n" << logofs_flush; #endif // // This must be enabled only for test // because it requires that client and // server reside on the same machine. // if (control -> PersistentCacheCheckOnShutdown == 1 && control -> ProxyMode == proxy_server) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: MATCH! Checking if the file '" << fullName << "' matches a client cache.\n" << logofs_flush; #endif strcpy(fullName, savePath); strcat(fullName, "/C-"); strcat(fullName, md5String); struct stat fileStat; if (stat(fullName, &fileStat) != 0) { #ifdef PANIC *logofs << "Proxy: PANIC! Can't find a client cache " << "with name '" << fullName << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't find a client cache " << "with name '" << fullName << "'.\n"; HandleShutdown(); } #if defined(TEST) || defined(INFO) *logofs << "Proxy: MATCH! Client cache '" << fullName << "' matches the local cache.\n" << logofs_flush; #endif } return cacheName; } const char *Proxy::handleLoadAllStores(const char *loadPath, const char *loadName) const { #ifdef TEST *logofs << "Proxy: Going to load content of message stores.\n" << logofs_flush; #endif // // Until we finish loading cache we // must at least ignore any SIGIPE. // DisableSignals(); if (loadPath == NULL || loadName == NULL) { #ifdef PANIC *logofs << "Proxy: PANIC! No path or no file name provided for cache to restore.\n" << logofs_flush; #endif cerr << "Error" << ": No path or no file name provided for cache to restore.\n"; EnableSignals(); return NULL; } else if (strlen(loadName) != MD5_LENGTH * 2 + 2) { #ifdef PANIC *logofs << "Proxy: PANIC! Bad file name provided for cache to restore.\n" << logofs_flush; #endif cerr << "Error" << ": Bad file name provided for cache to restore.\n"; EnableSignals(); return NULL; } istream *cachefs = NULL; char md5String[(MD5_LENGTH * 2) + 2]; md5_byte_t md5FromFile[MD5_LENGTH]; char *cacheName = new char[strlen(loadPath) + strlen(loadName) + 3]; strcpy(cacheName, loadPath); strcat(cacheName, "/"); strcat(cacheName, loadName); #ifdef TEST *logofs << "Proxy: Name of cache file is '" << cacheName << "'.\n" << logofs_flush; #endif cachefs = new ifstream(cacheName, ios::in | ios::binary); unsigned char version[4]; if (cachefs == NULL || GetData(cachefs, version, 4) < 0) { #ifdef PANIC *logofs << "Proxy: PANIC! Can't read cache file '" << cacheName << "'.\n" << logofs_flush;; #endif handleFailOnLoad(cacheName, "A"); delete cachefs; delete [] cacheName; EnableSignals(); return NULL; } int major; int minor; int patch; if (handleLoadVersion(version, major, minor, patch) < 0) { #ifdef PANIC *logofs << "Proxy: WARNING! Incompatible version '" << major << "." << minor << "." << patch << "' in cache file '" << cacheName << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Incompatible version '" << major << "." << minor << "." << patch << "' in cache file '" << cacheName << "'.\n" << logofs_flush; if (control -> ProxyMode == proxy_server) { handleFailOnLoad(cacheName, "B"); } else { // // Simply remove the cache file. // unlink(cacheName); } delete cachefs; delete [] cacheName; EnableSignals(); return NULL; } #ifdef TEST *logofs << "Proxy: Reading from cache file version '" << major << "." << minor << "." << patch << "'.\n" << logofs_flush; #endif if (GetData(cachefs, md5FromFile, MD5_LENGTH) < 0) { #ifdef PANIC *logofs << "Proxy: PANIC! No checksum in cache file '" << loadName << "'.\n" << logofs_flush; #endif handleFailOnLoad(cacheName, "C"); delete cachefs; delete [] cacheName; EnableSignals(); return NULL; } md5_state_t *md5StateStream = NULL; md5_byte_t *md5DigestStream = NULL; md5StateStream = new md5_state_t(); md5DigestStream = new md5_byte_t[MD5_LENGTH]; md5_init(md5StateStream); // // Use the virtual method of the proxy class. // if (handleLoadAllStores(cachefs, md5StateStream) < 0) { handleFailOnLoad(cacheName, "D"); delete cachefs; delete md5StateStream; delete [] md5DigestStream; delete [] cacheName; EnableSignals(); return NULL; } md5_append(md5StateStream, (const md5_byte_t *) loadName, strlen(loadName)); md5_finish(md5StateStream, md5DigestStream); for (int i = 0; i < MD5_LENGTH; i++) { if (md5DigestStream[i] != md5FromFile[i]) { #ifdef PANIC *logofs << "Proxy: PANIC! Bad checksum for cache file '" << cacheName << "'.\n" << logofs_flush; for (unsigned int i = 0; i < MD5_LENGTH; i++) { sprintf(md5String + (i * 2), "%02X", md5FromFile[i]); } *logofs << "Proxy: PANIC! Saved checksum is '" << md5String << "'.\n" << logofs_flush; for (unsigned int i = 0; i < MD5_LENGTH; i++) { sprintf(md5String + (i * 2),"%02X", md5DigestStream[i]); } *logofs << "Proxy: PANIC! Calculated checksum is '" << md5String << "'.\n" << logofs_flush; #endif handleFailOnLoad(cacheName, "E"); delete cachefs; delete md5StateStream; delete [] md5DigestStream; delete [] cacheName; EnableSignals(); return NULL; } } delete cachefs; delete md5StateStream; delete [] md5DigestStream; delete [] cacheName; // // Restore the original handlers. // EnableSignals(); #ifdef TEST *logofs << "Proxy: Successfully loaded cache file '" << loadName << "'.\n" << logofs_flush; #endif // // Return the string provided by caller. // return loadName; } int Proxy::allocateChannelMap(int fd) { // // We assume that the fd is lower than // the maximum allowed number. This is // checked at the time the connection // is accepted. // if (fd < 0 || fd >= CONNECTIONS_LIMIT) { #ifdef PANIC *logofs << "Proxy: PANIC! Internal error allocating " << "new channel with FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Internal error allocating " << "new channel with FD#" << fd_ << ".\n"; HandleCleanup(); } for (int channelId = 0; channelId < CONNECTIONS_LIMIT; channelId++) { if (checkLocalChannelMap(channelId) == 1 && fdMap_[channelId] == -1) { fdMap_[channelId] = fd; channelMap_[fd] = channelId; #ifdef TEST *logofs << "Proxy: Allocated new channel ID#" << channelId << " with FD#" << fd << ".\n" << logofs_flush; #endif return channelId; } } // // No available channel is remaining. // #ifdef TEST *logofs << "Proxy: WARNING! Can't allocate a new channel " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif return -1; } int Proxy::checkChannelMap(int channelId) { // // To be acceptable, the channel id must // be an id that is not possible to use // to allocate channels at this side. // if (checkLocalChannelMap(channelId) == 1) { #ifdef PANIC *logofs << "Proxy: PANIC! Can't open a new channel " << "with invalid ID#" << channelId << ".\n" << logofs_flush; #endif cerr << "Error" << ": Can't open a new channel " << "with invalid ID#" << channelId << ".\n"; return -1; } else if (channels_[channelId] != NULL) { #ifdef PANIC *logofs << "Proxy: PANIC! Can't open a new channel " << "over an existing ID#" << channelId << " with FD#" << getFd(channelId) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Can't open a new channel " << "over an existing ID#" << channelId << " with FD#" << getFd(channelId) << ".\n"; return -1; } return 1; } int Proxy::assignChannelMap(int channelId, int fd) { // // We assume that the fd is lower than // the maximum allowed number. This is // checked at the time the connection // is accepted. // if (channelId < 0 || channelId >= CONNECTIONS_LIMIT || fd < 0 || fd >= CONNECTIONS_LIMIT) { #ifdef PANIC *logofs << "Proxy: PANIC! Internal error assigning " << "new channel with FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Internal error assigning " << "new channel with FD#" << fd_ << ".\n"; HandleCleanup(); } fdMap_[channelId] = fd; channelMap_[fd] = channelId; return 1; } void Proxy::cleanupChannelMap(int channelId) { int fd = fdMap_[channelId]; if (fd != -1) { fdMap_[channelId] = -1; channelMap_[fd] = -1; } } int Proxy::addControlCodes(T_proxy_code code, int data) { // // Flush the encode buffer plus all the outstanding // control codes if there is not enough space for // the new control message. We need to ensure that // there are further bytes available, in the case // we will need to add more token control messages. // if (controlLength_ + 3 > CONTROL_CODES_THRESHOLD) { #ifdef WARNING *logofs << "Proxy: WARNING! Flushing control messages " << "while sending code '" << DumpControl(code) << "'.\n" << logofs_flush; #endif if (handleFlush() < 0) { return -1; } } controlCodes_[controlLength_++] = 0; controlCodes_[controlLength_++] = (unsigned char) code; controlCodes_[controlLength_++] = (unsigned char) (data == -1 ? 0 : data); // // Account for the control frame. // statistics -> addFrameOut(); return 1; } void Proxy::setSplitTimeout(int channelId) { int needed = channels_[channelId] -> needSplit(); if (needed != isTimestamp(timeouts_.splitTs)) { if (needed == 1) { #if defined(TEST) || defined(INFO) || defined(SPLIT) *logofs << "Proxy: SPLIT! Allocating split timestamp at " << strMsTimestamp() << ".\n" << logofs_flush; #endif timeouts_.splitTs = getTimestamp(); } else { T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] != NULL && channels_[channelId] -> needSplit() == 1) { #ifdef TEST *logofs << "Proxy: SPLIT! Channel for FD#" << getFd(channelId) << " still needs splits.\n" << logofs_flush; #endif return; } } #if defined(TEST) || defined(INFO) || defined(SPLIT) *logofs << "Proxy: SPLIT! Resetting split timestamp at " << strMsTimestamp() << ".\n" << logofs_flush; #endif timeouts_.splitTs = nullTimestamp(); } } } void Proxy::setMotionTimeout(int channelId) { int needed = channels_[channelId] -> needMotion(); if (needed != isTimestamp(timeouts_.motionTs)) { if (channels_[channelId] -> needMotion() == 1) { #if defined(TEST) || defined(INFO) || defined(SPLIT) *logofs << "Proxy: Allocating motion timestamp at " << strMsTimestamp() << ".\n" << logofs_flush; #endif timeouts_.motionTs = getTimestamp(); } else { T_list &channelList = activeChannels_.getList(); for (T_list::iterator j = channelList.begin(); j != channelList.end(); j++) { int channelId = *j; if (channels_[channelId] != NULL && channels_[channelId] -> needMotion() == 1) { #ifdef TEST *logofs << "Proxy: SPLIT! Channel for FD#" << getFd(channelId) << " still needs motions.\n" << logofs_flush; #endif return; } } #if defined(TEST) || defined(INFO) || defined(SPLIT) *logofs << "Proxy: Resetting motion timestamp at " << strMsTimestamp() << ".\n" << logofs_flush; #endif timeouts_.motionTs = nullTimestamp(); } } } void Proxy::increaseChannels(int channelId) { #ifdef TEST *logofs << "Proxy: Adding channel " << channelId << " to the list of active channels.\n" << logofs_flush; #endif activeChannels_.add(channelId); #ifdef TEST *logofs << "Proxy: There are " << activeChannels_.getSize() << " allocated channels for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif } void Proxy::decreaseChannels(int channelId) { #ifdef TEST *logofs << "Proxy: Removing channel " << channelId << " from the list of active channels.\n" << logofs_flush; #endif activeChannels_.remove(channelId); #ifdef TEST *logofs << "Proxy: There are " << activeChannels_.getSize() << " allocated channels for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif } int Proxy::allocateTransport(int channelFd, int channelId) { if (transports_[channelId] == NULL) { transports_[channelId] = new Transport(channelFd); if (transports_[channelId] == NULL) { #ifdef PANIC *logofs << "Proxy: PANIC! Can't allocate transport for " << "channel id " << channelId << ".\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate transport for " << "channel id " << channelId << ".\n"; return -1; } } else if (transports_[channelId] -> getType() != transport_agent) { #ifdef PANIC *logofs << "Proxy: PANIC! Transport for channel id " << channelId << " should be null.\n" << logofs_flush; #endif cerr << "Error" << ": Transport for channel id " << channelId << " should be null.\n"; return -1; } return 1; } int Proxy::deallocateTransport(int channelId) { // // Transport for the agent connection // is passed from the outside when // creating the channel. // if (transports_[channelId] -> getType() != transport_agent) { delete transports_[channelId]; } transports_[channelId] = NULL; return 1; } int Proxy::handleNewGenericConnection(int clientFd, T_channel_type type, const char *label) { int channelId = allocateChannelMap(clientFd); if (channelId == -1) { #ifdef PANIC *logofs << "Proxy: PANIC! Maximum mumber of available " << "channels exceeded.\n" << logofs_flush; #endif cerr << "Error" << ": Maximum mumber of available " << "channels exceeded.\n"; return -1; } #ifdef TEST *logofs << "Proxy: Channel for " << label << " descriptor " << "FD#" << clientFd << " mapped to ID#" << channelId << ".\n" << logofs_flush; #endif // // Turn queuing off for path server-to-proxy. // SetNoDelay(clientFd, 1); if (allocateTransport(clientFd, channelId) < 0) { return -1; } switch (type) { case channel_cups: { channels_[channelId] = new CupsChannel(transports_[channelId], compressor_); break; } case channel_smb: { channels_[channelId] = new SmbChannel(transports_[channelId], compressor_); break; } case channel_media: { channels_[channelId] = new MediaChannel(transports_[channelId], compressor_); break; } case channel_http: { channels_[channelId] = new HttpChannel(transports_[channelId], compressor_); break; } case channel_font: { channels_[channelId] = new FontChannel(transports_[channelId], compressor_); break; } default: { channels_[channelId] = new SlaveChannel(transports_[channelId], compressor_); break; } } if (channels_[channelId] == NULL) { deallocateTransport(channelId); return -1; } #ifdef TEST *logofs << "Proxy: Accepted new connection to " << label << " server.\n" << logofs_flush; #endif cerr << "Info" << ": Accepted new connection to " << label << " server.\n"; increaseChannels(channelId); switch (type) { case channel_cups: { if (handleControl(code_new_cups_connection, channelId) < 0) { return -1; } break; } case channel_smb: { if (handleControl(code_new_smb_connection, channelId) < 0) { return -1; } break; } case channel_media: { if (handleControl(code_new_media_connection, channelId) < 0) { return -1; } break; } case channel_http: { if (handleControl(code_new_http_connection, channelId) < 0) { return -1; } break; } case channel_font: { if (handleControl(code_new_font_connection, channelId) < 0) { return -1; } break; } default: { if (handleControl(code_new_slave_connection, channelId) < 0) { return -1; } break; } } channels_[channelId] -> handleConfiguration(); return 1; } int Proxy::handleNewSlaveConnection(int clientFd) { if (control -> isProtoStep7() == 1) { return handleNewGenericConnection(clientFd, channel_slave, "slave"); } else { #ifdef TEST *logofs << "Proxy: WARNING! Not sending unsupported " << "'code_new_slave_connection' message.\n" << logofs_flush; #endif return -1; } } int Proxy::handleNewGenericConnectionFromProxy(int channelId, T_channel_type type, const char *hostname, int port, const char *label) { if (port <= 0) { // // This happens when user has disabled // forwarding of the specific service. // #ifdef WARNING *logofs << "Proxy: WARNING! Refusing attempted connection " << "to " << label << " server.\n" << logofs_flush; #endif cerr << "Warning" << ": Refusing attempted connection " << "to " << label << " server.\n"; return -1; } const char *serverHost = hostname; int serverAddrFamily = AF_INET; sockaddr *serverAddr = NULL; unsigned int serverAddrLength = 0; #ifdef TEST *logofs << "Proxy: Connecting to " << label << " server '" << serverHost << "' on TCP port '" << port << "'.\n" << logofs_flush; #endif int serverIPAddr = GetHostAddress(serverHost); if (serverIPAddr == 0) { #ifdef PANIC *logofs << "Proxy: PANIC! Unknown " << label << " server host '" << serverHost << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Unknown " << label << " server host '" << serverHost << "'.\n"; return -1; } sockaddr_in *serverAddrTCP = new sockaddr_in; serverAddrTCP -> sin_family = AF_INET; serverAddrTCP -> sin_port = htons(port); serverAddrTCP -> sin_addr.s_addr = serverIPAddr; serverAddr = (sockaddr *) serverAddrTCP; serverAddrLength = sizeof(sockaddr_in); // // Connect to the requested server. // int serverFd = socket(serverAddrFamily, SOCK_STREAM, PF_UNSPEC); if (serverFd < 0) { #ifdef PANIC *logofs << "Proxy: PANIC! Call to socket failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to socket failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; delete serverAddrTCP; return -1; } else if (connect(serverFd, serverAddr, serverAddrLength) < 0) { #ifdef WARNING *logofs << "Proxy: WARNING! Connection to " << label << " server '" << serverHost << ":" << port << "' failed with error '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Connection to " << label << " server '" << serverHost << ":" << port << "' failed with error '" << ESTR() << "'.\n"; close(serverFd); delete serverAddrTCP; return -1; } delete serverAddrTCP; if (handlePostConnectionFromProxy(channelId, serverFd, type, label) < 0) { return -1; } #ifdef TEST *logofs << "Proxy: Forwarded new connection to " << label << " server on port '" << port << "'.\n" << logofs_flush; #endif cerr << "Info" << ": Forwarded new connection to " << label << " server on port '" << port << "'.\n"; return 1; } int Proxy::handleNewGenericConnectionFromProxy(int channelId, T_channel_type type, const char *hostname, const char *path, const char *label) { if (path == NULL || *path == '\0' ) { // // This happens when user has disabled // forwarding of the specific service. // #ifdef WARNING *logofs << "Proxy: WARNING! Refusing attempted connection " << "to " << label << " server.\n" << logofs_flush; #endif cerr << "Warning" << ": Refusing attempted connection " << "to " << label << " server.\n"; return -1; } sockaddr_un serverAddrUnix; unsigned int serverAddrLength = sizeof(sockaddr_un); int serverAddrFamily = AF_UNIX; serverAddrUnix.sun_family = AF_UNIX; const int serverAddrNameLength = 108; strncpy(serverAddrUnix.sun_path, path, serverAddrNameLength); *(serverAddrUnix.sun_path + serverAddrNameLength - 1) = '\0'; #ifdef TEST *logofs << "Proxy: Connecting to " << label << " server " << "on Unix port '" << path << "'.\n" << logofs_flush; #endif // // Connect to the requested server. // int serverFd = socket(serverAddrFamily, SOCK_STREAM, PF_UNSPEC); if (serverFd < 0) { #ifdef PANIC *logofs << "Proxy: PANIC! Call to socket failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to socket failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } else if (connect(serverFd, (sockaddr *) &serverAddrUnix, serverAddrLength) < 0) { #ifdef WARNING *logofs << "Proxy: WARNING! Connection to " << label << " server on Unix port '" << path << "' failed " << "with error " << EGET() << ", '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Connection to " << label << " server on Unix port '" << path << "' failed " << "with error " << EGET() << ", '" << ESTR() << "'.\n"; close(serverFd); return -1; } if (handlePostConnectionFromProxy(channelId, serverFd, type, label) < 0) { return -1; } #ifdef TEST *logofs << "Proxy: Forwarded new connection to " << label << " server on Unix port '" << path << "'.\n" << logofs_flush; #endif cerr << "Info" << ": Forwarded new connection to " << label << " server on Unix port '" << path << "'.\n"; return 1; } int Proxy::handleNewSlaveConnectionFromProxy(int channelId) { // // Implementation is incomplete. Opening a // slave channel should let the proxy fork // a new client and pass to it the channel // descriptors. For now we make the channel // fail immediately. // // #include // #include // #include // // char *slaveServer = "/dev/null"; // // #ifdef TEST // *logofs << "Proxy: Opening file '" << slaveServer // << "'.\n" << logofs_flush; // #endif // // int serverFd = open(slaveServer, O_RDWR); // // if (handlePostConnectionFromProxy(channelId, serverFd, channel_slave, "slave") < 0) // { // return -1; // } // #ifdef WARNING *logofs << "Proxy: Refusing new slave connection for " << "channel ID#" << channelId << "\n" << logofs_flush; #endif cerr << "Warning" << ": Refusing new slave connection for " << "channel ID#" << channelId << "\n"; return -1; } int Proxy::handlePostConnectionFromProxy(int channelId, int serverFd, T_channel_type type, const char *label) { // // Turn queuing off for path proxy-to-server. // SetNoDelay(serverFd, 1); assignChannelMap(channelId, serverFd); #ifdef TEST *logofs << "Proxy: Descriptor FD#" << serverFd << " mapped to channel ID#" << channelId << ".\n" << logofs_flush; #endif if (allocateTransport(serverFd, channelId) < 0) { return -1; } switch (type) { case channel_cups: { channels_[channelId] = new CupsChannel(transports_[channelId], compressor_); break; } case channel_smb: { channels_[channelId] = new SmbChannel(transports_[channelId], compressor_); break; } case channel_media: { channels_[channelId] = new MediaChannel(transports_[channelId], compressor_); break; } case channel_http: { channels_[channelId] = new HttpChannel(transports_[channelId], compressor_); break; } case channel_font: { channels_[channelId] = new FontChannel(transports_[channelId], compressor_); break; } default: { channels_[channelId] = new SlaveChannel(transports_[channelId], compressor_); break; } } if (channels_[channelId] == NULL) { deallocateTransport(channelId); return -1; } increaseChannels(channelId); channels_[channelId] -> handleConfiguration(); return 1; } nxcomp/StaticCompressor.cpp0000644000076400007640000002756611323113027016353 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Z.h" #include "Misc.h" #include "Control.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "StaticCompressor.h" #define PANIC #define WARNING #undef TEST #undef DEBUG StaticCompressor::StaticCompressor(int compressionLevel, int compressionThreshold) { buffer_ = NULL; bufferSize_ = 0; compressionStream_.zalloc = (alloc_func) 0; compressionStream_.zfree = (free_func) 0; compressionStream_.opaque = (voidpf) 0; decompressionStream_.zalloc = (alloc_func) 0; decompressionStream_.zfree = (free_func) 0; decompressionStream_.opaque = (void *) 0; decompressionStream_.next_in = (Bytef *) 0; decompressionStream_.avail_in = 0; #ifdef TEST *logofs << "StaticCompressor: Compression level is " << compressionLevel << ".\n" << logofs_flush; #endif int result = deflateInit2(&compressionStream_, compressionLevel, Z_DEFLATED, 15, 9, Z_DEFAULT_STRATEGY); if (result != Z_OK) { #ifdef PANIC *logofs << "StaticCompressor: PANIC! Cannot initialize the " << "compression stream. Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot initialize the compression " << "stream. Error is '" << zError(result) << "'.\n"; HandleAbort(); } result = inflateInit2(&decompressionStream_, 15); if (result != Z_OK) { #ifdef PANIC *logofs << "StaticCompressor: PANIC! Cannot initialize the " << "decompression stream. Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot initialize the decompression " << "stream. Error is '" << zError(result) << "'.\n"; HandleAbort(); } #ifdef TEST *logofs << "StaticCompressor: Compression threshold is " << compressionThreshold << ".\n" << logofs_flush; #endif threshold_ = compressionThreshold; } StaticCompressor::~StaticCompressor() { int result = deflateEnd(&compressionStream_); if (result != Z_OK) { #ifdef PANIC *logofs << "StaticCompressor: PANIC! Cannot deinitialize the " << "compression stream. Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot deinitialize the compression " << "stream. Error is '" << zError(result) << "'.\n"; } result = inflateEnd(&decompressionStream_); if (result != Z_OK) { #ifdef PANIC *logofs << "StaticCompressor: PANIC! Cannot deinitialize the " << "decompression stream. Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot deinitialize the decompression " << "stream. Error is '" << zError(result) << "'.\n"; } delete [] buffer_; } // // This function compresses and encodes the compressed // buffer. It returns a pointer to the internal buffer // where data was compressed. // int StaticCompressor::compressBuffer(const unsigned char *plainBuffer, const unsigned int plainSize, unsigned char *&compressedBuffer, unsigned int &compressedSize, EncodeBuffer &encodeBuffer) { if (control -> LocalDataCompression == 0 || compressBuffer(plainBuffer, plainSize, compressedBuffer, compressedSize) <= 0) { encodeBuffer.encodeBoolValue(0); encodeBuffer.encodeMemory(plainBuffer, plainSize); return 0; } else { encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeValue(compressedSize, 32, 14); encodeBuffer.encodeValue(plainSize, 32, 14); encodeBuffer.encodeMemory(compressedBuffer, compressedSize); return 1; } } // // This function compresses data into a dynamically // allocated buffer and returns a pointer to it, so // application must copy data before the next call. // int StaticCompressor::compressBuffer(const unsigned char *plainBuffer, const unsigned int plainSize, unsigned char *&compressedBuffer, unsigned int &compressedSize) { #ifdef DEBUG *logofs << "StaticCompressor: Called for buffer at " << (void *) plainBuffer << ".\n" << logofs_flush; #endif compressedSize = plainSize; if (plainSize < (unsigned int) threshold_) { #ifdef TEST *logofs << "StaticCompressor: Leaving buffer unchanged. " << "Plain size is " << plainSize << " with threshold " << (unsigned int) threshold_ << ".\n" << logofs_flush; #endif return 0; } // // Determine the size of the temporary // buffer. // unsigned int newSize = plainSize + (plainSize / 1000) + 12; // // Allocate a new buffer if it grows // beyond 64K. // if (buffer_ == NULL || (bufferSize_ > 65536 && newSize < bufferSize_ / 2) || newSize > bufferSize_) { delete [] buffer_; buffer_ = new unsigned char[newSize]; if (buffer_ == NULL) { #ifdef PANIC *logofs << "StaticCompressor: PANIC! Can't allocate compression " << "buffer of " << newSize << " bytes. Error is " << EGET() << " ' " << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Can't allocate compression buffer of " << newSize << " bytes. Error is " << EGET() << " '" << ESTR() << "'.\n"; bufferSize_ = 0; return 0; } bufferSize_ = newSize; } unsigned int resultingSize = newSize; int result = ZCompress(&compressionStream_, buffer_, &resultingSize, plainBuffer, plainSize); if (result == Z_OK) { if (resultingSize > newSize) { #ifdef PANIC *logofs << "StaticCompressor: PANIC! Overflow in compression " << "buffer size. " << "Expected size was " << newSize << " while it is " << resultingSize << ".\n" << logofs_flush; #endif cerr << "Error" << ": Overflow in compress buffer size. " << "Expected size was " << newSize << " while it is " << resultingSize << ".\n"; return -1; } else if (resultingSize >= plainSize) { #ifdef TEST *logofs << "StaticCompressor: Leaving buffer unchanged. " << "Plain size is " << plainSize << " compressed " << "size is " << resultingSize << ".\n" << logofs_flush; #endif return 0; } compressedBuffer = buffer_; compressedSize = resultingSize; #ifdef TEST *logofs << "StaticCompressor: Compressed buffer from " << plainSize << " to " << resultingSize << " bytes.\n" << logofs_flush; #endif return 1; } #ifdef PANIC *logofs << "StaticCompressor: PANIC! Failed compression of buffer. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed compression of buffer. " << "Error is '" << zError(result) << "'.\n"; return -1; } int StaticCompressor::decompressBuffer(unsigned char *plainBuffer, unsigned int plainSize, const unsigned char *&compressedBuffer, unsigned int &compressedSize, DecodeBuffer &decodeBuffer) { #ifdef DEBUG *logofs << "StaticCompressor: Called for buffer at " << (void *) plainBuffer << ".\n" << logofs_flush; #endif unsigned int value; decodeBuffer.decodeBoolValue(value); if (value == 0) { memcpy(plainBuffer, decodeBuffer.decodeMemory(plainSize), plainSize); return 0; } unsigned int checkSize = plainSize; decodeBuffer.decodeValue(value, 32, 14); compressedSize = value; decodeBuffer.decodeValue(value, 32, 14); checkSize = value; // // If caller needs the original compressed // data it must copy this to its own buffer // before using any further decode function. // compressedBuffer = decodeBuffer.decodeMemory(compressedSize); int result = ZDecompress(&decompressionStream_, plainBuffer, &checkSize, compressedBuffer, compressedSize); if (result != Z_OK) { #ifdef PANIC *logofs << "StaticCompressor: PANIC! Failure decompressing buffer. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failure decompressing buffer. " << "Error is '" << zError(result) << "'.\n"; return -1; } else if (plainSize != checkSize) { #ifdef PANIC *logofs << "StaticCompressor: PANIC! Expected decompressed size was " << plainSize << " while it is " << checkSize << ".\n" << logofs_flush; #endif cerr << "Error" << ": Expected decompressed size was " << plainSize << " while it is " << checkSize << ".\n"; return -1; } return 1; } // // This is used to uncompress on-the-fly // messages whose data has been stored // in compressed format. // int StaticCompressor::decompressBuffer(unsigned char *plainBuffer, const unsigned int plainSize, const unsigned char *compressedBuffer, const unsigned int compressedSize) { #ifdef TEST *logofs << "StaticCompressor: Called for buffer at " << (void *) plainBuffer << ".\n" << logofs_flush; #endif unsigned int checkSize = plainSize; int result = ZDecompress(&decompressionStream_, plainBuffer, &checkSize, compressedBuffer, compressedSize); if (result != Z_OK) { #ifdef PANIC *logofs << "StaticCompressor: PANIC! Failure decompressing buffer. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif return -1; } if (plainSize != checkSize) { #ifdef PANIC *logofs << "StaticCompressor: PANIC! Expected decompressed size was " << plainSize << " while it is " << checkSize << ".\n" << logofs_flush; #endif cerr << "Error" << ": Expected decompressed size was " << plainSize << " while it is " << checkSize << ".\n"; return -1; } #ifdef TEST *logofs << "StaticCompressor: Decompressed buffer from " << compressedSize << " to " << plainSize << " bytes.\n" << logofs_flush; #endif return 1; } nxcomp/WriteBuffer.cpp0000644000076400007640000002741011323113030015251 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include "Misc.h" #include "Control.h" #include "WriteBuffer.h" #define PANIC #define WARNING #undef TEST #undef DEBUG WriteBuffer::WriteBuffer() { size_ = WRITE_BUFFER_DEFAULT_SIZE; buffer_ = new unsigned char[size_]; length_ = 0; index_ = NULL; scratchLength_ = 0; scratchBuffer_ = NULL; scratchOwner_ = 1; initialSize_ = WRITE_BUFFER_DEFAULT_SIZE; thresholdSize_ = WRITE_BUFFER_DEFAULT_SIZE << 1; maximumSize_ = WRITE_BUFFER_DEFAULT_SIZE << 4; #ifdef VALGRIND memset(buffer_, '\0', size_); #endif } WriteBuffer::~WriteBuffer() { if (scratchOwner_ == 1 && scratchBuffer_ != NULL) { delete [] scratchBuffer_; } delete [] buffer_; } void WriteBuffer::setSize(unsigned int initialSize, unsigned int thresholdSize, unsigned int maximumSize) { initialSize_ = initialSize; thresholdSize_ = thresholdSize; maximumSize_ = maximumSize; #ifdef TEST *logofs << "WriteBuffer: Set buffer sizes to " << initialSize_ << "/" << thresholdSize_ << "/" << maximumSize_ << ".\n" << logofs_flush; #endif } void WriteBuffer::partialReset() { if (scratchBuffer_ != NULL) { if (scratchOwner_) { #ifdef DEBUG *logofs << "WriteBuffer: Going to delete " << scratchLength_ << " bytes from the " << "scratch buffer.\n" << logofs_flush; #endif delete [] scratchBuffer_; } scratchLength_ = 0; scratchBuffer_ = NULL; scratchOwner_ = 1; } length_ = 0; index_ = NULL; #ifdef DEBUG *logofs << "WriteBuffer: Performed partial reset with " << size_ << " bytes in buffer.\n" << logofs_flush; #endif } void WriteBuffer::fullReset() { if (scratchBuffer_ != NULL) { if (scratchOwner_ == 1) { #ifdef DEBUG *logofs << "WriteBuffer: Going to delete " << scratchLength_ << " bytes from the " << "scratch buffer.\n" << logofs_flush; #endif delete [] scratchBuffer_; } scratchLength_ = 0; scratchBuffer_ = NULL; scratchOwner_ = 1; } length_ = 0; index_ = NULL; if (size_ > initialSize_) { #ifdef TEST *logofs << "WriteBuffer: Reallocating a new buffer of " << initialSize_ << " bytes.\n" << logofs_flush; #endif delete [] buffer_; size_ = initialSize_; buffer_ = new unsigned char[size_]; if (buffer_ == NULL) { #ifdef PANIC *logofs << "WriteBuffer: PANIC! Can't allocate memory for " << "X messages in context [A].\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory for " << "X messages in context [A].\n"; HandleAbort(); } #ifdef VALGRIND memset(buffer_, '\0', size_); #endif } #ifdef DEBUG *logofs << "WriteBuffer: Performed full reset with " << size_ << " bytes in buffer.\n" << logofs_flush; #endif } unsigned char *WriteBuffer::addMessage(unsigned int numBytes) { #ifdef DEBUG *logofs << "WriteBuffer: Adding " << numBytes << " bytes to " << length_ << " bytes already in buffer.\n" << logofs_flush; #endif if (numBytes > WRITE_BUFFER_OVERFLOW_SIZE) { #ifdef PANIC *logofs << "WriteBuffer: PANIC! Can't add a message of " << numBytes << " bytes.\n" << logofs_flush; *logofs << "WriteBuffer: PANIC! Assuming error handling " << "data in context [B].\n" << logofs_flush; #endif cerr << "Error" << ": Can't add a message of " << numBytes << " bytes to write buffer.\n"; cerr << "Error" << ": Assuming error handling " << "data in context [B].\n"; HandleAbort(); } else if (length_ + numBytes > size_) { unsigned int newSize = thresholdSize_; while (newSize < length_ + numBytes) { newSize <<= 1; if (newSize > maximumSize_) { newSize = length_ + numBytes + initialSize_; } } #ifdef TEST *logofs << "WriteBuffer: Growing buffer from " << size_ << " to " << newSize << " bytes.\n" << logofs_flush; #endif unsigned int indexOffset = 0; if (index_ && *index_) { indexOffset = *index_ - buffer_; } size_ = newSize; unsigned char *newBuffer = new unsigned char[size_]; if (newBuffer == NULL) { #ifdef PANIC *logofs << "WriteBuffer: PANIC! Can't allocate memory for " << "X messages in context [C].\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory for " << "X messages in context [C].\n"; HandleAbort(); } #ifdef TEST if (newSize >= maximumSize_) { *logofs << "WriteBuffer: WARNING! Buffer grown to reach " << "size of " << newSize << " bytes.\n" << logofs_flush; } #endif #ifdef VALGRIND memset(newBuffer, '\0', size_); #endif memcpy(newBuffer, buffer_, length_); #ifdef DEBUG *logofs << "WriteBuffer: Going to delete the " << "old buffer with new size " << size_ << ".\n" << logofs_flush; #endif delete [] buffer_; buffer_ = newBuffer; if (index_ && *index_) { *index_ = buffer_ + indexOffset; } } unsigned char *result = buffer_ + length_; length_ += numBytes; #ifdef DEBUG *logofs << "WriteBuffer: Bytes in buffer are " << length_ << " while size is " << size_ << ".\n" << logofs_flush; #endif return result; } unsigned char *WriteBuffer::removeMessage(unsigned int numBytes) { #ifdef TEST *logofs << "WriteBuffer: Removing " << numBytes << " bytes from buffer.\n" << logofs_flush; #endif if (numBytes > length_) { #ifdef PANIC *logofs << "WriteBuffer: PANIC! Can't remove " << numBytes << " bytes with only " << length_ << " bytes in buffer.\n" << logofs_flush; #endif cerr << "Error" << ": Buffer underflow handling " << "write buffer in context [D].\n"; HandleAbort(); } length_ -= numBytes; #ifdef TEST *logofs << "WriteBuffer: Bytes in buffer are now " << length_ << " while size is " << size_ << ".\n" << logofs_flush; #endif return (buffer_ + length_); } unsigned char *WriteBuffer::addScratchMessage(unsigned int numBytes) { if (numBytes > WRITE_BUFFER_OVERFLOW_SIZE) { #ifdef PANIC *logofs << "WriteBuffer: PANIC! Can't add a message of " << numBytes << " bytes.\n" << logofs_flush; *logofs << "WriteBuffer: PANIC! Assuming error handling " << "data in context [E].\n" << logofs_flush; #endif cerr << "Error" << ": Can't add a message of " << numBytes << " bytes to write buffer.\n"; cerr << "Error" << ": Assuming error handling " << "data in context [E].\n"; HandleAbort(); } else if (scratchBuffer_ != NULL) { #ifdef PANIC *logofs << "WriteBuffer: PANIC! Can't add a message of " << numBytes << " bytes with " << scratchLength_ << " bytes already in scratch buffer.\n" << logofs_flush; *logofs << "WriteBuffer: PANIC! Assuming error handling " << "data in context [F].\n" << logofs_flush; #endif cerr << "Error" << ": Can't add a message of " << numBytes << " bytes with " << scratchLength_ << " bytes already in scratch buffer.\n"; cerr << "Error" << ": Assuming error handling " << "data in context [F].\n"; HandleAbort(); } #ifdef DEBUG *logofs << "WriteBuffer: Adding " << numBytes << " bytes " << "to scratch buffer.\n" << logofs_flush; #endif unsigned char *newBuffer = new unsigned char[numBytes]; if (newBuffer == NULL) { #ifdef PANIC *logofs << "WriteBuffer: PANIC! Can't allocate memory for " << "X messages in context [G].\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory for " << "X messages in context [G].\n"; HandleAbort(); } #ifdef VALGRIND memset(newBuffer, '\0', numBytes); #endif scratchBuffer_ = newBuffer; scratchOwner_ = 1; scratchLength_ = numBytes; return newBuffer; } unsigned char *WriteBuffer::addScratchMessage(unsigned char *newBuffer, unsigned int numBytes) { if (numBytes > WRITE_BUFFER_OVERFLOW_SIZE) { #ifdef PANIC *logofs << "WriteBuffer: PANIC! Can't add a message of " << numBytes << " bytes.\n" << logofs_flush; *logofs << "WriteBuffer: PANIC! Assuming error handling " << "data in context [H].\n" << logofs_flush; #endif cerr << "Error" << ": Can't add a message of " << numBytes << " bytes to write buffer.\n"; cerr << "Error" << ": Assuming error handling " << "data in context [H].\n"; HandleAbort(); } else if (scratchBuffer_ != NULL) { #ifdef PANIC *logofs << "WriteBuffer: PANIC! Can't add a foreign " << "message of " << numBytes << " bytes with " << scratchLength_ << " bytes already in " << "scratch buffer.\n" << logofs_flush; *logofs << "WriteBuffer: PANIC! Assuming error handling " << "data in context [I].\n" << logofs_flush; #endif cerr << "Error" << ": Can't add a foreign message of " << numBytes << " bytes with " << scratchLength_ << " bytes already in scratch buffer.\n"; cerr << "Error" << ": Assuming error handling " << "data in context [I].\n"; HandleAbort(); } #ifdef DEBUG *logofs << "WriteBuffer: Adding " << numBytes << " bytes " << "from a foreign message to scratch buffer.\n" << logofs_flush; #endif scratchBuffer_ = newBuffer; scratchLength_ = numBytes; scratchOwner_ = 0; return newBuffer; } void WriteBuffer::removeScratchMessage() { #ifdef TEST if (scratchLength_ == 0 || scratchBuffer_ == NULL) { #ifdef PANIC *logofs << "WriteBuffer: PANIC! Can't remove non existent scratch message.\n" << logofs_flush; #endif cerr << "Error" << ": Can't remove non existent scratch message.\n"; HandleAbort(); } *logofs << "WriteBuffer: Removing " << scratchLength_ << " bytes from scratch buffer.\n" << logofs_flush; #endif if (scratchOwner_ == 1) { #ifdef DEBUG *logofs << "WriteBuffer: Going to delete " << scratchLength_ << " bytes from the " << "scratch buffer.\n" << logofs_flush; #endif delete [] scratchBuffer_; } scratchLength_ = 0; scratchBuffer_ = NULL; scratchOwner_ = 1; } nxcomp/Unpack.cpp0000644000076400007640000010454611342773403014275 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Misc.h" #include "Unpack.h" #define PANIC #define WARNING #undef TEST #undef DEBUG // // Used for the ZLIB decompression // of RGB, alpha and colormap data. // z_stream unpackStream; static int unpackInitialized; int Unpack8To8(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack8To8(T_colormap *colormap, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack8To16(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack8To16(T_colormap *colormap, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack8To24(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack8To24(T_colormap *colormap, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack8To32(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack8To32(T_colormap *colormap, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack15To16(const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack15To24(const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack15To32(const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack16To16(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack16To16(const unsigned char *data, unsigned char *out, unsigned char *end, int imageByteOrder); int Unpack16To24(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack16To24(const unsigned char *data, unsigned char *out, unsigned char *end, int imageByteOrder); int Unpack16To32(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack16To32(const unsigned char *data, unsigned char *out, unsigned char *end, int imageByteOrder); int Unpack24To24(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack24To24(const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack24To32(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack24To32(const unsigned char *data, unsigned char *out, unsigned char *end); int Unpack32To32(const T_colormask *colormask, const unsigned int *data, unsigned int *out, unsigned int *end); void UnpackInit() { if (unpackInitialized == 0) { unpackStream.zalloc = (alloc_func) 0; unpackStream.zfree = (free_func) 0; unpackStream.opaque = (voidpf) 0; unpackStream.next_in = (Bytef *) 0; unpackStream.avail_in = 0; int result = inflateInit2(&unpackStream, 15); if (result != Z_OK) { #ifdef PANIC *logofs << "UnpackInit: PANIC! Cannot initialize the Z stream " << "for decompression. Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot initialize the Z stream for " << "decompression. Error is '" << zError(result) << "'.\n"; } else { unpackInitialized = 1; } } } void UnpackDestroy() { if (unpackInitialized == 1) { inflateEnd(&unpackStream); unpackInitialized = 0; } } // // Get bits per pixel set by client // according to display geometry. // int UnpackBitsPerPixel(T_geometry *geometry, unsigned int depth) { switch (depth) { case 1: { return geometry -> depth1_bpp; } case 4: { return geometry -> depth4_bpp; } case 8: { return geometry -> depth8_bpp; } case 15: case 16: { return geometry -> depth16_bpp; } case 24: { return geometry -> depth24_bpp; } case 32: { return geometry -> depth32_bpp; } default: { return 0; } } } int Unpack8To8(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack8To8: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif memcpy(out, data, end - out); return 1; } int Unpack8To16(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack8To16: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif unsigned short *out16 = (unsigned short *) out; unsigned short *end16 = (unsigned short *) end; while (out16 < end16) { if (*data == 0) { *out16 = 0x0; } else if (*data == 0xff) { *out16 = 0xffff; } else { // // Pixel layout: // // 8bits 00RRGGBB -> 16bits RR000GG0 000BB000. // *out16 = (((((*data & 0x30) << 2) | colormask -> correction_mask) << 8) & 0xf800) | (((((*data & 0xc) << 4) | colormask -> correction_mask) << 3) & 0x7e0) | (((((*data & 0x3) << 6) | colormask -> correction_mask) >> 3) & 0x1f); } out16++; data++; } return 1; } int Unpack8To24(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack8To24: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif while (out < (end - 2)) { if (*data == 0x00) { out[0] = out[1] = out[2] = 0x00; } else if (*data == 0xff) { out[0] = out[1] = out[2] = 0xff; } else { // // Pixel layout: // // 8bits 00RRGGBB -> 24bits RR000000 GG00000 BB000000. // out[0] = (((*data & 0x30) << 2) | colormask -> correction_mask); out[1] = (((*data & 0x0c) << 4) | colormask -> correction_mask); out[2] = (((*data & 0x03) << 6) | colormask -> correction_mask); } out += 3; data += 1; } return 1; } int Unpack8To32(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack8To32: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif unsigned int *out32 = (unsigned int *) out; unsigned int *end32 = (unsigned int *) end; while (out32 < end32) { if (*data == 0) { *out32 = 0x0; } else if (*data == 0xff) { *out32 = 0xffffff; } else { *out32 = ((((*data & 0x30) << 2) | colormask -> correction_mask) << 16) | ((((*data & 0xc) << 4) | colormask -> correction_mask) << 8) | (((*data & 0x3) << 6) | colormask -> correction_mask); } out32++; data++; } return 1; } int Unpack8(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size) { int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); int (*unpack)(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); switch (dst_bpp) { case 8: { unpack = Unpack8To8; break; } case 16: { unpack = Unpack8To16; break; } case 24: { unpack = Unpack8To24; break; } case 32: { unpack = Unpack8To32; break; } default: { #ifdef PANIC *logofs << "Unpack8: PANIC! Bad destination bits per pixel " << dst_bpp << ". Only 16/24/32 are supported.\n" << logofs_flush; #endif return -1; } } if (dst_bpp == 24) { unsigned char *dst_end = dst_data; #ifdef TEST *logofs << "Unpack8: Handling 24 bits with dst_size " << dst_size << ".\n" << logofs_flush; #endif for (int y = 0; y < dst_height; y++) { dst_data = dst_end; dst_end += RoundUp4(dst_width * 3); (*unpack)(colormask, src_data, dst_data, dst_end); src_data += src_width; } } else { unsigned char *dst_end = dst_data + dst_size; (*unpack)(colormask, src_data, dst_data, dst_end); } return 1; } int Unpack16To16(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack16To16: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif if (colormask -> correction_mask) { unsigned short *data16 = (unsigned short *) data; unsigned short *out16 = (unsigned short *) out; unsigned short *end16 = (unsigned short *) end; while (out16 < end16) { if (*data16 == 0x0000) { *out16 = 0x0000; } else if (*data16 == 0xffff) { *out16 = 0xffff; } else { // // Pixel layout: // // 16bit RRRRRGGG GG0BBBBB -> RRRRRGGG GGGBBBBB. // *out16 = (((((*data16 & 0xf100) >> 8) | colormask -> correction_mask) << 8) & 0xf800) | (((((*data16 & 0x7c0) >> 3) | colormask -> correction_mask) << 3) & 0x7e0) | (((((*data16 & 0x1f) << 3) | colormask -> correction_mask) >> 3) & 0x1f); } out16++; data16++; } } else { #ifdef TEST *logofs << "Unpack16To16: Using bitwise copy due to null correction mask.\n" << logofs_flush; #endif memcpy((unsigned char *) out, (unsigned char *) data, end - out); } return 1; } int Unpack16To24(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack16To24: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif unsigned short *data16 = (unsigned short *) data; while (out < end - 2) { if (*data16 == 0x0) { out[0] = 0x00; out[1] = 0x00; out[2] = 0x00; } else if (*data16 == 0xffff) { out[0] = 0xff; out[1] = 0xff; out[2] = 0xff; } else { #ifdef TEST *logofs << "Unpack16To24: Pixel [" << *data16 << "]\n" << logofs_flush; #endif // // Pixel layout: // // 16bit 0RRRRRGG GGGBBBBB -> 24 bit RRRRR000 GGGGG000 BBBBB000 // out[0] = (((*data16 & 0x7c00) >> 7) | colormask -> correction_mask); out[1] = (((*data16 & 0x03e0) >> 2) | colormask -> correction_mask); out[2] = (((*data16 & 0x001f) << 3) | colormask -> correction_mask); } out += 3; data16 += 1; } return 1; } int Unpack16To32(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack16To32: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif unsigned short *data16 = (unsigned short *) data; unsigned int *out32 = (unsigned int *) out; unsigned int *end32 = (unsigned int *) end; while (out32 < end32) { if (*data16 == 0x0) { *out32 = 0x0; } else if (*data16 == 0xffff) { *out32 = 0xffffff; } else { *out32 = ((((*data16 & 0x7c00) >> 7) | colormask -> correction_mask) << 16) | ((((*data16 & 0x3e0) >> 2) | colormask -> correction_mask) << 8) | (((*data16 & 0x1f) << 3) | colormask -> correction_mask); } out32++; data16++; } return 1; } int Unpack16(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size) { int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); int (*unpack)(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); switch (dst_bpp) { case 16: { unpack = Unpack16To16; break; } case 24: { unpack = Unpack16To24; break; } case 32: { unpack = Unpack16To32; break; } default: { #ifdef PANIC *logofs << "Unpack16: PANIC! Bad destination bits per pixel " << dst_bpp << ". Only 24/32 are supported.\n" << logofs_flush; #endif return -1; } } if (dst_bpp == 24) { unsigned char *dst_end = dst_data; for (int y = 0; y < dst_height; y++) { dst_data = dst_end; dst_end += RoundUp4(dst_width * 3); (*unpack)(colormask, src_data, dst_data, dst_end); src_data += (src_width * 2); } } else { unsigned char *dst_end = dst_data + dst_size; (*unpack)(colormask, src_data, dst_data, dst_end); } return 1; } int Unpack24To24(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack24To24: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif if (colormask -> correction_mask) { while (out < end) { if (data[0] == 0x00 && data[1] == 0x00 && data[2] == 0x00) { out[0] = out[1] = out[2] = 0x00; } else if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) { out[0] = out[1] = out[2] = 0xff; } else { out[0] = (data[0] | colormask -> correction_mask); out[1] = (data[1] | colormask -> correction_mask); out[2] = (data[2] | colormask -> correction_mask); } out += 3; data += 3; } } else { #ifdef TEST *logofs << "Unpack24To24: Using bitwise copy due to null correction mask.\n" << logofs_flush; #endif memcpy((unsigned char *) out, (unsigned char *) data, end - out); } return 1; } int Unpack24To32(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack24To32: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif unsigned int *out32 = (unsigned int *) out; unsigned int *end32 = (unsigned int *) end; while (out32 < end32) { if (colormask -> color_mask == 0xff) { *out32 = (data[0] << 16) | (data[1] << 8) | data[2]; } else { if (data[0] == 0x0 && data[1] == 0x0 && data[2] == 0x0) { *out32 = 0x0; } else if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) { *out32 = 0xffffff; } else { *out32 = (((unsigned int) data[0] | colormask -> correction_mask) << 16) | (((unsigned int) data[1] | colormask -> correction_mask) << 8) | ((unsigned int) data[2] | colormask -> correction_mask); } } out32 += 1; data += 3; } return 1; } int Unpack24(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size) { int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); int (*unpack)(const T_colormask *colormask, const unsigned char *data, unsigned char *out, unsigned char *end); switch (dst_bpp) { case 24: { unpack = Unpack24To24; break; } case 32: { unpack = Unpack24To32; break; } default: { #ifdef PANIC *logofs << "Unpack24: PANIC! Bad destination bits per pixel " << dst_bpp << ". Only 32 is supported.\n" << logofs_flush; #endif return -1; } } if (dst_bpp == 24) { unsigned char *dst_end; unsigned long scanline_size = RoundUp4(dst_width * dst_bpp / 8); dst_end = dst_data; #ifdef TEST *logofs << "Unpack24: Handling 24 bits with dst_height " << dst_height << " scanline_size " << scanline_size << " dst_size " << dst_size << ".\n" << logofs_flush; #endif for (int y = 0; y < dst_height; y++) { dst_data = dst_end; dst_end += scanline_size; (*unpack)(colormask, src_data, dst_data, dst_end); src_data += scanline_size; } } else { unsigned char *dst_end = dst_data + dst_size; (*unpack)(colormask, src_data, dst_data, dst_end); } return 1; } int Unpack8To8(T_colormap *colormap, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack8To8: Unpacking " << end - out << " bytes of colormapped data.\n" << logofs_flush; #endif while (out < end) { *(out++) = (unsigned char) colormap -> data[*(data++)]; } return 1; } int Unpack8To16(T_colormap *colormap, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack8To16: Unpacking " << end - out << " bytes of colormapped data.\n" << logofs_flush; #endif unsigned short *out16 = (unsigned short *) out; unsigned short *end16 = (unsigned short *) end; while (out16 < end16) { *(out16++) = (unsigned short) colormap -> data[*(data++)]; } return 1; } int Unpack8To24(T_colormap *colormap, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack8To24: Unpacking " << end - out << " bytes of colormapped data.\n" << logofs_flush; #endif unsigned int value; while (out < end) { value = colormap -> data[*(data++)]; *(out++) = value; *(out++) = value >> 8; *(out++) = value >> 16; } return 1; } int Unpack8To32(T_colormap *colormap, const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack8To32: Unpacking " << end - out << " bytes of colormapped data.\n" << logofs_flush; #endif unsigned int *out32 = (unsigned int *) out; unsigned int *end32 = (unsigned int *) end; while (out32 < end32) { *(out32++) = colormap -> data[*(data++)]; } return 1; } int Unpack8(T_geometry *geometry, T_colormap *colormap, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size) { if (src_depth != 8) { #ifdef PANIC *logofs << "Unpack8: PANIC! Cannot unpack colormapped image of source depth " << src_depth << ".\n" << logofs_flush; #endif return -1; } int (*unpack)(T_colormap *colormap, const unsigned char *data, unsigned char *out, unsigned char *end); int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); switch (dst_bpp) { case 8: { unpack = Unpack8To8; break; } case 16: { unpack = Unpack8To16; break; } case 24: { unpack = Unpack8To24; break; } case 32: { unpack = Unpack8To32; break; } default: { #ifdef PANIC *logofs << "Unpack8: PANIC! Bad destination bits per pixel " << dst_bpp << ". Only 8/16/24/32 are supported.\n" << logofs_flush; #endif return -1; } } if (src_width == dst_width && src_height == dst_height) { unsigned char *dst_end = dst_data + dst_size; (*unpack)(colormap, src_data, dst_data, dst_end); } else if (src_width >= dst_width && src_height >= dst_height) { unsigned char *dst_end = dst_data; for (int y = 0; y < dst_height; y++) { dst_data = dst_end; dst_end += RoundUp4(dst_width * dst_bpp / 8); (*unpack)(colormap, src_data, dst_data, dst_end); src_data += src_width; } } else { #ifdef PANIC *logofs << "Unpack8: PANIC! Cannot unpack image. " << "Destination area " << dst_width << "x" << dst_height << " is not fully contained in " << src_width << "x" << src_height << " source.\n" << logofs_flush; #endif return -1; } return 1; } int Unpack15To16(const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack15To16: Unpacking " << end - out << " bytes of colormapped data.\n" << logofs_flush; #endif unsigned short *data16 = (unsigned short *) data; unsigned short *out16 = (unsigned short *) out; unsigned short *end16 = (unsigned short *) end; while (out16 < end16) { if (*data16 == 0x0) { *out16 = 0x0; } else if (*data16 == 0x7fff) { *out16 = 0xffff; } else { #ifdef TEST *logofs << "Unpack15To16: Pixel [" << *data16 << "]\n" << logofs_flush; #endif *out16 = ((*data16 & 0x7ff0) << 1) | (*data16 & 0x001f); } out16 += 1; data16 += 1; } return 1; } int Unpack15To24(const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack15To24: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif unsigned short *data16 = (unsigned short *) data; while (out < end - 2) { if (*data16 == 0x0) { out[0] = 0x00; out[1] = 0x00; out[2] = 0x00; } else if (*data16 == 0x7fff) { out[0] = 0xff; out[1] = 0xff; out[2] = 0xff; } else { #ifdef TEST *logofs << "Unpack15To24: Pixel [" << *data16 << "]\n" << logofs_flush; #endif out[0] = ((*data16 >> 7) & 0xf8) | ((*data16 >> 12) & 0x07); out[1] = ((*data16 >> 2) & 0xf8) | ((*data16 >> 8) & 0x07); out[2] = ((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07); } out += 3; data16 += 1; } return 1; } int Unpack15To32(const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack15To32: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif unsigned short *data16 = (unsigned short *) data; unsigned int *out32 = (unsigned int *) out; unsigned int *end32 = (unsigned int *) end; while (out32 < end32) { if (*data16 == 0x0) { *out32 = 0x0; } else if (*data16 == 0xffff) { *out32 = 0xffffff; } else { *out32 = ((((*data16 >> 7) & 0xf8) | ((*data16 >> 12) & 0x07)) << 16) | ((((*data16 >> 2) & 0xf8) | ((*data16 >> 8) & 0x07)) << 8) | (((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07)); } out32++; data16++; } return 1; } int Unpack15(T_geometry *geometry, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size) { if (src_depth != 16) { #ifdef PANIC *logofs << "Unpack15: PANIC! Cannot unpack colormapped image of source depth " << src_depth << ".\n" << logofs_flush; #endif return -1; } int (*unpack)(const unsigned char *data, unsigned char *out, unsigned char *end); int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); switch (dst_bpp) { case 16: { unpack = Unpack15To16; break; } case 24: { unpack = Unpack15To24; break; } case 32: { unpack = Unpack15To32; break; } default: { #ifdef PANIC *logofs << "Unpack15: PANIC! Bad destination bits per pixel " << dst_bpp << ". Only 16/24/32 are supported.\n" << logofs_flush; #endif return -1; } } if (src_width == dst_width && src_height == dst_height) { unsigned char *dst_end = dst_data + dst_size; (*unpack)(src_data, dst_data, dst_end); } else if (src_width >= dst_width && src_height >= dst_height) { unsigned char *dst_end = dst_data; for (int y = 0; y < dst_height; y++) { dst_data = dst_end; dst_end += RoundUp4(dst_width * dst_bpp / 8); (*unpack)(src_data, dst_data, dst_end); src_data += src_width * 2; } } else { #ifdef PANIC *logofs << "Unpack15: PANIC! Cannot unpack image. " << "Destination area " << dst_width << "x" << dst_height << " is not fully contained in " << src_width << "x" << src_height << " source.\n" << logofs_flush; #endif return -1; } return 1; } int Unpack16To16(const unsigned char *data, unsigned char *out, unsigned char *end, int imageByteOrder) { #ifdef TEST *logofs << "Unpack16To16: Unpacking " << end - out << " bytes of colormapped data.\n" << logofs_flush; #endif memcpy((unsigned char *) out, (unsigned char *) data, end - out); return 1; } int Unpack16To24(const unsigned char *data, unsigned char *out, unsigned char *end, int imageByteOrder) { #ifdef TEST *logofs << "Unpack16To24: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif unsigned short *data16 = (unsigned short *) data; while (out < end - 2) { if (*data16 == 0x0) { out[0] = 0x00; out[1] = 0x00; out[2] = 0x00; } else if (*data16 == 0xffff) { out[0] = 0xff; out[1] = 0xff; out[2] = 0xff; } else { #ifdef TEST *logofs << "Unpack16To24: Pixel [" << *data16 << "]\n" << logofs_flush; #endif out[0] = ((*data16 >> 8) & 0xf8) | ((*data16 >> 13) & 0x07); out[1] = ((*data16 >> 3) & 0xfc) | ((*data16 >> 9) & 0x03); out[2] = ((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07); } out += 3; data16 += 1; } return 1; } int Unpack16To32(const unsigned char *data, unsigned char *out, unsigned char *end, int imageByteOrder) { #ifdef TEST *logofs << "Unpack16To32: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif unsigned short *data16 = (unsigned short *) data; unsigned int *out32 = (unsigned int *) out; unsigned int *end32 = (unsigned int *) end; unsigned short pixel16; unsigned int pixel32; while (out32 < end32) { pixel16 = GetUINT((unsigned char *)data16, 0); if (pixel16 == 0x0) { PutULONG(0x0, (unsigned char *) out32, imageByteOrder); } else if (pixel16 == 0xffff) { PutULONG(0xffffff, (unsigned char *) out32, imageByteOrder); } else { pixel32 = ((((pixel16 >> 8) & 0xf8) | ((pixel16 >> 13) & 0x07)) << 16) | ((((pixel16 >> 3) & 0xfc) | ((pixel16 >> 9) & 0x03)) << 8) | (((pixel16 << 3) & 0xf8) | ((pixel16 >> 2) & 0x07)); PutULONG(pixel32, (unsigned char *) out32, imageByteOrder); } out32++; data16++; } return 1; } int Unpack16(T_geometry *geometry, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size) { int imageByteOrder = geometry -> image_byte_order; if (src_depth != 16) { #ifdef PANIC *logofs << "Unpack16: PANIC! Cannot unpack colormapped image of source depth " << src_depth << ".\n" << logofs_flush; #endif return -1; } int (*unpack)(const unsigned char *data, unsigned char *out, unsigned char *end, int imageByteOrder); int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); switch (dst_bpp) { case 16: { unpack = Unpack16To16; break; } case 24: { unpack = Unpack16To24; break; } case 32: { unpack = Unpack16To32; break; } default: { #ifdef PANIC *logofs << "Unpack16: PANIC! Bad destination bits per pixel " << dst_bpp << ". Only 16/24/32 are supported.\n" << logofs_flush; #endif return -1; } } if (src_width == dst_width && src_height == dst_height) { unsigned char *dst_end = dst_data + dst_size; (*unpack)(src_data, dst_data, dst_end, imageByteOrder); } else if (src_width >= dst_width && src_height >= dst_height) { unsigned char *dst_end = dst_data; for (int y = 0; y < dst_height; y++) { dst_data = dst_end; dst_end += RoundUp4(dst_width * dst_bpp / 8); (*unpack)(src_data, dst_data, dst_end, imageByteOrder); src_data += src_width * 2; } } else { #ifdef PANIC *logofs << "Unpack16: PANIC! Cannot unpack image. " << "Destination area " << dst_width << "x" << dst_height << " is not fully contained in " << src_width << "x" << src_height << " source.\n" << logofs_flush; #endif return -1; } return 1; } int Unpack24To24(const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack124To24: Unpacking " << end - out << " bytes of colormapped data.\n" << logofs_flush; #endif while (out < end) { *(out++) = *(data++); } return 1; } int Unpack24To32(const unsigned char *data, unsigned char *out, unsigned char *end) { #ifdef TEST *logofs << "Unpack24To32: Unpacking " << end - out << " bytes of colormapped data.\n" << logofs_flush; #endif unsigned int *out32 = (unsigned int *) out; unsigned int *end32 = (unsigned int *) end; while (out32 < end32) { if (data[0] == 0x0 && data[1] == 0x0 && data[2] == 0x0) { *out32 = 0x0; } else if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) { *out32 = 0xffffff; } else { *out32 = (data[2] << 16) | (data[1] << 8) | data[0]; } out32 += 1; data += 3; } return 1; } int Unpack24(T_geometry *geometry, int src_depth, int src_width, int src_height, unsigned char *src_data, int src_size, int dst_depth, int dst_width, int dst_height, unsigned char *dst_data, int dst_size) { if (src_depth != 24) { #ifdef PANIC *logofs << "Unpack24: PANIC! Cannot unpack colormapped image of source depth " << src_depth << ".\n" << logofs_flush; #endif return -1; } int (*unpack)(const unsigned char *data, unsigned char *out, unsigned char *end); int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); switch (dst_bpp) { case 24: { unpack = Unpack24To24; break; } case 32: { unpack = Unpack24To32; break; } default: { #ifdef PANIC *logofs << "Unpack24: PANIC! Bad destination bits per pixel " << dst_bpp << ". Only 24/32 are supported.\n" << logofs_flush; #endif return -1; } } if (src_width == dst_width && src_height == dst_height) { unsigned char *dst_end = dst_data + dst_size; (*unpack)(src_data, dst_data, dst_end); } else if (src_width >= dst_width && src_height >= dst_height) { unsigned char *dst_end = dst_data; for (int y = 0; y < dst_height; y++) { dst_data = dst_end; dst_end += RoundUp4(dst_width * dst_bpp / 8); (*unpack)(src_data, dst_data, dst_end); src_data += src_width * 3; } } else { #ifdef PANIC *logofs << "Unpack24: PANIC! Cannot unpack image. " << "Destination area " << dst_width << "x" << dst_height << " is not fully contained in " << src_width << "x" << src_height << " source.\n" << logofs_flush; #endif return -1; } return 1; } int Unpack32To32(const T_colormask *colormask, const unsigned int *data, unsigned int *out, unsigned int *end) { #ifdef TEST *logofs << "Unpack32To32: Unpacking " << end - out << " bytes of data.\n" << logofs_flush; #endif if (colormask -> correction_mask) { while (out < end) { if (*data == 0x00000000) { *out = 0x00000000; } else if (*data == 0xFFFFFFFF) { *out = 0xFFFFFFFF; } else { *out = *data | ((colormask -> correction_mask << 16) | (colormask -> correction_mask << 8) | colormask -> correction_mask); } out += 1; data += 1; } } else { #ifdef TEST *logofs << "Unpack32To32: Using bitwise copy due to null correction mask.\n" << logofs_flush; #endif memcpy((unsigned char *) out, (unsigned char *) data, end - out); } return 1; } nxcomp/SetUnpackColormapCompat.cpp0000644000076400007640000002033111323113030017556 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "SetUnpackColormapCompat.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // SetUnpackColormapCompatStore::SetUnpackColormapCompatStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = SETUNPACKCOLORMAP_ENABLE_CACHE; enableData = SETUNPACKCOLORMAP_ENABLE_DATA; enableSplit = SETUNPACKCOLORMAP_ENABLE_SPLIT; enableCompress = SETUNPACKCOLORMAP_ENABLE_COMPRESS; dataLimit = SETUNPACKCOLORMAP_DATA_LIMIT; dataOffset = SETUNPACKCOLORMAP_DATA_OFFSET; cacheSlots = SETUNPACKCOLORMAP_CACHE_SLOTS; cacheThreshold = SETUNPACKCOLORMAP_CACHE_THRESHOLD; cacheLowerThreshold = SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } SetUnpackColormapCompatStore::~SetUnpackColormapCompatStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int SetUnpackColormapCompatStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif // Client. encodeBuffer.encodeCachedValue(*(buffer + 1), 8, clientCache -> resourceCache); // Entries. encodeBuffer.encodeValue(GetULONG(buffer + 4, bigEndian), 32, 9); #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int SetUnpackColormapCompatStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif unsigned int value; unsigned char cValue; // Client. decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> resourceCache); // Entries. decodeBuffer.decodeValue(value, 32, 9); size = (value << 2) + 8; buffer = writeBuffer -> addMessage(size); *(buffer + 1) = cValue; PutULONG(value, buffer + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int SetUnpackColormapCompatStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message; setUnpackColormap -> client = *(buffer + 1); setUnpackColormap -> entries = GetULONG(buffer + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int SetUnpackColormapCompatStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message; *(buffer + 1) = setUnpackColormap -> client; PutULONG(setUnpackColormap -> entries, buffer + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void SetUnpackColormapCompatStore::dumpIdentity(const Message *message) const { #ifdef DUMP SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message; *logofs << name() << ": Identity client " << (unsigned int) setUnpackColormap -> client << " entries " << setUnpackColormap -> entries << " size " << setUnpackColormap -> size_ << ".\n"; #endif } void SetUnpackColormapCompatStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 4, 4); } void SetUnpackColormapCompatStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message; SetUnpackColormapCompatMessage *cachedSetUnpackColormap = (SetUnpackColormapCompatMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << (unsigned int) setUnpackColormap -> client << " as client field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(setUnpackColormap -> client, 8, clientCache -> resourceCache); cachedSetUnpackColormap -> client = setUnpackColormap -> client; if (cachedSetUnpackColormap -> entries != setUnpackColormap -> entries) { #ifdef TEST *logofs << name() << ": Encoding value " << setUnpackColormap -> entries << " as entries field.\n" << logofs_flush; #endif encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeValue(setUnpackColormap -> entries, 32, 9); cachedSetUnpackColormap -> entries = setUnpackColormap -> entries; } else { encodeBuffer.encodeBoolValue(0); } } void SetUnpackColormapCompatStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeCachedValue(setUnpackColormap -> client, 8, clientCache -> resourceCache); #ifdef DEBUG *logofs << name() << ": Decoded value " << (unsigned int) setUnpackColormap -> client << " as client field.\n" << logofs_flush; #endif decodeBuffer.decodeBoolValue(value); if (value) { decodeBuffer.decodeValue(value, 32, 9); setUnpackColormap -> entries = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << setUnpackColormap -> entries << " as entries field.\n" << logofs_flush; #endif } } nxcomp/configure.in0000644000076400007640000002466611342773403014665 0ustar svetonisvetonidnl Process this file with autoconf to produce a configure script. dnl Prolog AC_INIT(NX.h) AC_PREREQ(2.13) dnl Set our default compilation flags. CXXFLAGS="-O3 -fno-rtti -fno-exceptions" CFLAGS="-O3" dnl Reset default linking directives. LIBSTATIC="" LIBSHARED="" dnl Prefer headers and libraries from nx-X11, if present. if test -d "../nx-X11/include" ; then CXXFLAGS="$CXXFLAGS -I../nx-X11/exports/include" CFLAGS="$CFLAGS -I../nx-X11/exports/include" LIBS="$LIBS -L../nx-X11/exports/lib" fi dnl Check whether --with-ipaq was given. if test "${with_ipaq}" = yes; then echo -e "enabling IPAQ configuration" CXX="arm-linux-c++" CC="arm-linux-gcc" unset ac_cv_prog_armcxx unset ac_cv_prog_armcc unset ac_cv_prog_CXXCPP AC_CHECK_PROG([armcxx],["$CXX"],[yes],[no],[$PATH]) AC_CHECK_PROG([armcc],["$CC"],[yes],[no],[$PATH]) if test $armcxx = "yes" && test $armcc = "yes" ; then ac_cv_prog_CXX="$CXX" ac_cv_prog_CC="$CC" else AC_MSG_ERROR(Installation or configuration problem. Cannot find compiler for arm-linux.) fi else unset ac_cv_prog_CXX unset ac_cv_prog_CC unset ac_cv_prog_CXXCPP fi dnl Check for programs. AC_PROG_CXX AC_PROG_CC AC_LANG_CPLUSPLUS dnl Check whether option -Wno-deprecated dnl is needed by GCC compiler. AC_MSG_CHECKING([whether compiler needs -Wno-deprecated]) gcc_version=`${CC} --version | grep 'gcc (GCC) [[3-4]].' | head -n 1` case "${gcc_version}" in gcc*) AC_MSG_RESULT([yes]) CXXFLAGS="$CXXFLAGS -Wno-deprecated" ;; *) AC_MSG_RESULT([no]) ;; esac AC_MSG_CHECKING([whether compiler accepts -Wmissing-declarations]) gcc_version=`${CC} --version | grep 'gcc (GCC) [[3-4]].' | head -n 1` case "${gcc_version}" in gcc*) AC_MSG_RESULT([no]) ;; *) AC_MSG_RESULT([yes]) CXXFLAGS="$CXXFLAGS -Wmissing-declarations" ;; esac dnl Check for BSD compatible install. AC_PROG_INSTALL dnl Check for extra header files. AC_PATH_XTRA dnl Custom addition. ac_help="$ac_help --with-symbols add the -g flag to produce the debug symbols --with-use-malloc add the __USE_MALLOC flag to avoid the STL allocators --with-info define INFO at compile time to get basic log output --with-valgrind clean up allocated buffers to avoid valgrind warnings --with-version use this version for produced libraries --with-static-png enable static linking of PNG library --with-static-jpeg enable static linking of JPEG library --with-static-z enable static linking of Z library" dnl Check to see if we're running under Cygwin32. AC_DEFUN(nxconf_CYGWIN32, [AC_CACHE_CHECK(for Cygwin32 environment, nxconf_cv_cygwin32, [AC_TRY_COMPILE(,[return __CYGWIN32__;], nxconf_cv_cygwin32=yes, nxconf_cv_cygwin32=no) rm -f conftest*]) CYGWIN32= test "$nxconf_cv_cygwin32" = yes && CYGWIN32=yes]) nxconf_CYGWIN32 dnl Check whether we're building on a AMD64. AC_DEFUN(nxconf_AMD64, [AC_CACHE_CHECK(for Amd64 environment, nxconf_cv_amd64, [AC_TRY_COMPILE(,[return (__amd64__ || __x86_64__);], nxconf_cv_amd64=yes, nxconf_cv_amd64=no) rm -f conftest*]) AMD64= test "$nxconf_cv_amd64" = yes && AMD64=yes]) nxconf_AMD64 dnl Check for Darwin environment. AC_DEFUN(nxconf_DARWIN, [AC_CACHE_CHECK(for Darwin environment, nxconf_cv_darwin, [AC_TRY_COMPILE(,[return __APPLE__;], nxconf_cv_darwin=yes, nxconf_cv_darwin=no) rm -f conftest*]) DARWIN= test "$nxconf_cv_darwin" = yes && DARWIN=yes]) nxconf_DARWIN dnl Check to see if we're running under Solaris. AC_DEFUN(nxconf_SUN, [AC_CACHE_CHECK(for Solaris environment, nxconf_cv_sun, [AC_TRY_COMPILE(,[return __sun;], nxconf_cv_sun=yes, nxconf_cv_sun=no) rm -f conftest*]) SUN= test "$nxconf_cv_sun" = yes && SUN=yes]) nxconf_SUN dnl Check to see if we're running under FreeBSD. AC_DEFUN(nxconf_FreeBSD, [AC_CACHE_CHECK(for FreeBSD environment, nxconf_cv_freebsd, [AC_TRY_COMPILE(,[return __FreeBSD__;], nxconf_cv_freebsd=yes, nxconf_cv_freebsd=no) rm -f conftest*]) FreeBSD= test "$nxconf_cv_freebsd" = yes && FreeBSD=yes]) nxconf_FreeBSD dnl Build PIC libraries. if test "$CYGWIN32" != yes -a "$DARWIN" != yes; then CXXFLAGS="$CXXFLAGS -fPIC" CFLAGS="$CFLAGS -fPIC" fi dnl Solaris requires the socket and gcc_s libs explicitly linked. dnl Note also that headers from default /usr/openwin/include/X11 dnl cause a warning due to pragma in Xmd.h. if test "$SUN" = yes; then LIBS="$LIBS -L/usr/sfw/lib -lsocket " CXXFLAGS="$CXXFLAGS -I/usr/sfw/include" CFLAGS="$CFLAGS -I/usr/sfw/include" fi dnl On FreeBSD search libraries and includes under /usr/local. if test "$FreeBSD" = yes; then LIBS="$LIBS -L/usr/local/lib" CXXFLAGS="$CXXFLAGS -I/usr/local/include" CFLAGS="$CFLAGS -I/usr/local/include" fi dnl Under Darwin we don't have support for -soname option and dnl we need the -bundle flag. Under Solaris, instead, we need dnl the options -G -h. if test "$DARWIN" = yes; then LDFLAGS="$LDFLAGS -bundle" elif test "$SUN" = yes; then LDFLAGS="$LDFLAGS -G -h \$(LIBLOAD)" else LDFLAGS="$LDFLAGS -Wl,-soname,\$(LIBLOAD)" fi dnl Check to see if in_addr_t is defined. dnl Could use a specific configure test. AC_DEFUN(nxconf_INADDRT, [AC_CACHE_CHECK(for in_addr_t, nxconf_cv_inaddrt, [AC_TRY_COMPILE([#include ],[in_addr_t t; t = 1; return t;], nxconf_cv_inaddrt=yes, nxconf_cv_inaddrt=no) rm -f conftest*]) INADDRT= test "$nxconf_cv_inaddrt" = yes && INADDRT=yes]) nxconf_INADDRT dnl If in_addr_t is not defined use unsigned int. if test "$INADDRT" != yes ; then echo -e "using unsigned int for type in_addr_t" CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=unsigned" CFLAGS="$CFLAGS -DIN_ADDR_T=unsigned" else CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=in_addr_t" CFLAGS="$CFLAGS -DIN_ADDR_T=in_addr_t" fi dnl Check whether --with-version was given. AC_SUBST(LIBVERSION) AC_SUBST(VERSION) if test "${with_version}" = yes; then VERSION=${ac_option} else VERSION=`cat VERSION` fi echo -e "compiling version ${VERSION}" LIBVERSION=`echo ${VERSION} | cut -d '.' -f 1` CXXFLAGS="$CXXFLAGS -DVERSION=\\\"${VERSION}\\\"" CFLAGS="$CFLAGS -DVERSION=\\\"${VERSION}\\\"" dnl Check whether --with-static-png was given and dnl add -lpng or libpng.a to linking. if test "${with_static_png}" = yes; then echo -e "enabling static linking of PNG library" if test "$SUN" = yes && test -f "/usr/sfw/lib/libpng.a"; then LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libpng.a" else if test -f "/usr/lib/libpng.a" ; then LIBSTATIC="$LIBSTATIC /usr/lib/libpng.a" else if test -f "/usr/local/lib/libpng.a" ; then echo -e "assuming libpng.a in /usr/local/lib" LIBSTATIC="$LIBSTATIC /usr/local/lib/libpng.a" else echo -e "Warning: assuming libpng.a in the local path" LIBSTATIC="$LIBSTATIC libpng.a" fi fi fi else echo -e "enabling dynamic linking of PNG library" LIBSHARED="$LIBSHARED -lpng" fi dnl Check whether --with-static-jpeg was given and dnl add -ljpeg or libjpeg.a to linking. if test "${with_static_jpeg}" = yes; then echo -e "enabling static linking of JPEG library" if test "$SUN" = yes && test -f "/usr/sfw/lib/libjpeg.a"; then LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libjpeg.a" else if test -f "/usr/lib/libjpeg.a" ; then LIBSTATIC="$LIBSTATIC /usr/lib/libjpeg.a" else if test -f "/usr/local/lib/libjpeg.a" ; then echo -e "assuming libjpeg.a in /usr/local/lib" LIBSTATIC="$LIBSTATIC /usr/local/lib/libjpeg.a" else echo -e "Warning: assuming libjpeg.a in the local path" LIBSTATIC="$LIBSTATIC libjpeg.a" fi fi fi else echo -e "enabling dynamic linking of JPEG library" LIBSHARED="$LIBSHARED -ljpeg" fi dnl Check whether --with-static-z was given and dnl add -lz or libz.a to linking. if test "${with_static_z}" = yes; then echo -e "enabling static linking of Z library" if test "$SUN" = yes && test -f "/usr/sfw/lib/libz.a"; then LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libz.a" else if test -f "/usr/lib/libz.a" ; then LIBSTATIC="$LIBSTATIC /usr/lib/libz.a" else if test -f "/usr/local/lib/libz.a" ; then echo -e "assuming libz.a in /usr/local/lib" LIBSTATIC="$LIBSTATIC /usr/local/lib/libz.a" else echo -e "Warning: assuming libz.a in the local path" LIBSTATIC="$LIBSTATIC libz.a" fi fi fi else echo -e "enabling dynamic linking of Z library" LIBSHARED="$LIBSHARED -lz" fi dnl Finally compose the LIB variable. if test "$DARWIN" = yes ; then LIBS="$LIBS $LIBSTATIC $LIBSHARED" elif test "$SUN" = yes ; then LIBS="$LIBS $LIBSTATIC $LIBSHARED" else LIBS="$LIBS $LIBSTATIC -shared $LIBSHARED" fi dnl Check whether --with-symbols or --without-symbols was dnl given and set the required optimization level. if test "${with_symbols}" = yes; then echo -e "enabling production of debug symbols" CXXFLAGS="-g $CXXFLAGS" CFLAGS="-g $CFLAGS" else echo -e "disabling production of debug symbols" fi dnl Check whether --with-use-malloc or --without-use-malloc dnl was given. if test "${with_use_malloc}" = yes; then echo -e "disabling use of the STL allocators" CXXFLAGS="$CXXFLAGS -D__USE_MALLOC" else echo -e "enabling use of the STL allocators" fi dnl Check whether --with-info or --without-info was given. if test "${with_info}" = yes; then echo -e "enabling info output in the log file" CXXFLAGS="$CXXFLAGS -DINFO" CFLAGS="$CFLAGS -DINFO" else echo -e "disabling info output in the log file" fi dnl Check whether --with-valgrind or --without-valgrind was given. if test "${with_valgrind}" = yes; then echo -e "enabling valgrind memory checker workarounds" CXXFLAGS="$CXXFLAGS -DVALGRIND" CFLAGS="$CFLAGS -DVALGRIND" else echo -e "disabling valgrind memory checker workarounds" fi dnl Find makedepend somewhere. AC_SUBST(MAKEDEPEND) if test -x "../nx-X11/config/makedepend/makedepend" ; then MAKEDEPEND=../nx-X11/config/makedepend/makedepend else if test -x "/usr/X11R6/bin/makedepend" ; then MAKEDEPEND=/usr/X11R6/bin/makedepend else if test -x "/usr/openwin/bin/makedepend" ; then MAKEDEPEND=/usr/openwin/bin/makedepend else MAKEDEPEND=/usr/bin/makedepend fi fi fi dnl Determine what to build based on the platform. dnl Override the LIBS settings on Cygwin32 so that dnl we always link with the exact set of libraries. AC_SUBST(ALL) if test "$CYGWIN32" = yes; then ALL="\$(LIBCYGARCHIVE) \$(LIBCYGSHARED) \$(LIBARCHIVE)" LIBS="-lstdc++ -lpng -ljpeg -lz" else ALL="\$(LIBFULL) \$(LIBLOAD) \$(LIBSHARED) \$(LIBARCHIVE)" fi AC_OUTPUT(Makefile) nxcomp/StaticCompressor.h0000644000076400007640000000543411323113027016006 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef StaticCompressor_H #define StaticCompressor_H #include "Z.h" class EncodeBuffer; class DecodeBuffer; class StaticCompressor { public: StaticCompressor(int compressionLevel, int compressionThreshold); ~StaticCompressor(); int compressBuffer(const unsigned char *plainBuffer, const unsigned int plainSize, unsigned char *&compressedBuffer, unsigned int &compressedSize, EncodeBuffer &encodeBuffer); int compressBuffer(const unsigned char *plainBuffer, const unsigned int plainSize, unsigned char *&compressedBuffer, unsigned int &compressedSize); int decompressBuffer(unsigned char *plainBuffer, unsigned int plainSize, const unsigned char *&compressedBuffer, unsigned int &compressedSize, DecodeBuffer &decodeBuffer); int decompressBuffer(unsigned char *plainBuffer, const unsigned int plainSize, const unsigned char *compressedBuffer, const unsigned compressedSize); static int isCompressionLevel(int compressionLevel) { return (compressionLevel == Z_DEFAULT_COMPRESSION || (compressionLevel >= Z_NO_COMPRESSION && compressionLevel <= Z_BEST_COMPRESSION)); } int fullReset() { return (deflateReset(&compressionStream_) == Z_OK && inflateReset(&decompressionStream_) == Z_OK); } private: z_stream compressionStream_; z_stream decompressionStream_; unsigned char *buffer_; unsigned int bufferSize_; int threshold_; }; #endif nxcomp/ListFontsReply.cpp0000644000076400007640000001270311323113031015766 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ListFontsReply.h" #include "ServerCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef DUMP #undef TEST #undef DEBUG ListFontsReplyStore::ListFontsReplyStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = LISTFONTSREPLY_ENABLE_CACHE; enableData = LISTFONTSREPLY_ENABLE_DATA; enableSplit = LISTFONTSREPLY_ENABLE_SPLIT; enableCompress = LISTFONTSREPLY_ENABLE_COMPRESS; if (control -> isProtoStep7() == 1) { enableCompress = LISTFONTSREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7; } dataLimit = LISTFONTSREPLY_DATA_LIMIT; dataOffset = LISTFONTSREPLY_DATA_OFFSET; cacheSlots = LISTFONTSREPLY_CACHE_SLOTS; cacheThreshold = LISTFONTSREPLY_CACHE_THRESHOLD; cacheLowerThreshold = LISTFONTSREPLY_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } ListFontsReplyStore::~ListFontsReplyStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int ListFontsReplyStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message; // // Here is the fingerprint. // listFontsReply -> number_of_names = GetUINT(buffer + 8, bigEndian); // // Clean up padding bytes. // if ((int) size > dataOffset) { unsigned int current; unsigned int length; unsigned int nstringInNames; unsigned char *end = NULL; unsigned char *pad = NULL; #ifdef DUMP *logofs << "\n" << logofs_flush; *logofs << "Number of STRING8 " << listFontsReply -> number_of_names << ".\n" << logofs_flush; *logofs << "Size " << size << ".\n" << logofs_flush; DumpHexData(buffer, size); *logofs << "\n" << logofs_flush; #endif length = LISTFONTSREPLY_DATA_OFFSET; for (nstringInNames = 0; nstringInNames < listFontsReply -> number_of_names && listFontsReply -> number_of_names > 0; nstringInNames++) { // // Start with offset LISTFONTSREPLY_DATA_OFFSET 32. // current = buffer[length]; length += current + 1; #ifdef DUMP *logofs << "\nString number : " << nstringInNames << " Current length : " << current << "\n" << logofs_flush; #endif } #ifdef DUMP *logofs << "\nFinal length " << length << "\n" << logofs_flush; #endif end = ((unsigned char *) buffer) + size; for (pad = ((unsigned char *) buffer) + length; pad < end; pad++) { *pad = 0; #ifdef DUMP *logofs << "\nPadding ." << "\n" << logofs_flush; #endif } } #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int ListFontsReplyStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message; // // Fill all the message's fields. // PutUINT(listFontsReply -> number_of_names, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void ListFontsReplyStore::dumpIdentity(const Message *message) const { #ifdef DUMP ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message; *logofs << name() << ": Identity number_of_names " << listFontsReply -> number_of_names << ", size " << listFontsReply -> size_ << ".\n"; #endif } void ListFontsReplyStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // Field number_of_names. // md5_append(md5_state_, buffer + 8, 2); } nxcomp/ClientStore.cpp0000644000076400007640000001660211323113031015262 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ClientStore.h" // // Cached request classes. // #include "ChangeProperty.h" #include "SendEvent.h" #include "CreateGC.h" #include "ChangeGC.h" #include "CreatePixmap.h" #include "SetClipRectangles.h" #include "CopyArea.h" #include "PolyLine.h" #include "PolySegment.h" #include "PolyFillRectangle.h" #include "PutImage.h" #include "TranslateCoords.h" #include "GetImage.h" #include "ClearArea.h" #include "ConfigureWindow.h" #include "ShapeExtension.h" #include "RenderExtension.h" #include "PolyText8.h" #include "PolyText16.h" #include "ImageText8.h" #include "ImageText16.h" #include "PolyPoint.h" #include "PolyFillArc.h" #include "PolyArc.h" #include "FillPoly.h" #include "InternAtom.h" #include "GetProperty.h" #include "SetUnpackGeometry.h" #include "SetUnpackColormap.h" #include "SetUnpackAlpha.h" #include "PutPackedImage.h" #include "GenericRequest.h" #include "ChangeGCCompat.h" #include "CreatePixmapCompat.h" #include "SetUnpackColormapCompat.h" #include "SetUnpackAlphaCompat.h" // // Set the verbosity level. // #define WARNING #define PANIC #undef TEST ClientStore::ClientStore(StaticCompressor *compressor) : compressor_(compressor) { if (logofs == NULL) { logofs = &cout; } for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { requests_[i] = NULL; } requests_[X_ChangeProperty] = new ChangePropertyStore(); requests_[X_SendEvent] = new SendEventStore(); requests_[X_CreateGC] = new CreateGCStore(); requests_[X_SetClipRectangles] = new SetClipRectanglesStore(); requests_[X_CopyArea] = new CopyAreaStore(); requests_[X_PolyLine] = new PolyLineStore(); requests_[X_PolySegment] = new PolySegmentStore(); requests_[X_PolyFillRectangle] = new PolyFillRectangleStore(); requests_[X_PutImage] = new PutImageStore(compressor); requests_[X_TranslateCoords] = new TranslateCoordsStore(); requests_[X_GetImage] = new GetImageStore(); requests_[X_ClearArea] = new ClearAreaStore(); requests_[X_ConfigureWindow] = new ConfigureWindowStore(); requests_[X_PolyText8] = new PolyText8Store(); requests_[X_PolyText16] = new PolyText16Store(); requests_[X_ImageText8] = new ImageText8Store(); requests_[X_ImageText16] = new ImageText16Store(); requests_[X_PolyPoint] = new PolyPointStore(); requests_[X_PolyFillArc] = new PolyFillArcStore(); requests_[X_PolyArc] = new PolyArcStore(); requests_[X_FillPoly] = new FillPolyStore(); requests_[X_InternAtom] = new InternAtomStore(); requests_[X_GetProperty] = new GetPropertyStore(); requests_[X_NXInternalShapeExtension] = new ShapeExtensionStore(compressor); requests_[X_NXInternalGenericRequest] = new GenericRequestStore(compressor); requests_[X_NXInternalRenderExtension] = new RenderExtensionStore(compressor); requests_[X_NXSetUnpackGeometry] = new SetUnpackGeometryStore(compressor); requests_[X_NXPutPackedImage] = new PutPackedImageStore(compressor); if (control -> isProtoStep7() == 1) { requests_[X_ChangeGC] = new ChangeGCStore(); requests_[X_CreatePixmap] = new CreatePixmapStore(); requests_[X_NXSetUnpackColormap] = new SetUnpackColormapStore(compressor); requests_[X_NXSetUnpackAlpha] = new SetUnpackAlphaStore(compressor); } else { requests_[X_ChangeGC] = new ChangeGCCompatStore(); requests_[X_CreatePixmap] = new CreatePixmapCompatStore(); requests_[X_NXSetUnpackColormap] = new SetUnpackColormapCompatStore(compressor); requests_[X_NXSetUnpackAlpha] = new SetUnpackAlphaCompatStore(compressor); } for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++) { splits_[i] = NULL; } commits_ = new CommitStore(compressor); } ClientStore::~ClientStore() { if (logofs == NULL) { logofs = &cout; } for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { delete requests_[i]; } for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++) { delete splits_[i]; } delete commits_; } int ClientStore::saveRequestStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient, T_checksum_action checksumAction, T_data_action dataAction) const { for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { if (requests_[i] != NULL && requests_[i] -> saveStore(cachefs, md5StateStream, md5StateClient, checksumAction, dataAction, storeBigEndian()) < 0) { #ifdef WARNING *logofs << "ClientStore: WARNING! Error saving request store " << "for OPCODE#" << (unsigned int) i << ".\n" << logofs_flush; #endif cerr << "Warning" << ": Error saving request store " << "for opcode '" << (unsigned int) i << "'.\n"; return -1; } } return 1; } int ClientStore::loadRequestStores(istream *cachefs, md5_state_t *md5StateStream, T_checksum_action checksumAction, T_data_action dataAction) const { for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { if (requests_[i] != NULL && requests_[i] -> loadStore(cachefs, md5StateStream, checksumAction, dataAction, storeBigEndian()) < 0) { #ifdef WARNING *logofs << "ClientStore: WARNING! Error loading request store " << "for OPCODE#" << (unsigned int) i << ".\n" << logofs_flush; #endif return -1; } } return 1; } void ClientStore::dumpSplitStores() const { for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++) { if (splits_[i] != NULL) { splits_[i] -> dump(); } } if ((getSplitTotalSize() != 0 && getSplitTotalStorageSize() == 0) || (getSplitTotalSize() == 0 && getSplitTotalStorageSize() != 0)) { #ifdef PANIC *logofs << "ClientStore: PANIC! Inconsistency detected " << "while handling the split stores.\n" << logofs_flush; #endif HandleCleanup(); } } nxcomp/Control.cpp0000644000076400007640000005365411323113030014456 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "NXpack.h" #include "Control.h" #define PANIC #define WARNING #undef TEST #undef DEBUG // // Flush immediately on prioritized messages. // #define FLUSH_PRIORITY 0 // // Maximum number of bytes sent for each token. // #define TOKEN_SIZE 1536 // // Maximum number of tokens that can be spent // by the client proxy before having to block // waiting for a token reply. // #define TOKEN_LIMIT 24 // // By default assume the proxy is running as a // standalone program. // #define LINK_ENCRYPTED 0 // // Maximum number of pids the proxy will record // and kill at shutdown. // #define KILL_LIMIT 16 // // Allocate on the NX client side channels whose // ids are a multiple of 8 (starting from 0). All // the other ids can be used to allocate channels // at the NX server side (X client side). // #define CHANNEL_MASK 0x07 // // Kill session if control parameters cannot be // negotiated before this timeout. // #define INIT_TIMEOUT 60000 // // Enter the congestion state if the remote does // not reply to a ping within the given amount // of time. // #define PING_TIMEOUT 5000 // // Only send one motion event any N milliseconds. // #define MOTION_TIMEOUT 0 // // Force an update of the congestion counter if // the proxy is idle for this time. // #define IDLE_TIMEOUT 50 // // Close X connection if can't write before this // timeout. // #define CHANNEL_TIMEOUT 10000 // // Warn user (or close proxy connection) if don't // receive any data before this timeout. // #define PROXY_TIMEOUT 120000 // // How many milliseconds to wait for the shared // memory completion event to become available. // #define SHMEM_TIMEOUT 200 // // Before closing down the proxy, wait for the // given amount of miliseconds to let all the // running applications to close down their // connections. // // A null timeout will cause the proxy to wait // indefinitely, until the watchdog process is // killed. This is usually the way the proxy is // started by the NX server. If on the other // hand a timeout is given and there no channel // is remaining, the proxy will be closed down // using a small timeout, presently of 500 ms. // #define CLEANUP_TIMEOUT 3000 // // Wait this amount of milliseconds after any // iteration of the house-keeping process. // #define KEEPER_TIMEOUT 60000 // // In case of timeout, select can return control // to program earlier or later of this amount of // ms. Consider this when calculating if timeout // is elapsed. // #define LATENCY_TIMEOUT 1 // // Control memory allocation in transport // and other classes. // #define TRANSPORT_X_BUFFER_SIZE 131072 #define TRANSPORT_PROXY_BUFFER_SIZE 65536 #define TRANSPORT_GENERIC_BUFFER_SIZE 16384 #define TRANSPORT_X_BUFFER_THRESHOLD 262144 #define TRANSPORT_PROXY_BUFFER_THRESHOLD 131072 #define TRANSPORT_GENERIC_BUFFER_THRESHOLD 32768 // // Never allow buffers to exceed this limit. // #define TRANSPORT_MAXIMUM_BUFFER_SIZE 393216 // // Immediately flush the accumulated data to // the X server if the write buffer exceeds // this size. // #define TRANSPORT_FLUSH_BUFFER_SIZE 16384 // // Defaults used for socket options. // #define OPTION_PROXY_KEEP_ALIVE 0 #define OPTION_PROXY_LOW_DELAY 1 #define OPTION_PROXY_CLIENT_NO_DELAY 1 #define OPTION_PROXY_SERVER_NO_DELAY 1 #define OPTION_CLIENT_NO_DELAY 1 #define OPTION_SERVER_NO_DELAY 1 #define OPTION_PROXY_RECEIVE_BUFFER -1 #define OPTION_CLIENT_RECEIVE_BUFFER -1 #define OPTION_SERVER_RECEIVE_BUFFER -1 #define OPTION_PROXY_SEND_BUFFER -1 #define OPTION_CLIENT_SEND_BUFFER -1 #define OPTION_SERVER_SEND_BUFFER -1 #define OPTION_PROXY_RETRY_CONNECT 30 #define OPTION_PROXY_RETRY_ACCEPT 3 #define OPTION_SERVER_RETRY_CONNECT 3 // // Defaults used for cache persistence. // #define PERSISTENT_CACHE_THRESHOLD 102400 #define PERSISTENT_CACHE_ENABLE_LOAD 1 #define PERSISTENT_CACHE_ENABLE_SAVE 1 #define PERSISTENT_CACHE_CHECK_ON_SHUTDOWN 0 #define PERSISTENT_CACHE_LOAD_PACKED 1 #define PERSISTENT_CACHE_LOAD_RENDER 1 #define PERSISTENT_CACHE_DISK_LIMIT 33554432 // // Defaults used for image cache. // #define IMAGE_CACHE_ENABLE_LOAD 0 #define IMAGE_CACHE_ENABLE_SAVE 0 #define IMAGE_CACHE_DISK_LIMIT 33554432 // // Suggested defaults for read length parameters // used by read buffer classes. // #define CLIENT_INITIAL_READ_SIZE 8192 #define CLIENT_MAXIMUM_BUFFER_SIZE 262144 #define SERVER_INITIAL_READ_SIZE 8192 #define SERVER_MAXIMUM_BUFFER_SIZE 65536 #define PROXY_INITIAL_READ_SIZE 65536 #define PROXY_MAXIMUM_BUFFER_SIZE 262144 + 1024 #define GENERIC_INITIAL_READ_SIZE 8192 #define GENERIC_MAXIMUM_BUFFER_SIZE 8192 // // Calculate bitrate in given time frames. // Values are in milliseconds. // #define SHORT_BITRATE_TIME_FRAME 5000 #define LONG_BITRATE_TIME_FRAME 30000 // // Bandwidth control. A value of 0 means no // limit. Values are stored internally in // bytes per second. // #define CLIENT_BITRATE_LIMIT 0 #define SERVER_BITRATE_LIMIT 0 // // Default values for cache control. We limit // the maximum size of a request to 262144 but // we need to consider the replies, whose size // may be up to 4MB. // #define MINIMUM_MESSAGE_SIZE 4 #define MAXIMUM_MESSAGE_SIZE 4194304 #define MAXIMUM_REQUEST_SIZE 262144 #define CLIENT_TOTAL_STORAGE_SIZE 8388608 #define SERVER_TOTAL_STORAGE_SIZE 8388608 #define STORE_TIME_LIMIT 3600 #define STORE_HITS_LOAD_BONUS 10 #define STORE_HITS_ADD_BONUS 20 #define STORE_HITS_LIMIT 100 #define STORE_HITS_TOUCH 1 #define STORE_HITS_UNTOUCH 2 // // Default parameters for message splitting. // #define SPLIT_MODE 1 #define SPLIT_TIMEOUT 50 #define SPLIT_TOTAL_SIZE 128 #define SPLIT_TOTAL_STORAGE_SIZE 1048576 #define SPLIT_DATA_THRESHOLD 65536 #define SPLIT_DATA_PACKET_LIMIT 24576 // // Agent related parameters. // #define PACK_METHOD 63 #define PACK_QUALITY 9 #define HIDE_RENDER 0 #define TAINT_REPLIES 1 #define TAINT_THRESHOLD 8 // // In current version only X server support is // implemented. Note that use of shared memory // is negotiated according to options provided // by the user. // #define SHMEM_CLIENT 0 #define SHMEM_SERVER 1 // // Default size of shared memory segments used // in MIT-SHM support. // #define SHMEM_CLIENT_SIZE 0 #define SHMEM_SERVER_SIZE 2097152 // // What do we do at the end of session? If this // flag is set, we launch a new client letting // the user run a new NX session. // #define ENABLE_RESTART_ON_SHUTDOWN 0 // // Do we produce a core dump on fatal errors? // #define ENABLE_CORE_DUMP_ON_ABORT 0 // // Reopen the log file if it exceeds this size. // #define FILE_SIZE_LIMIT 60000000 // // Check periodically if we need to truncate the // log file. By default check every minute. // #define FILE_SIZE_CHECK_TIMEOUT 60000 // // Set defaults for control. They should be what // you get in case of 'local' connection. // Control::Control() { ProxyMode = proxy_undefined; ProxyStage = stage_undefined; SessionMode = session_undefined; FlushPolicy = policy_undefined; LinkMode = link_undefined; LinkEncrypted = LINK_ENCRYPTED; FlushPriority = FLUSH_PRIORITY; TokenSize = TOKEN_SIZE; TokenLimit = TOKEN_LIMIT; ChannelMask = CHANNEL_MASK; InitTimeout = INIT_TIMEOUT; PingTimeout = PING_TIMEOUT; MotionTimeout = MOTION_TIMEOUT; IdleTimeout = IDLE_TIMEOUT; ChannelTimeout = CHANNEL_TIMEOUT; ProxyTimeout = PROXY_TIMEOUT; ShmemTimeout = SHMEM_TIMEOUT; CleanupTimeout = CLEANUP_TIMEOUT; KeeperTimeout = KEEPER_TIMEOUT; LatencyTimeout = LATENCY_TIMEOUT; FileSizeLimit = FILE_SIZE_LIMIT; FileSizeCheckTimeout = FILE_SIZE_CHECK_TIMEOUT; EnableRestartOnShutdown = ENABLE_RESTART_ON_SHUTDOWN; KillDaemonOnShutdownLimit = KILL_LIMIT; KillDaemonOnShutdown = new int[KillDaemonOnShutdownLimit]; for (int i = 0; i < KILL_LIMIT; i++) { KillDaemonOnShutdown[i] = -1; } KillDaemonOnShutdownNumber = 0; EnableCoreDumpOnAbort = ENABLE_CORE_DUMP_ON_ABORT; // // Collect statistics by default. // EnableStatistics = 1; // // Memory restrictions if any. // LocalMemoryLevel = -1; // // Compression must be negotiated between proxies. // LocalDeltaCompression = -1; RemoteDeltaCompression = -1; LocalDataCompression = -1; LocalStreamCompression = -1; RemoteDataCompression = -1; RemoteStreamCompression = -1; LocalDataCompressionLevel = -1; LocalDataCompressionThreshold = -1; LocalStreamCompressionLevel = -1; RemoteDataCompressionLevel = -1; RemoteStreamCompressionLevel = -1; // // Transport buffers' allocation parameters. // TransportXBufferSize = TRANSPORT_X_BUFFER_SIZE; TransportProxyBufferSize = TRANSPORT_PROXY_BUFFER_SIZE; TransportGenericBufferSize = TRANSPORT_GENERIC_BUFFER_SIZE; TransportXBufferThreshold = TRANSPORT_X_BUFFER_THRESHOLD; TransportProxyBufferThreshold = TRANSPORT_PROXY_BUFFER_THRESHOLD; TransportGenericBufferThreshold = TRANSPORT_GENERIC_BUFFER_THRESHOLD; TransportMaximumBufferSize = TRANSPORT_MAXIMUM_BUFFER_SIZE; // // Flush the write buffer if it exceeds // this size. // TransportFlushBufferSize = TRANSPORT_FLUSH_BUFFER_SIZE; // // Socket options. // OptionProxyKeepAlive = OPTION_PROXY_KEEP_ALIVE; OptionProxyLowDelay = OPTION_PROXY_LOW_DELAY; OptionProxyClientNoDelay = OPTION_PROXY_CLIENT_NO_DELAY; OptionProxyServerNoDelay = OPTION_PROXY_SERVER_NO_DELAY; OptionClientNoDelay = OPTION_CLIENT_NO_DELAY; OptionServerNoDelay = OPTION_SERVER_NO_DELAY; OptionProxyReceiveBuffer = OPTION_PROXY_RECEIVE_BUFFER; OptionClientReceiveBuffer = OPTION_CLIENT_RECEIVE_BUFFER; OptionServerReceiveBuffer = OPTION_SERVER_RECEIVE_BUFFER; OptionProxySendBuffer = OPTION_PROXY_SEND_BUFFER; OptionClientSendBuffer = OPTION_CLIENT_SEND_BUFFER; OptionServerSendBuffer = OPTION_SERVER_SEND_BUFFER; OptionProxyRetryAccept = OPTION_PROXY_RETRY_ACCEPT; OptionProxyRetryConnect = OPTION_PROXY_RETRY_CONNECT; OptionServerRetryConnect = OPTION_SERVER_RETRY_CONNECT; // // Base NX directories. // HomePath = NULL; RootPath = NULL; SystemPath = NULL; TempPath = NULL; ClientPath = NULL; // // Set defaults for handling persistent cache. // PersistentCachePath = NULL; PersistentCacheName = NULL; PersistentCacheThreshold = PERSISTENT_CACHE_THRESHOLD; PersistentCacheEnableLoad = PERSISTENT_CACHE_ENABLE_LOAD; PersistentCacheEnableSave = PERSISTENT_CACHE_ENABLE_SAVE; PersistentCacheCheckOnShutdown = PERSISTENT_CACHE_CHECK_ON_SHUTDOWN; PersistentCacheLoadPacked = PERSISTENT_CACHE_LOAD_PACKED; PersistentCacheLoadRender = PERSISTENT_CACHE_LOAD_RENDER; PersistentCacheDiskLimit = PERSISTENT_CACHE_DISK_LIMIT; // // Set defaults for image cache. // ImageCachePath = NULL; ImageCacheEnableLoad = IMAGE_CACHE_ENABLE_LOAD; ImageCacheEnableSave = IMAGE_CACHE_ENABLE_SAVE; ImageCacheDiskLimit = IMAGE_CACHE_DISK_LIMIT; // // Set defaults for the read buffers. // ClientInitialReadSize = CLIENT_INITIAL_READ_SIZE; ClientMaximumBufferSize = CLIENT_MAXIMUM_BUFFER_SIZE; ServerInitialReadSize = SERVER_INITIAL_READ_SIZE; ServerMaximumBufferSize = SERVER_MAXIMUM_BUFFER_SIZE; ProxyInitialReadSize = PROXY_INITIAL_READ_SIZE; ProxyMaximumBufferSize = PROXY_MAXIMUM_BUFFER_SIZE; GenericInitialReadSize = GENERIC_INITIAL_READ_SIZE; GenericMaximumBufferSize = GENERIC_MAXIMUM_BUFFER_SIZE; ShortBitrateTimeFrame = SHORT_BITRATE_TIME_FRAME; LongBitrateTimeFrame = LONG_BITRATE_TIME_FRAME; // // Bandwidth control. // LocalBitrateLimit = -1; ClientBitrateLimit = CLIENT_BITRATE_LIMIT; ServerBitrateLimit = SERVER_BITRATE_LIMIT; // // Default parameters for message handling. // ClientTotalStorageSize = CLIENT_TOTAL_STORAGE_SIZE; ServerTotalStorageSize = SERVER_TOTAL_STORAGE_SIZE; LocalTotalStorageSize = -1; RemoteTotalStorageSize = -1; StoreTimeLimit = STORE_TIME_LIMIT; StoreHitsLoadBonus = STORE_HITS_LOAD_BONUS; StoreHitsAddBonus = STORE_HITS_ADD_BONUS; StoreHitsLimit = STORE_HITS_LIMIT; StoreHitsTouch = STORE_HITS_TOUCH; StoreHitsUntouch = STORE_HITS_UNTOUCH; MinimumMessageSize = MINIMUM_MESSAGE_SIZE; MaximumMessageSize = MAXIMUM_MESSAGE_SIZE; MaximumRequestSize = MAXIMUM_REQUEST_SIZE; SplitMode = SPLIT_MODE; SplitTimeout = SPLIT_TIMEOUT; SplitTotalSize = SPLIT_TOTAL_SIZE; SplitTotalStorageSize = SPLIT_TOTAL_STORAGE_SIZE; SplitDataThreshold = SPLIT_DATA_THRESHOLD; SplitDataPacketLimit = SPLIT_DATA_PACKET_LIMIT; PackMethod = PACK_METHOD; PackQuality = PACK_QUALITY; HideRender = HIDE_RENDER; TaintReplies = TAINT_REPLIES; TaintThreshold = TAINT_THRESHOLD; ShmemClient = SHMEM_CLIENT; ShmemServer = SHMEM_SERVER; ShmemClientSize = SHMEM_CLIENT_SIZE; ShmemServerSize = SHMEM_SERVER_SIZE; // // Get local version number from compile time // settings. Version of remote proxy will be // checked at connection time. // RemoteVersionMajor = -1; RemoteVersionMinor = -1; RemoteVersionPatch = -1; CompatVersionMajor = -1; CompatVersionMinor = -1; CompatVersionPatch = -1; char version[32]; strcpy(version, VERSION); char *value; value = strtok(version, "."); for (int i = 0; value != NULL && i < 3; i++) { switch (i) { case 0: LocalVersionMajor = atoi(value); break; case 1: LocalVersionMinor = atoi(value); break; case 2: LocalVersionPatch = atoi(value); break; } value = strtok(NULL, "."); } #ifdef TEST *logofs << "Control: Major version is " << LocalVersionMajor << " minor is " << LocalVersionMinor << " patch is " << LocalVersionPatch << ".\n" << logofs_flush; #endif // // Initialize local implemented methods later // and negotiate remote methods at connection // time. // LocalUnpackMethods = NULL; RemoteUnpackMethods = NULL; // // Set to 1 those methods which are implemented. // setLocalUnpackMethods(); // // Set the protocol version at the // time the session is negotiated. // protoStep6_ = 0; protoStep7_ = 0; protoStep8_ = 0; protoStep9_ = 0; protoStep10_ = 0; } Control::~Control() { if (KillDaemonOnShutdown != NULL) { delete [] KillDaemonOnShutdown; } if (HomePath != NULL) { delete [] HomePath; } if (RootPath != NULL) { delete [] RootPath; } if (SystemPath != NULL) { delete [] SystemPath; } if (TempPath != NULL) { delete [] TempPath; } if (ClientPath != NULL) { delete [] ClientPath; } if (PersistentCachePath != NULL) { delete [] PersistentCachePath; } if (PersistentCacheName != NULL) { delete [] PersistentCacheName; } if (LocalUnpackMethods != NULL) { delete [] LocalUnpackMethods; } if (RemoteUnpackMethods != NULL) { delete [] RemoteUnpackMethods; } if (ImageCachePath != NULL) { delete [] ImageCachePath; } } // // Set the protocol step based on the // remote version. // void Control::setProtoStep(int step) { switch (step) { case 6: { protoStep6_ = 1; protoStep7_ = 0; protoStep8_ = 0; protoStep9_ = 0; protoStep10_ = 0; break; } case 7: { protoStep6_ = 1; protoStep7_ = 1; protoStep8_ = 0; protoStep9_ = 0; protoStep10_ = 0; break; } case 8: { protoStep6_ = 1; protoStep7_ = 1; protoStep8_ = 1; protoStep9_ = 0; protoStep10_ = 0; break; } case 9: { protoStep6_ = 1; protoStep7_ = 1; protoStep8_ = 1; protoStep9_ = 1; protoStep10_ = 0; break; } case 10: { protoStep6_ = 1; protoStep7_ = 1; protoStep8_ = 1; protoStep9_ = 1; protoStep10_ = 1; break; } default: { #ifdef PANIC *logofs << "Control: PANIC! Invalid protocol step " << "with value " << step << ".\n" << logofs_flush; #endif HandleCleanup(); } } } int Control::getProtoStep() { if (protoStep10_ == 1) { return 10; } else if (protoStep9_ == 1) { return 9; } else if (protoStep8_ == 1) { return 8; } else if (protoStep7_ == 1) { return 7; } else if (protoStep6_ == 1) { return 6; } else { #ifdef PANIC *logofs << "Control: PANIC! Can't identify the " << "protocol step.\n" << logofs_flush; #endif HandleCleanup(); } } // // Set here the pack/unpack methods that are // implemented by this NX proxy. // void Control::setLocalUnpackMethods() { LocalUnpackMethods = new unsigned char[PACK_METHOD_LIMIT]; RemoteUnpackMethods = new unsigned char[PACK_METHOD_LIMIT]; for (int i = 0; i < PACK_METHOD_LIMIT; i++) { LocalUnpackMethods[i] = 0; RemoteUnpackMethods[i] = 0; } LocalUnpackMethods[NO_PACK] = 1; LocalUnpackMethods[PACK_MASKED_8_COLORS] = 1; LocalUnpackMethods[PACK_MASKED_64_COLORS] = 1; LocalUnpackMethods[PACK_MASKED_256_COLORS] = 1; LocalUnpackMethods[PACK_MASKED_512_COLORS] = 1; LocalUnpackMethods[PACK_MASKED_4K_COLORS] = 1; LocalUnpackMethods[PACK_MASKED_32K_COLORS] = 1; LocalUnpackMethods[PACK_MASKED_64K_COLORS] = 1; LocalUnpackMethods[PACK_MASKED_256K_COLORS] = 1; LocalUnpackMethods[PACK_MASKED_2M_COLORS] = 1; LocalUnpackMethods[PACK_MASKED_16M_COLORS] = 1; LocalUnpackMethods[PACK_RAW_8_BITS] = 1; LocalUnpackMethods[PACK_RAW_16_BITS] = 1; LocalUnpackMethods[PACK_RAW_24_BITS] = 1; LocalUnpackMethods[PACK_COLORMAP_256_COLORS] = 1; LocalUnpackMethods[PACK_JPEG_8_COLORS] = 1; LocalUnpackMethods[PACK_JPEG_64_COLORS] = 1; LocalUnpackMethods[PACK_JPEG_256_COLORS] = 1; LocalUnpackMethods[PACK_JPEG_512_COLORS] = 1; LocalUnpackMethods[PACK_JPEG_4K_COLORS] = 1; LocalUnpackMethods[PACK_JPEG_32K_COLORS] = 1; LocalUnpackMethods[PACK_JPEG_64K_COLORS] = 1; LocalUnpackMethods[PACK_JPEG_256K_COLORS] = 1; LocalUnpackMethods[PACK_JPEG_2M_COLORS] = 1; LocalUnpackMethods[PACK_JPEG_16M_COLORS] = 1; LocalUnpackMethods[PACK_PNG_8_COLORS] = 1; LocalUnpackMethods[PACK_PNG_64_COLORS] = 1; LocalUnpackMethods[PACK_PNG_256_COLORS] = 1; LocalUnpackMethods[PACK_PNG_512_COLORS] = 1; LocalUnpackMethods[PACK_PNG_4K_COLORS] = 1; LocalUnpackMethods[PACK_PNG_32K_COLORS] = 1; LocalUnpackMethods[PACK_PNG_64K_COLORS] = 1; LocalUnpackMethods[PACK_PNG_256K_COLORS] = 1; LocalUnpackMethods[PACK_PNG_2M_COLORS] = 1; LocalUnpackMethods[PACK_PNG_16M_COLORS] = 1; LocalUnpackMethods[PACK_RGB_16M_COLORS] = 1; LocalUnpackMethods[PACK_RLE_16M_COLORS] = 1; LocalUnpackMethods[PACK_ALPHA] = 1; LocalUnpackMethods[PACK_COLORMAP] = 1; LocalUnpackMethods[PACK_BITMAP_16M_COLORS] = 1; } nxcomp/RenderComposite.cpp0000644000076400007640000003111311323113030016122 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderComposite.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), clientCache -> renderMaskPictureCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 16, bigEndian), clientCache -> renderDstPictureCache); // // Src X and Y. // encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian), clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian), clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); // // Mask X and Y. // encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 24, bigEndian), clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 26, bigEndian), clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); // // Dst X and Y. // encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 28, bigEndian), clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 30, bigEndian), clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); // // Width and height. // encodeBuffer.encodeCachedValue(GetUINT(buffer + 32, bigEndian), 16, clientCache -> renderWidthCache, 11); encodeBuffer.encodeCachedValue(GetUINT(buffer + 34, bigEndian), 16, clientCache -> renderHeightCache, 11); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> renderMaskPictureCache); PutULONG(value, buffer + 12, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> renderDstPictureCache); PutULONG(value, buffer + 16, bigEndian); // // Src X and Y. // decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian); // // Mask X and Y. // decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); PutUINT(clientCache -> renderLastX, buffer + 24, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); PutUINT(clientCache -> renderLastY, buffer + 26, bigEndian); // // Dst X and Y. // decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); PutUINT(clientCache -> renderLastX, buffer + 28, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); PutUINT(clientCache -> renderLastY, buffer + 30, bigEndian); // // Width and height. // decodeBuffer.decodeCachedValue(value, 16, clientCache -> renderWidthCache, 11); PutUINT(value, buffer + 32, bigEndian); decodeBuffer.decodeCachedValue(value, 16, clientCache -> renderHeightCache, 11); PutUINT(value, buffer + 34, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.composite.type = *(buffer + 1); renderExtension -> data.composite.op = *(buffer + 4); renderExtension -> data.composite.src_id = GetULONG(buffer + 8, bigEndian); renderExtension -> data.composite.msk_id = GetULONG(buffer + 12, bigEndian); renderExtension -> data.composite.dst_id = GetULONG(buffer + 16, bigEndian); renderExtension -> data.composite.src_x = GetUINT(buffer + 20, bigEndian); renderExtension -> data.composite.src_y = GetUINT(buffer + 22, bigEndian); renderExtension -> data.composite.msk_x = GetUINT(buffer + 24, bigEndian); renderExtension -> data.composite.msk_y = GetUINT(buffer + 26, bigEndian); renderExtension -> data.composite.dst_x = GetUINT(buffer + 28, bigEndian); renderExtension -> data.composite.dst_y = GetUINT(buffer + 30, bigEndian); renderExtension -> data.composite.width = GetUINT(buffer + 32, bigEndian); renderExtension -> data.composite.height = GetUINT(buffer + 34, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.composite.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.composite.type; *(buffer + 4) = renderExtension -> data.composite.op; PutULONG(renderExtension -> data.composite.src_id, buffer + 8, bigEndian); PutULONG(renderExtension -> data.composite.msk_id, buffer + 12, bigEndian); PutULONG(renderExtension -> data.composite.dst_id, buffer + 16, bigEndian); PutUINT(renderExtension -> data.composite.src_x, buffer + 20, bigEndian); PutUINT(renderExtension -> data.composite.src_y, buffer + 22, bigEndian); PutUINT(renderExtension -> data.composite.msk_x, buffer + 24, bigEndian); PutUINT(renderExtension -> data.composite.msk_y, buffer + 26, bigEndian); PutUINT(renderExtension -> data.composite.dst_x, buffer + 28, bigEndian); PutUINT(renderExtension -> data.composite.dst_y, buffer + 30, bigEndian); PutUINT(renderExtension -> data.composite.width, buffer + 32, bigEndian); PutUINT(renderExtension -> data.composite.height, buffer + 34, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.composite.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { // // Include the minor opcode and size in the // identity, plus the operator, the x and y // of the source and mask and the width and // height of the destination. // md5_append(md5_state, buffer + 1, 4); md5_append(md5_state, buffer + 20, 8); md5_append(md5_state, buffer + 32, 4); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Source " << renderExtension -> data.composite.src_id << " mask " << renderExtension -> data.composite.msk_id << " destination " << renderExtension -> data.composite.msk_id << ".\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(renderExtension -> data.composite.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.composite.src_id = renderExtension -> data.composite.src_id; encodeBuffer.encodeXidValue(renderExtension -> data.composite.msk_id, clientCache -> renderMaskPictureCache); cachedRenderExtension -> data.composite.msk_id = renderExtension -> data.composite.msk_id; encodeBuffer.encodeXidValue(renderExtension -> data.composite.dst_id, clientCache -> renderDstPictureCache); cachedRenderExtension -> data.composite.dst_id = renderExtension -> data.composite.dst_id; // // Dst X and Y. // unsigned int value; unsigned int previous; value = renderExtension -> data.composite.dst_x; previous = cachedRenderExtension -> data.composite.dst_x; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderXCache, 11); cachedRenderExtension -> data.composite.dst_x = value; value = renderExtension -> data.composite.dst_y; previous = cachedRenderExtension -> data.composite.dst_y; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderYCache, 11); cachedRenderExtension -> data.composite.dst_y = value; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.composite.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.composite.src_id, clientCache -> renderSrcPictureCache); decodeBuffer.decodeXidValue(renderExtension -> data.composite.msk_id, clientCache -> renderMaskPictureCache); decodeBuffer.decodeXidValue(renderExtension -> data.composite.dst_id, clientCache -> renderDstPictureCache); // // Dst X and Y. // unsigned int value; unsigned int previous; previous = renderExtension -> data.composite.dst_x; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderXCache, 11); renderExtension -> data.composite.dst_x = value; previous = renderExtension -> data.composite.dst_y; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderYCache, 11); renderExtension -> data.composite.dst_y = value; #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.composite.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/RenderFillRectangles.h0000644000076400007640000000466211323113030016534 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderFillRectangles_H #define RenderFillRectangles_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderFillRectangles" #undef MESSAGE_STORE #define MESSAGE_STORE RenderFillRectanglesStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 12 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderFillRectangles_H */ nxcomp/RenderFreeGlyphSet.h0000644000076400007640000000464711323113030016202 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderFreeGlyphSet_H #define RenderFreeGlyphSet_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderFreeGlyphSet" #undef MESSAGE_STORE #define MESSAGE_STORE RenderFreeGlyphSetStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 8 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 0 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 0 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderFreeGlyphSet_H */ nxcomp/SetUnpackAlphaCompat.h0000644000076400007640000001102711323113027016504 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef SetUnpackAlphaCompat_H #define SetUnpackAlphaCompat_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define SETUNPACKALPHA_ENABLE_CACHE 1 #define SETUNPACKALPHA_ENABLE_DATA 1 #define SETUNPACKALPHA_ENABLE_SPLIT 0 #define SETUNPACKALPHA_ENABLE_COMPRESS 1 #define SETUNPACKALPHA_DATA_LIMIT 16384 #define SETUNPACKALPHA_DATA_OFFSET 8 #define SETUNPACKALPHA_CACHE_SLOTS 2000 #define SETUNPACKALPHA_CACHE_THRESHOLD 10 #define SETUNPACKALPHA_CACHE_LOWER_THRESHOLD 5 // // The message class. // class SetUnpackAlphaCompatMessage : public Message { friend class SetUnpackAlphaCompatStore; public: SetUnpackAlphaCompatMessage() { } ~SetUnpackAlphaCompatMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char client; unsigned int entries; }; class SetUnpackAlphaCompatStore : public MessageStore { public: SetUnpackAlphaCompatStore(StaticCompressor *compressor); virtual ~SetUnpackAlphaCompatStore(); virtual const char *name() const { return "SetUnpackAlphaCompat"; } virtual unsigned char opcode() const { return X_NXSetUnpackAlpha; } virtual unsigned int storage() const { return sizeof(SetUnpackAlphaCompatMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new SetUnpackAlphaCompatMessage(); } virtual Message *create(const Message &message) const { return new SetUnpackAlphaCompatMessage((const SetUnpackAlphaCompatMessage &) message); } virtual void destroy(Message *message) const { delete (SetUnpackAlphaCompatMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* SetUnpackAlphaCompat_H */ nxcomp/SetClipRectangles.h0000644000076400007640000001161411323113027016052 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef SetClipRectangles_H #define SetClipRectangles_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define SETCLIPRECTANGLES_ENABLE_CACHE 1 #define SETCLIPRECTANGLES_ENABLE_DATA 0 #define SETCLIPRECTANGLES_ENABLE_SPLIT 0 #define SETCLIPRECTANGLES_ENABLE_COMPRESS 0 #define SETCLIPRECTANGLES_DATA_LIMIT 2048 #define SETCLIPRECTANGLES_DATA_OFFSET 12 #define SETCLIPRECTANGLES_CACHE_SLOTS 3000 #define SETCLIPRECTANGLES_CACHE_THRESHOLD 3 #define SETCLIPRECTANGLES_CACHE_LOWER_THRESHOLD 1 // // The message class. // class SetClipRectanglesMessage : public Message { friend class SetClipRectanglesStore; public: SetClipRectanglesMessage() { } ~SetClipRectanglesMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char ordering; unsigned int gcontext; unsigned short x_origin; unsigned short y_origin; }; class SetClipRectanglesStore : public MessageStore { // // Constructors and destructors. // public: SetClipRectanglesStore() : MessageStore() { enableCache = SETCLIPRECTANGLES_ENABLE_CACHE; enableData = SETCLIPRECTANGLES_ENABLE_DATA; enableSplit = SETCLIPRECTANGLES_ENABLE_SPLIT; enableCompress = SETCLIPRECTANGLES_ENABLE_COMPRESS; dataLimit = SETCLIPRECTANGLES_DATA_LIMIT; dataOffset = SETCLIPRECTANGLES_DATA_OFFSET; cacheSlots = SETCLIPRECTANGLES_CACHE_SLOTS; cacheThreshold = SETCLIPRECTANGLES_CACHE_THRESHOLD; cacheLowerThreshold = SETCLIPRECTANGLES_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~SetClipRectanglesStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "SetClipRectangles"; } virtual unsigned char opcode() const { return X_SetClipRectangles; } virtual unsigned int storage() const { return sizeof(SetClipRectanglesMessage); } // // Message handling methods. // public: virtual Message *create() const { return new SetClipRectanglesMessage(); } virtual Message *create(const Message &message) const { return new SetClipRectanglesMessage((const SetClipRectanglesMessage &) message); } virtual void destroy(Message *message) const { delete (SetClipRectanglesMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* SetClipRectangles_H */ nxcomp/PolyText16.h0000644000076400007640000001114511323113030014427 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PolyText16_H #define PolyText16_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define POLYTEXT16_ENABLE_CACHE 1 #define POLYTEXT16_ENABLE_DATA 0 #define POLYTEXT16_ENABLE_SPLIT 0 #define POLYTEXT16_ENABLE_COMPRESS 0 #define POLYTEXT16_DATA_LIMIT 420 #define POLYTEXT16_DATA_OFFSET 16 #define POLYTEXT16_CACHE_SLOTS 3000 #define POLYTEXT16_CACHE_THRESHOLD 4 #define POLYTEXT16_CACHE_LOWER_THRESHOLD 1 // // The message class. // class PolyText16Message : public Message { friend class PolyText16Store; public: PolyText16Message() { } ~PolyText16Message() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int drawable; unsigned int gcontext; unsigned short x; unsigned short y; }; class PolyText16Store : public MessageStore { // // Constructors and destructors. // public: PolyText16Store() : MessageStore() { enableCache = POLYTEXT16_ENABLE_CACHE; enableData = POLYTEXT16_ENABLE_DATA; enableSplit = POLYTEXT16_ENABLE_SPLIT; enableCompress = POLYTEXT16_ENABLE_COMPRESS; dataLimit = POLYTEXT16_DATA_LIMIT; dataOffset = POLYTEXT16_DATA_OFFSET; cacheSlots = POLYTEXT16_CACHE_SLOTS; cacheThreshold = POLYTEXT16_CACHE_THRESHOLD; cacheLowerThreshold = POLYTEXT16_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~PolyText16Store() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "PolyText16"; } virtual unsigned char opcode() const { return X_PolyText16; } virtual unsigned int storage() const { return sizeof(PolyText16Message); } // // Message handling methods. // public: virtual Message *create() const { return new PolyText16Message(); } virtual Message *create(const Message &message) const { return new PolyText16Message((const PolyText16Message &) message); } virtual void destroy(Message *message) const { delete (PolyText16Message *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* PolyText16_H */ nxcomp/RenderCreatePictureCompat.h0000644000076400007640000000471311323113030017536 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderCreatePictureCompat_H #define RenderCreatePictureCompat_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderCreatePictureCompat" #undef MESSAGE_STORE #define MESSAGE_STORE RenderCreatePictureCompatStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 20 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderCreatePictureCompat_H */ nxcomp/BlockCache.h0000644000076400007640000000371111323113031014447 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef BlockCache_H #define BlockCache_H // Cache to hold an arbitrary-length block of bytes class BlockCache { public: BlockCache():buffer_(0), size_(0), checksum_(0) { } ~BlockCache() { delete[]buffer_; } int compare(unsigned int size, const unsigned char *data, int overwrite = 1); void set(unsigned int size, const unsigned char *data); unsigned int getLength() const { return size_; } unsigned int getChecksum() const { return checksum_; } const unsigned char *getData() const { return buffer_; } static unsigned int checksum(unsigned int size, const unsigned char *data); private: unsigned char *buffer_; unsigned int size_; unsigned int checksum_; }; #endif /* BlockCache_H */ nxcomp/RenderMinorExtensionMethods.h0000644000076400007640000000710211323113027020141 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // This file is included multiple times, // one for each message inheriting the // parent class. // public: #if MESSAGE_HAS_SIZE virtual void encodeSize(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual void decodeSize(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, unsigned char type, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; #endif #if MESSAGE_HAS_DATA virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, unsigned int size, int bigEndian, ChannelCache *channelCache) const; #endif virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, unsigned char type, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, md5_state_t *md5_state, int bigEndian) const; nxcomp/SetUnpackAlphaCompat.cpp0000644000076400007640000001725711323113030017044 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "SetUnpackAlphaCompat.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // SetUnpackAlphaCompatStore::SetUnpackAlphaCompatStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = SETUNPACKALPHA_ENABLE_CACHE; enableData = SETUNPACKALPHA_ENABLE_DATA; enableSplit = SETUNPACKALPHA_ENABLE_SPLIT; enableCompress = SETUNPACKALPHA_ENABLE_COMPRESS; dataLimit = SETUNPACKALPHA_DATA_LIMIT; dataOffset = SETUNPACKALPHA_DATA_OFFSET; cacheSlots = SETUNPACKALPHA_CACHE_SLOTS; cacheThreshold = SETUNPACKALPHA_CACHE_THRESHOLD; cacheLowerThreshold = SETUNPACKALPHA_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } SetUnpackAlphaCompatStore::~SetUnpackAlphaCompatStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int SetUnpackAlphaCompatStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif // Client. encodeBuffer.encodeCachedValue(*(buffer + 1), 8, clientCache -> resourceCache); // Entries. encodeBuffer.encodeValue(GetULONG(buffer + 4, bigEndian), 32, 9); #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int SetUnpackAlphaCompatStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif unsigned int value; unsigned char cValue; // Client. decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> resourceCache); // Entries. decodeBuffer.decodeValue(value, 32, 9); size = RoundUp4(value) + 8; buffer = writeBuffer -> addMessage(size); *(buffer + 1) = cValue; PutULONG(value, buffer + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int SetUnpackAlphaCompatStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message; setUnpackAlpha -> client = *(buffer + 1); setUnpackAlpha -> entries = GetULONG(buffer + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int SetUnpackAlphaCompatStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message; *(buffer + 1) = setUnpackAlpha -> client; PutULONG(setUnpackAlpha -> entries, buffer + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void SetUnpackAlphaCompatStore::dumpIdentity(const Message *message) const { #ifdef DUMP SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message; *logofs << name() << ": Identity client " << (unsigned int) setUnpackAlpha -> client << " entries " << setUnpackAlpha -> entries << " size " << setUnpackAlpha -> size_ << ".\n"; #endif } void SetUnpackAlphaCompatStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 4, 4); } void SetUnpackAlphaCompatStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message; SetUnpackAlphaCompatMessage *cachedSetUnpackAlpha = (SetUnpackAlphaCompatMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(setUnpackAlpha -> client, 8, clientCache -> resourceCache); cachedSetUnpackAlpha -> client = setUnpackAlpha -> client; if (cachedSetUnpackAlpha -> entries != setUnpackAlpha -> entries) { #ifdef TEST *logofs << name() << ": Encoding value " << setUnpackAlpha -> entries << " as entries field.\n" << logofs_flush; #endif encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeValue(setUnpackAlpha -> entries, 32, 9); cachedSetUnpackAlpha -> entries = setUnpackAlpha -> entries; } else { encodeBuffer.encodeBoolValue(0); } } void SetUnpackAlphaCompatStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeCachedValue(setUnpackAlpha -> client, 8, clientCache -> resourceCache); decodeBuffer.decodeBoolValue(value); if (value) { decodeBuffer.decodeValue(value, 32, 9); setUnpackAlpha -> entries = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << setUnpackAlpha -> entries << " as entries field.\n" << logofs_flush; #endif } } nxcomp/Agent.h0000644000076400007640000001327211323113030013531 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Agent_H #define Agent_H #include #include #include #include #include "Misc.h" #include "Transport.h" #include "Proxy.h" extern Proxy *proxy; #define PANIC #define WARNING #undef TEST #undef DEBUG class Agent { public: // // Must be created by passing the fake descriptor that // will be used for simulating socket communication // betwen the agent and the proxy. I/O will take place // by copying data to the agent's read and write buf- // fers. // Agent(int fd[2]); ~Agent(); AgentTransport *getTransport() const { return transport_; } void saveReadMask(fd_set *readSet) { saveRead_ = *readSet; } void saveWriteMask(fd_set *writeSet) { saveWrite_ = *writeSet; } void clearReadMask(fd_set *readSet) { FD_CLR(remoteFd_, readSet); FD_CLR(localFd_, readSet); } void clearWriteMask(fd_set *writeSet) { FD_CLR(remoteFd_, writeSet); FD_CLR(localFd_, writeSet); } void setLocalRead(fd_set *readSet, int *result) { (*result)++; FD_SET(localFd_, readSet); } void setRemoteRead(fd_set *readSet, int *result) { (*result)++; FD_SET(remoteFd_, readSet); } void setRemoteWrite(fd_set *writeSet, int *result) { (*result)++; FD_SET(remoteFd_, writeSet); } fd_set *getSavedReadMask() { return &saveRead_; } fd_set *getSavedWriteMask() { return &saveWrite_; } int getRemoteFd() const { return remoteFd_; } int getLocalFd() const { return localFd_; } int getProxyFd() const { return proxy -> getFd(); } int isValid() const { return (transport_ != NULL); } int localReadable() { return (transport_ -> readable() != 0); } // // Check if we can process more data from // the agent descriptor and cache the result // to avoid multiple calls. This must be // always called before querying the other // functions. // void saveChannelState() { canRead_ = (proxy != NULL ? proxy -> canRead(localFd_) : 0); } int remoteCanRead(const fd_set * const readSet) { #if defined(TEST) || defined(INFO) *logofs << "Agent: remoteCanRead() is " << (FD_ISSET(remoteFd_, readSet) && transport_ -> dequeuable() != 0) << " with FD_ISSET() " << (int) FD_ISSET(remoteFd_, readSet) << " and dequeuable " << transport_ -> dequeuable() << ".\n" << logofs_flush; #endif return (FD_ISSET(remoteFd_, readSet) && transport_ -> dequeuable() != 0); } int remoteCanWrite(const fd_set * const writeSet) { #if defined(TEST) || defined(INFO) *logofs << "Agent: remoteCanWrite() is " << (FD_ISSET(remoteFd_, writeSet) && transport_ -> queuable() != 0 && canRead_ == 1) << " with FD_ISSET() " << (int) FD_ISSET(remoteFd_, writeSet) << " queueable " << transport_ -> queuable() << " channel can read " << canRead_ << ".\n" << logofs_flush; #endif return (FD_ISSET(remoteFd_, writeSet) && transport_ -> queuable() != 0 && canRead_ == 1); } int localCanRead() { #if defined(TEST) || defined(INFO) *logofs << "Agent: localCanRead() is " << (transport_ -> readable() != 0 && canRead_ == 1) << " with readable " << transport_ -> readable() << " channel can read " << canRead_ << ".\n" << logofs_flush; #endif return (transport_ -> readable() != 0 && canRead_ == 1); } int proxyCanRead() { #if defined(TEST) || defined(INFO) *logofs << "Agent: proxyCanRead() is " << proxy -> canRead() << ".\n" << logofs_flush; #endif return (proxy -> canRead()); } int proxyCanRead(const fd_set * const readSet) { #if defined(TEST) || defined(INFO) *logofs << "Agent: proxyCanRead() is " << ((int) FD_ISSET(proxy -> getFd(), readSet) << ".\n" << logofs_flush; #endif return (FD_ISSET(proxy -> getFd(), readSet)); } int enqueueData(const char *data, const int size) const { return transport_ -> enqueue(data, size); } int dequeueData(char *data, int size) const { return transport_ -> dequeue(data, size); } int dequeuableData() const { return transport_ -> dequeuable(); } private: int remoteFd_; int localFd_; fd_set saveRead_; fd_set saveWrite_; int canRead_; AgentTransport *transport_; }; #endif /* Agent_H */ nxcomp/RenderAddGlyphs.h0000644000076400007640000000463111323113031015512 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderAddGlyphs_H #define RenderAddGlyphs_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderAddGlyphs" #undef MESSAGE_STORE #define MESSAGE_STORE RenderAddGlyphsStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 12 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderAddGlyphs_H */ nxcomp/ServerReadBuffer.cpp0000644000076400007640000001350211323113030016216 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ServerReadBuffer.h" #include "ServerChannel.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG unsigned int ServerReadBuffer::suggestedLength(unsigned int pendingLength) { // // Always read all the data that // is available. // int readable = transport_ -> readable(); unsigned int readLength = (readable == -1 ? 0 : (unsigned int) readable); if (readLength < pendingLength) { readLength = pendingLength; } // // Even if the readable data is not // enough to make a complete message, // resize the buffer to accomodate // it all. // if (pendingLength < remaining_) { readLength = remaining_; } return readLength; } int ServerReadBuffer::locateMessage(const unsigned char *start, const unsigned char *end, unsigned int &controlLength, unsigned int &dataLength, unsigned int &trailerLength) { unsigned int size = end - start; #ifdef TEST *logofs << "ServerReadBuffer: Locating message for FD#" << transport_ -> fd() << " with " << size << " bytes.\n" << logofs_flush; #endif if (firstMessage_) { if (size < 8) { remaining_ = 8 - size; #ifdef TEST *logofs << "ServerReadBuffer: No message was located " << "with remaining " << remaining_ << ".\n" << logofs_flush; #endif return 0; } dataLength = 8 + (GetUINT(start + 6, bigEndian_) << 2); } else { if (size < 32) { remaining_ = 32 - size; #ifdef TEST *logofs << "ServerReadBuffer: No message was located " << "with remaining " << remaining_ << ".\n" << logofs_flush; #endif return 0; } if (*start == 1) { dataLength = 32 + (GetULONG(start + 4, bigEndian_) << 2); } else { dataLength = 32; } if (dataLength < 32) { #ifdef TEST *logofs << "ServerReadBuffer: WARNING! Assuming length 32 " << "for suspicious message of length " << dataLength << ".\n" << logofs_flush; #endif dataLength = 32; } } #ifdef TEST *logofs << "ServerReadBuffer: Length of the next message is " << dataLength << ".\n" << logofs_flush; #endif if (size < dataLength) { remaining_ = dataLength - size; #ifdef TEST *logofs << "ServerReadBuffer: No message was located " << "with remaining " << remaining_ << ".\n" << logofs_flush; #endif return 0; } firstMessage_ = 0; controlLength = 0; trailerLength = 0; remaining_ = 0; #ifdef TEST *logofs << "ServerReadBuffer: Located message with " << "remaining " << remaining_ << ".\n" << logofs_flush; #endif return 1; } // // Check if the data already read contains a // message matching the opcode and sequence, // starting at the given offset. // unsigned char *ServerReadBuffer::peekMessage(unsigned int &offset, unsigned char opcode, unsigned short sequence) { #ifdef TEST *logofs << "ServerReadBuffer: Peeking message " << "for FD#" << transport_ -> fd() << " with size " << length_ << " offset " << offset << " opcode " << (unsigned int) opcode << " and sequence " << sequence << ".\n" << logofs_flush; #endif if (firstMessage_) { return NULL; } unsigned char *next = buffer_ + start_ + offset; unsigned char *end = buffer_ + start_ + length_; int found = 0; while (end - next >= 32) { #ifdef DEBUG *logofs << "ServerReadBuffer: Checking opcode " << (unsigned int) *next << " sequence " << GetUINT(next + 2, bigEndian_) << " at " << next - buffer_ + start_ << ".\n" << logofs_flush; #endif if (*next == opcode && GetUINT(next + 2, bigEndian_) == sequence) { found = 1; break; } else if (*next == 1) { next += (32 + (GetULONG(next + 4, bigEndian_) << 2)); } else { next += 32; } } offset = next - buffer_ + start_; if (found == 1) { #ifdef TEST *logofs << "ServerReadBuffer: Found message at " << "offset " << next - buffer_ + start_ << ".\n" << logofs_flush; #endif return next; } #ifdef TEST *logofs << "ServerReadBuffer: Quitting loop at " << "offset " << next - buffer_ + start_ << ".\n" << logofs_flush; #endif return NULL; } nxcomp/LICENSE0000644000076400007640000000305511332041054013333 0ustar svetonisvetoniCopyright (c) 2001, 2011 NoMachine - http://www.nomachine.com/. NXCOMP library and NX extensions to X are copyright of NoMachine. Redistribution and use of this software is allowed according to the following terms: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 2, and not any other version, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTA- BILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, you can request a copy to NoMachine or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Parts of this software are derived from DXPC project. These copyright notices apply to original DXPC code: Redistribution and use in source and binary forms are permitted provi- ded that the above copyright notice and this paragraph are duplicated in all such forms. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. Copyright (c) 1995,1996 Brian Pane Copyright (c) 1996,1997 Zachary Vonler and Brian Pane Copyright (c) 1999 Kevin Vigor and Brian Pane Copyright (c) 2000,2006 Gian Filippo Pinzari and Brian Pane All rights reserved. nxcomp/GetImage.h0000644000076400007640000001124711323113027014163 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef GetImage_H #define GetImage_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define GETIMAGE_ENABLE_CACHE 1 #define GETIMAGE_ENABLE_DATA 0 #define GETIMAGE_ENABLE_SPLIT 0 #define GETIMAGE_ENABLE_COMPRESS 0 #define GETIMAGE_DATA_LIMIT 0 #define GETIMAGE_DATA_OFFSET 20 #define GETIMAGE_CACHE_SLOTS 200 #define GETIMAGE_CACHE_THRESHOLD 1 #define GETIMAGE_CACHE_LOWER_THRESHOLD 0 // // The message class. // class GetImageMessage : public Message { friend class GetImageStore; public: GetImageMessage() { } ~GetImageMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char format; unsigned int drawable; unsigned short int x; unsigned short int y; unsigned short int width; unsigned short int height; unsigned int plane_mask; }; class GetImageStore : public MessageStore { // // Constructors and destructors. // public: GetImageStore() : MessageStore() { enableCache = GETIMAGE_ENABLE_CACHE; enableData = GETIMAGE_ENABLE_DATA; enableSplit = GETIMAGE_ENABLE_SPLIT; enableCompress = GETIMAGE_ENABLE_COMPRESS; dataLimit = GETIMAGE_DATA_LIMIT; dataOffset = GETIMAGE_DATA_OFFSET; cacheSlots = GETIMAGE_CACHE_SLOTS; cacheThreshold = GETIMAGE_CACHE_THRESHOLD; cacheLowerThreshold = GETIMAGE_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~GetImageStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "GetImage"; } virtual unsigned char opcode() const { return X_GetImage; } virtual unsigned int storage() const { return sizeof(GetImageMessage); } // // Message handling methods. // public: virtual Message *create() const { return new GetImageMessage(); } virtual Message *create(const Message &message) const { return new GetImageMessage((const GetImageMessage &) message); } virtual void destroy(Message *message) const { delete (GetImageMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* GetImage_H */ nxcomp/Alpha.h0000644000076400007640000000277611342775506013556 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Alpha_H #define Alpha_H int UnpackAlpha(unsigned char method, unsigned char *src_data, int src_size, unsigned char *dst_data, int dst_size); int UnpackAlpha(T_alpha *alpha, unsigned char *dst_data, int dst_size, int big_endian); #endif /* Aplha_H */ nxcomp/FillPoly.cpp0000644000076400007640000001601111323113027014560 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "FillPoly.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int FillPolyStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { FillPolyMessage *fillPoly = (FillPolyMessage *) message; // // Here is the fingerprint. // fillPoly -> drawable = GetULONG(buffer + 4, bigEndian); fillPoly -> gcontext = GetULONG(buffer + 8, bigEndian); fillPoly -> shape = *(buffer + 12); fillPoly -> mode = *(buffer + 13); if (control -> isProtoStep8() == 1 && size >= (unsigned int) dataOffset) { fillPoly -> x_origin = GetUINT(buffer + 16, bigEndian); fillPoly -> y_origin = GetUINT(buffer + 18, bigEndian); } else { fillPoly -> x_origin = 0; fillPoly -> y_origin = 0; } #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int FillPolyStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { FillPolyMessage *fillPoly = (FillPolyMessage *) message; // // Fill all the message's fields. // PutULONG(fillPoly -> drawable, buffer + 4, bigEndian); PutULONG(fillPoly -> gcontext, buffer + 8, bigEndian); *(buffer + 12) = fillPoly -> shape; *(buffer + 13) = fillPoly -> mode; if (control -> isProtoStep8() == 1 && size >= (unsigned int) dataOffset) { PutUINT(fillPoly -> x_origin, buffer + 16, bigEndian); PutUINT(fillPoly -> y_origin, buffer + 18, bigEndian); } #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void FillPolyStore::dumpIdentity(const Message *message) const { #ifdef DUMP FillPolyMessage *fillPoly = (FillPolyMessage *) message; *logofs << name() << ": Identity drawable " << fillPoly -> drawable << ", gcontext " << fillPoly -> gcontext << ", shape " << fillPoly -> shape << ", mode " << fillPoly -> mode << fillPoly -> size_ << ", x_origin " << fillPoly -> x_origin << ", y_origin " << fillPoly -> y_origin << ".\n" << logofs_flush; #endif } void FillPolyStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // Fields shape, mode. // md5_append(md5_state_, buffer + 12, 2); } void FillPolyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { FillPolyMessage *fillPoly = (FillPolyMessage *) message; FillPolyMessage *cachedFillPoly = (FillPolyMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << fillPoly -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(fillPoly -> drawable, clientCache -> drawableCache); cachedFillPoly -> drawable = fillPoly -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << fillPoly -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(fillPoly -> gcontext, clientCache -> gcCache); cachedFillPoly -> gcontext = fillPoly -> gcontext; if (control -> isProtoStep8() == 1 && fillPoly -> size_ >= dataOffset) { #ifdef TEST *logofs << name() << ": Encoding value " << fillPoly -> x_origin << " as x_origin field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(fillPoly -> x_origin, 16, *clientCache -> fillPolyXAbsCache[0], 8); cachedFillPoly -> x_origin = fillPoly -> x_origin; #ifdef TEST *logofs << name() << ": Encoding value " << fillPoly -> y_origin << " as y_origin field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(fillPoly -> y_origin, 16, *clientCache -> fillPolyYAbsCache[0], 8); cachedFillPoly -> y_origin = fillPoly -> y_origin; } } void FillPolyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { FillPolyMessage *fillPoly = (FillPolyMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(fillPoly -> drawable, clientCache -> drawableCache); #ifdef TEST *logofs << name() << ": Decoded value " << fillPoly -> drawable << " as drawable field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(fillPoly -> gcontext, clientCache -> gcCache); #ifdef TEST *logofs << name() << ": Decoded value " << fillPoly -> gcontext << " as gcontext field.\n" << logofs_flush; #endif if (control -> isProtoStep8() == 1 && fillPoly -> size_ >= dataOffset) { unsigned int value; decodeBuffer.decodeCachedValue(value, 16, *clientCache -> fillPolyXAbsCache[0], 8); fillPoly -> x_origin = value; #ifdef TEST *logofs << name() << ": Decoded value " << fillPoly -> x_origin << " as x_origin field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, *clientCache -> fillPolyYAbsCache[0], 8); fillPoly -> y_origin = value; #ifdef TEST *logofs << name() << ": Decoded value " << fillPoly -> y_origin << " as y_origin field.\n" << logofs_flush; #endif } } nxcomp/Colormap.h0000644000076400007640000000264011342775642014274 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Colormap_H #define Colormap_H int UnpackColormap(unsigned char method, unsigned char *src_data, int src_size, unsigned char *dst_data, int dst_size); #endif /* Colormap_H */ nxcomp/Message.cpp0000644000076400007640000016077211323113031014423 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include "Misc.h" // // We need channel's cache data. // #include "Message.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. You also // need to define DUMP in Misc.cpp // if DUMP is defined here. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Define this to log when messages // are allocated and deallocated. // #undef REFERENCES // // Keep track of how many bytes are // occupied by cache. // int MessageStore::totalLocalStorageSize_ = 0; int MessageStore::totalRemoteStorageSize_ = 0; // // These are used for reference count. // #ifdef REFERENCES int Message::references_ = 0; int MessageStore::references_ = 0; #endif // // Here are the methods to handle cached messages. // MessageStore::MessageStore(StaticCompressor *compressor) : compressor_(compressor) { // // Public members. // enableCache = MESSAGE_ENABLE_CACHE; enableData = MESSAGE_ENABLE_DATA; enableSplit = MESSAGE_ENABLE_SPLIT; enableCompress = MESSAGE_ENABLE_COMPRESS; dataLimit = MESSAGE_DATA_LIMIT; dataOffset = MESSAGE_DATA_OFFSET; cacheSlots = MESSAGE_CACHE_SLOTS; cacheThreshold = MESSAGE_CACHE_THRESHOLD; cacheLowerThreshold = MESSAGE_CACHE_LOWER_THRESHOLD; #ifdef TEST *logofs << "MessageStore: Static compressor is at " << compressor_ << ".\n" << logofs_flush; #endif md5_state_ = new md5_state_t(); #ifdef DEBUG *logofs << "MessageStore: Created MD5 state for object at " << this << ".\n" << logofs_flush; #endif lastAdded = cacheSlots; lastHit = 0; lastRemoved = 0; lastRated = nothing; lastAction = is_discarded; // // This is used only for compatibility // with older proxies. // if (control -> isProtoStep7() == 1) { lastResize = -1; } else { lastResize = 0; } // // Private members. // localStorageSize_ = 0; remoteStorageSize_ = 0; #ifdef TEST *logofs << "MessageStore: Size of total cache is " << totalLocalStorageSize_ << " bytes at local side and " << totalRemoteStorageSize_ << " bytes at remote side.\n" << logofs_flush; #endif messages_ = new T_messages(); checksums_ = new T_checksums(); temporary_ = NULL; #ifdef REFERENCES references_++; *logofs << "MessageStore: Created new store at " << this << "out of " << references_ << " allocated stores.\n" << logofs_flush; #endif } MessageStore::~MessageStore() { // // The virtual destructor of specialized class // must get rid of both messages in container // and temporary. // #ifdef DEBUG *logofs << "MessageStore: Deleting MD5 state for object at " << this << ".\n" << logofs_flush; #endif delete md5_state_; delete messages_; delete checksums_; // // Update the static members tracking // size of total memory allocated for // all stores. // totalLocalStorageSize_ -= localStorageSize_; totalRemoteStorageSize_ -= remoteStorageSize_; #ifdef TEST *logofs << "MessageStore: Size of total cache is " << totalLocalStorageSize_ << " bytes at local side and " << totalRemoteStorageSize_ << " bytes at remote side.\n" << logofs_flush; #endif #ifdef REFERENCES references_--; *logofs << "MessageStore: Deleted store at " << this << " out of " << references_ << " allocated stores.\n" << logofs_flush; #endif } // // Here are the methods to parse and cache // messages in the message stores. // int MessageStore::parse(Message *message, int split, const unsigned char *buffer, unsigned int size, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian) { // // Save the message size as received on the link. // This information will be used to create an ap- // propriate buffer at the time the message will // be unparsed. // message -> size_ = size; message -> i_size_ = identitySize(buffer, size); message -> c_size_ = 0; validateSize(size); if (checksumAction == use_checksum) { beginChecksum(message); parseIdentity(message, buffer, size, bigEndian); identityChecksum(message, buffer, size, bigEndian); parseData(message, split, buffer, size, checksumAction, dataAction, bigEndian); endChecksum(message); } else { parseIdentity(message, buffer, size, bigEndian); parseData(message, split, buffer, size, checksumAction, dataAction, bigEndian); } return 1; } int MessageStore::parse(Message *message, const unsigned char *buffer, unsigned int size, const unsigned char *compressedData, const unsigned int compressedDataSize, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian) { int offset = identitySize(buffer, size); message -> size_ = size; message -> i_size_ = offset; message -> c_size_ = compressedDataSize + offset; validateSize(message -> size_ - offset, compressedDataSize); if (checksumAction == use_checksum) { beginChecksum(message); parseIdentity(message, buffer, size, bigEndian); identityChecksum(message, buffer, size, bigEndian); parseData(message, buffer, size, compressedData, compressedDataSize, checksumAction, dataAction, bigEndian); endChecksum(message); } else { parseIdentity(message, buffer, size, bigEndian); parseData(message, buffer, size, compressedData, compressedDataSize, checksumAction, dataAction, bigEndian); } return 1; } int MessageStore::parseData(Message *message, int split, const unsigned char *buffer, unsigned int size, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian) { if ((int) size > message -> i_size_) { unsigned int dataSize = size - message -> i_size_; if (checksumAction == use_checksum) { #ifdef DEBUG *logofs << name() << ": Calculating checksum of object at " << message << " with data size " << dataSize << ".\n" << logofs_flush; #endif dataChecksum(message, buffer, size, bigEndian); } if (dataAction == discard_data) { #ifdef DEBUG *logofs << name() << ": Discarded " << dataSize << " bytes of plain data. Real size is " << message -> size_ << " compressed size is " << message -> c_size_ << ".\n" << logofs_flush; #endif return 1; } // // Accept anyway data beyond the // expected limit. // #ifdef TEST if (dataSize > (unsigned int) dataLimit) { *logofs << name() << ": WARNING! Data is " << dataSize << " bytes. Ignoring the established limit.\n" << logofs_flush; } #endif if (dataSize != message -> data_.size()) { #ifdef DEBUG *logofs << name() << ": Data will be resized from " << message -> data_.size() << " to hold a plain buffer of " << dataSize << " bytes.\n" << logofs_flush; #endif message -> data_.clear(); message -> data_.resize(dataSize); } if (split == 0) { memcpy(message -> data_.begin(), buffer + message -> i_size_, dataSize); } #ifdef TEST else { *logofs << name() << ": Not copied " << dataSize << " bytes of fake data for the split message.\n" << logofs_flush; } #endif #ifdef DEBUG *logofs << name() << ": Parsed " << dataSize << " bytes of plain data. Real size is " << message -> size_ << " compressed size is " << message -> c_size_ << ".\n" << logofs_flush; #endif } return 1; } // // Store the data part in compressed format. // int MessageStore::parseData(Message *message, const unsigned char *buffer, unsigned int size, const unsigned char *compressedData, const unsigned int compressedDataSize, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian) { if ((int) size > message -> i_size_) { unsigned int dataSize = size - message -> i_size_; if (checksumAction == use_checksum) { #ifdef DEBUG *logofs << name() << ": Calculating checksum of object at " << message << " with data size " << dataSize << ".\n" << logofs_flush; #endif dataChecksum(message, buffer, size, bigEndian); } if (dataAction == discard_data) { #ifdef DEBUG *logofs << name() << ": Discarded " << dataSize << " bytes of compressed data. Real size is " << message -> size_ << " compressed size is " << message -> c_size_ << ".\n" << logofs_flush; #endif return 1; } #ifdef WARNING if (dataSize > (unsigned int) dataLimit) { *logofs << name() << ": WARNING! Data is " << dataSize << " bytes. Ignoring the established limit!\n" << logofs_flush; } #endif dataSize = compressedDataSize; if (dataSize != message -> data_.size()) { #ifdef DEBUG *logofs << name() << ": Data will be resized from " << message -> data_.size() << " to hold a compressed buffer of " << dataSize << " bytes.\n" << logofs_flush; #endif message -> data_.clear(); message -> data_.resize(compressedDataSize); } memcpy(message -> data_.begin(), compressedData, compressedDataSize); #ifdef DEBUG *logofs << name() << ": Parsed " << dataSize << " bytes of compressed data. Real size is " << message -> size_ << " compressed size is " << message -> c_size_ << ".\n" << logofs_flush; #endif } return 1; } int MessageStore::unparseData(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) { // // Copy data, if any, to the buffer. // if ((int) size > message -> i_size_) { // // Check if message has been stored // in compressed format. // if (message -> c_size_ == 0) { memcpy(buffer + message -> i_size_, message -> data_.begin(), size - message -> i_size_); #ifdef DEBUG *logofs << name() << ": Unparsed " << message -> size_ - message -> i_size_ << " bytes of data to a buffer of " << message -> size_ - message -> i_size_ << ".\n" << logofs_flush; #endif } else { #ifdef DEBUG *logofs << name() << ": Using static compressor at " << (void *) compressor_ << ".\n" << logofs_flush; #endif if (compressor_ -> decompressBuffer(buffer + message -> i_size_, size - message -> i_size_, message -> data_.begin(), message -> c_size_ - message -> i_size_) < 0) { #ifdef PANIC *logofs << name() << ": PANIC! Data decompression failed.\n" << logofs_flush; #endif cerr << "Error" << ": Data decompression failed.\n"; return -1; } #ifdef DEBUG *logofs << name() << ": Unparsed " << message -> c_size_ - message -> i_size_ << " bytes of compressed data to a buffer of " << message -> size_ - message -> i_size_ << ".\n" << logofs_flush; #endif } } // // We could write size to the buffer but this // is something the channel class is doing by // itself. // // PutUINT(size >> 2, buffer + 2, bigEndian); // return 1; } void MessageStore::dumpData(const Message *message) const { #ifdef DUMP *logofs << name() << ": Dumping enumerated data:\n" << logofs_flush; DumpData(message -> data_.begin(), message -> data_.size()); #endif #ifdef DUMP *logofs << name() << ": Dumping checksum data:\n" << logofs_flush; DumpData(message -> md5_digest_, MD5_LENGTH); #endif } T_checksum MessageStore::getChecksum(const unsigned char *buffer, unsigned int size, int bigEndian) { Message *message = getTemporary(); message -> size_ = size; message -> i_size_ = identitySize(buffer, size); message -> c_size_ = 0; validateSize(size); beginChecksum(message); // // We don't need to extract the identity // data from the buffer. // // parseIdentity(message, buffer, size, bigEndian); // identityChecksum(message, buffer, size, bigEndian); parseData(message, 0, buffer, size, use_checksum, discard_data, bigEndian); endChecksum(message); // // The caller will have to explicitly // deallocated the memory after use. // T_checksum checksum = new md5_byte_t[MD5_LENGTH]; memcpy(checksum, message -> md5_digest_, MD5_LENGTH); return checksum; } int MessageStore::clean(T_checksum_action checksumAction) { int position = lastRemoved + 1; if (position >= cacheSlots) { position = 0; } #ifdef DEBUG *logofs << name() << ": Searching a message to remove " << "starting at position " << position << " with " << checksums_ -> size() << " elements in cache.\n" << logofs_flush; #endif while (position != lastRemoved) { #ifdef DEBUG *logofs << name() << ": Examining position " << position << ".\n" << logofs_flush; #endif if ((*messages_)[position] != NULL) { if (getRating((*messages_)[position], rating_for_clean) == 0) { break; } else { untouch((*messages_)[position]); } } if (++position == cacheSlots) { #ifdef DEBUG *logofs << name() << ": Rolled position at " << strMsTimestamp() << ".\n" << logofs_flush; #endif position = 0; } } // // If no message is a good candidate, // then try the object at the next slot // in respect to last element removed. // if (position == lastRemoved) { position = lastRemoved + 1; if (position >= cacheSlots) { position = 0; } if ((*messages_)[position] == NULL || (*messages_)[position] -> locks_ != 0) { #ifdef DEBUG *logofs << name() << ": WARNING! No message found " << "to be actually removed.\n" << logofs_flush; #endif return nothing; } #ifdef DEBUG *logofs << name() << ": WARNING! Assuming object " << "at position " << position << ".\n" << logofs_flush; #endif } return position; } // // This is the insertion method used at local side // side. Cache at remote side side will be kept in // sync by telling the to other party where to // store the message. // int MessageStore::findOrAdd(Message *message, T_checksum_action checksumAction, T_data_action dataAction, int &added, int &locked) { if (checksumAction != use_checksum) { #ifdef PANIC *logofs << name() << ": PANIC! Internal error in context [A]. " << "Cannot find or add message to repository " << "without using checksum.\n" << logofs_flush; #endif cerr << "Error" << ": Internal error in context [A]. " << "Cannot find or add message to repository " << "without using checksum.\n"; HandleAbort(); } // // Set added to true only if message // is inserted in cache. // added = 0; locked = 0; // // First of all figure out where to // store this object. // #ifdef DEBUG *logofs << name() << ": Searching an empty slot " << "with last rated " << lastRated << " and " << "last added " << lastAdded << ".\n" << logofs_flush; #endif int position = lastRated; if (position == nothing) { position = lastAdded + 1; if (position >= cacheSlots) { position = 0; } #ifdef DEBUG *logofs << name() << ": Searching an empty slot " << "starting at position " << position << " with " << checksums_ -> size() << " elements in cache.\n" << logofs_flush; #endif while (position != lastAdded) { #ifdef DEBUG *logofs << name() << ": Examining position " << position << ".\n" << logofs_flush; #endif if ((*messages_)[position] == NULL) { break; } else if (getRating((*messages_)[position], rating_for_insert) == 0) { break; } else { untouch((*messages_)[position]); } if (++position == cacheSlots) { #ifdef DEBUG *logofs << name() << ": Rolled position at " << strMsTimestamp() << ".\n" << logofs_flush; #endif position = 0; } } } #ifdef DEBUG else { *logofs << name() << ": Using last rated position " << position << ".\n" << logofs_flush; } #endif // // If we made an extensive check but did not // find neither a free slot or a message to // replace, assume slot at next position in // respect to last added. This can happen if // all objects in repository have got an hit // recently. // if (position == lastAdded) { position = lastAdded + 1; if (position >= cacheSlots) { position = 0; } #ifdef DEBUG *logofs << name() << ": WARNING! Assuming slot " << "at position " << position << ".\n" << logofs_flush; #endif } #ifdef DEBUG else { *logofs << name() << ": Found candidate slot " << "at position " << position << ".\n" << logofs_flush; } #endif // // Save the search result so if the message // is found in cache, we can use the slot // at next run. // lastRated = position; if ((*messages_)[position] != NULL && (*messages_)[position] -> locks_ != 0) { #ifdef WARNING *logofs << name() << ": WARNING! Insertion at position " << position << " would replace a locked message. " << "Forcing channel to discard the message.\n" << logofs_flush; #endif #ifdef TEST *logofs << name() << ": Invalidating rating of object " << "at position " << position << ".\n" << logofs_flush; #endif return (lastRated = nothing); } if (checksumAction == use_checksum) { T_checksum checksum = getChecksum(message); #ifdef TEST *logofs << name() << ": Searching checksum [" << DumpChecksum(checksum) << "] in repository.\n" << logofs_flush; #endif pair result; result = checksums_ -> insert(T_checksums::value_type(checksum, position)); // // Message was found in cache or // insertion couldn't take place. // if (result.second == 0) { if (result.first == checksums_ -> end()) { #ifdef PANIC *logofs << name() << ": PANIC! Failed to insert object " << "in the cache.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to insert object of type " << name() << " in the cache.\n"; return nothing; } // // Message is in cache. // #ifdef TEST *logofs << name() << ": Object is already in cache " << "at position " << (result.first) -> second << ".\n" << logofs_flush; #endif #ifdef DEBUG printStorageSize(); #endif // // Message is locked, probably because // it has not completely recomposed at // remote side after a split. // if ((*messages_)[(result.first) -> second] -> locks_ != 0) { #ifdef TEST *logofs << name() << ": WARNING! Object at position " << (result.first) -> second << " is locked.\n" << logofs_flush; #endif locked = 1; } // // Object got a hit, so prevent // its removal. // if (lastRated == (result.first) -> second) { #ifdef TEST *logofs << name() << ": Resetting rating of object " << "at position " << (result.first) -> second << ".\n" << logofs_flush; #endif lastRated = nothing; } return (result.first) -> second; } #ifdef DEBUG *logofs << name() << ": Could not find message in cache.\n" << logofs_flush; #endif } // // Message not found in hash table (or insertion // of checksum in hash table was not requested). // Message was added to cache. // added = 1; // // Log data about the missed message. // #ifdef TEST if (opcode() == X_PutImage || opcode() == X_NXPutPackedImage) { #ifdef WARNING *logofs << name() << ": WARNING! Dumping identity of " << "missed image object of type " << name() << ".\n" << logofs_flush; #endif dumpIdentity(message); } #endif if ((*messages_)[position] != NULL) { #ifdef DEBUG *logofs << name() << ": The message replaces " << "the old one at position " << position << ".\n" << logofs_flush; #endif remove(position, checksumAction, dataAction); } (*messages_)[position] = message; // // We used the slot. Perform a new // search at next run. // lastRated = nothing; #ifdef TEST *logofs << name() << ": Stored message object of size " << plainSize(position) << " (" << message -> size_ << "/" << message -> c_size_ << ") at position " << position << ".\n" << logofs_flush; #endif unsigned int localSize; unsigned int remoteSize; storageSize(message, localSize, remoteSize); localStorageSize_ += localSize; remoteStorageSize_ += remoteSize; totalLocalStorageSize_ += localSize; totalRemoteStorageSize_ += remoteSize; #ifdef DEBUG printStorageSize(); #endif // // Set hits and timestamp at insertion in cache. // message -> hits_ = control -> StoreHitsAddBonus; message -> last_ = (getTimestamp()).tv_sec; message -> locks_ = 0; #ifdef DEBUG *logofs << name() << ": Set last hit of object at " << strMsTimestamp() << " with a bonus of " << message -> hits_ << ".\n" << logofs_flush; #endif return position; } // // Add a parsed message to repository. It is normally used // at decoding side or at encoding side when we load store // from disk. To handle messages coming from network, the // encoding side uses the optimized method findOrAdd(). // int MessageStore::add(Message *message, const int position, T_checksum_action checksumAction, T_data_action dataAction) { if (position < 0 || position >= cacheSlots) { #ifdef PANIC *logofs << name() << ": PANIC! Cannot add a message " << "at non existing position " << position << ".\n" << logofs_flush; #endif cerr << "Error" << ": Cannot add a message " << "at non existing position " << position << ".\n"; HandleAbort(); } if ((*messages_)[position] != NULL) { #ifdef DEBUG *logofs << name() << ": The message will replace " << "the old one at position " << position << ".\n" << logofs_flush; #endif remove(position, checksumAction, dataAction); } #ifdef DEBUG *logofs << name() << ": Inserting object in repository at position " << position << ".\n" << logofs_flush; #endif (*messages_)[position] = message; // // Get the object's checksum value // and insert it in the table. // if (checksumAction == use_checksum) { #ifdef DEBUG *logofs << name() << ": Inserting object's checksum in repository.\n"; #endif T_checksum checksum = getChecksum(message); checksums_ -> insert(T_checksums::value_type(checksum, position)); } #ifdef DEBUG *logofs << name() << ": Stored message object of size " << plainSize(position) << " (" << message -> size_ << "/" << message -> c_size_ << ") at position " << position << ".\n" << logofs_flush; #endif unsigned int localSize; unsigned int remoteSize; storageSize(message, localSize, remoteSize); localStorageSize_ += localSize; remoteStorageSize_ += remoteSize; totalLocalStorageSize_ += localSize; totalRemoteStorageSize_ += remoteSize; #ifdef DEBUG printStorageSize(); #endif // // Set hits and timestamp at insertion in cache. // message -> hits_ = control -> StoreHitsAddBonus; message -> last_ = (getTimestamp()).tv_sec; message -> locks_ = 0; #ifdef DEBUG *logofs << name() << ": Set last hit of object at " << strMsTimestamp() << " with a bonus of " << message -> hits_ << ".\n" << logofs_flush; #endif return position; } // // The following functions don't modify data, // so they are supposed to be called only at // the encoding side. // void MessageStore::updateData(const int position, unsigned int dataSize, unsigned int compressedDataSize) { Message *message = (*messages_)[position]; validateSize(dataSize, compressedDataSize); if (compressedDataSize != 0) { unsigned int localSize; unsigned int remoteSize; storageSize(message, localSize, remoteSize); localStorageSize_ -= localSize; remoteStorageSize_ -= remoteSize; totalLocalStorageSize_ -= localSize; totalRemoteStorageSize_ -= remoteSize; message -> c_size_ = compressedDataSize + message -> i_size_; #ifdef TEST if (message -> size_ != (int) (dataSize + message -> i_size_)) { #ifdef PANIC *logofs << name() << ": PANIC! Size of object looks " << message -> size_ << " bytes while it " << "should be " << dataSize + message -> i_size_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Size of object looks " << message -> size_ << " bytes while it " << "should be " << dataSize + message -> i_size_ << ".\n"; HandleAbort(); } #endif storageSize(message, localSize, remoteSize); localStorageSize_ += localSize; remoteStorageSize_ += remoteSize; totalLocalStorageSize_ += localSize; totalRemoteStorageSize_ += remoteSize; #ifdef DEBUG printStorageSize(); #endif } } void MessageStore::updateData(const T_checksum checksum, unsigned int compressedDataSize) { #ifdef TEST *logofs << name() << ": Searching checksum [" << DumpChecksum(checksum) << "] in repository.\n" << logofs_flush; #endif T_checksums::iterator found = checksums_ -> find(checksum); if (found != checksums_ -> end()) { Message *message = (*messages_)[found -> second]; #ifdef TEST *logofs << name() << ": Message found in cache at " << "position " << found -> second << " with size " << message -> size_ << " and compressed size " << message -> c_size_ << ".\n" << logofs_flush; #endif updateData(found -> second, message -> size_ - message -> i_size_, compressedDataSize); } #ifdef TEST else if (checksums_ -> size() > 0) { *logofs << name() << ": WARNING! Can't locate the " << "checksum [" << DumpChecksum(checksum) << "] for the update.\n" << logofs_flush; } #endif } // // This function replaces the data part of the message // and updates the information about its size. Split // messages are advertised to the decoding side with // their uncompressed size, data is then compressed // before sending the first chunk. This function is // called by the decoding side after the split message // is fully recomposed to replace the dummy data and // set the real size. // void MessageStore::updateData(const int position, const unsigned char *newData, unsigned int dataSize, unsigned int compressedDataSize) { Message *message = (*messages_)[position]; validateSize(dataSize, compressedDataSize); #ifdef TEST if (message -> size_ != (int) (dataSize + message -> i_size_)) { #ifdef PANIC *logofs << name() << ": PANIC! Data of object looks " << dataSize << " bytes while it " << "should be " << message -> size_ - message -> i_size_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Data of object looks " << dataSize << " bytes while it " << "should be " << message -> size_ - message -> i_size_ << ".\n"; HandleAbort(); } #endif // // A compressed data size of 0 means that // message's data was not compressed. // if (compressedDataSize != 0) { unsigned int localSize; unsigned int remoteSize; storageSize(message, localSize, remoteSize); localStorageSize_ -= localSize; remoteStorageSize_ -= remoteSize; totalLocalStorageSize_ -= localSize; totalRemoteStorageSize_ -= remoteSize; if (message -> c_size_ != (int) compressedDataSize + message -> i_size_) { #ifdef TEST *logofs << name() << ": Resizing data of message at " << "position " << position << " from " << message -> c_size_ << " to " << compressedDataSize + message -> i_size_ << " bytes.\n" << logofs_flush; #endif message -> data_.clear(); message -> data_.resize(compressedDataSize); } memcpy(message -> data_.begin(), newData, compressedDataSize); #ifdef TEST *logofs << name() << ": Data of message at position " << position << " has size " << message -> data_.size() << " and capacity " << message -> data_.capacity() << ".\n" << logofs_flush; #endif message -> c_size_ = compressedDataSize + message -> i_size_; storageSize(message, localSize, remoteSize); localStorageSize_ += localSize; remoteStorageSize_ += remoteSize; totalLocalStorageSize_ += localSize; totalRemoteStorageSize_ += remoteSize; #ifdef DEBUG printStorageSize(); #endif } else { #ifdef TEST *logofs << name() << ": No changes to data size for message " << "at position " << position << ".\n" << logofs_flush; #endif memcpy(message -> data_.begin(), newData, dataSize); } } int MessageStore::remove(const int position, T_checksum_action checksumAction, T_data_action dataAction) { Message *message; if (position < 0 || position >= cacheSlots || (message = (*messages_)[position]) == NULL) { #ifdef PANIC *logofs << name() << ": PANIC! Cannot remove " << "a non existing message at position " << position << ".\n" << logofs_flush; #endif cerr << "Error" << ": Cannot remove " << "a non existing message at position " << position << ".\n"; HandleAbort(); } #if defined(TEST) || defined(INFO) if (opcode() == X_PutImage || opcode() == X_NXPutPackedImage) { #ifdef WARNING *logofs << name() << ": WARNING! Discarding image object " << "of type " << name() << " at position " << position << ".\n" << logofs_flush; #endif } #endif // // The checksum is only stored at the encoding // side. // if (checksumAction == use_checksum) { #ifdef DEBUG *logofs << name() << ": Removing checksum for object at " << "position " << position << ".\n" << logofs_flush; #endif // // TODO: If we had stored the iterator and // not the pointer to the message, we could // have removed the message without having // to look up the checksum. // T_checksum checksum = getChecksum(message); #ifdef TEST *logofs << name() << ": Searching checksum [" << DumpChecksum(checksum) << "] in repository.\n" << logofs_flush; #endif T_checksums::iterator found = checksums_ -> find(checksum); if (found == checksums_ -> end()) { #ifdef PANIC *logofs << name() << ": PANIC! No checksum found for " << "object at position " << position << ".\n" << logofs_flush; #endif cerr << "Error" << ": No checksum found for " << "object at position " << position << ".\n"; HandleAbort(); } #ifdef TEST else if (position != found -> second) { #ifdef PANIC *logofs << name() << ": PANIC! Value of position for object " << "doesn't match position " << position << ".\n" << logofs_flush; #endif cerr << "Error" << ": Value of position for object " << "doesn't match position " << position << ".\n"; HandleAbort(); } #endif checksums_ -> erase(found); } #ifdef DEBUG *logofs << name() << ": Removing message at position " << position << " of size " << plainSize(position) << " (" << message -> size_ << "/" << message -> c_size_ << ").\n" << logofs_flush; #endif unsigned int localSize; unsigned int remoteSize; storageSize(message, localSize, remoteSize); localStorageSize_ -= localSize; remoteStorageSize_ -= remoteSize; totalLocalStorageSize_ -= localSize; totalRemoteStorageSize_ -= remoteSize; recycle(message); (*messages_)[position] = NULL; #ifdef DEBUG printStorageSize(); #endif return position; } // // This should only be called at encoding side. // The decoding side can't rely on the counter // as it is decremented by the encoding side // every time the repository is searched for a // message to be removed. // int MessageStore::getRating(Message *message, T_rating type) const { if (message -> locks_ != 0) { #ifdef TEST *logofs << name() << ": Rate set to -1 as locks of object are " << (int) message -> locks_ << ".\n" << logofs_flush; #endif return -1; } else if ((type == rating_for_clean || (int) checksums_ -> size() == cacheSlots) && message -> hits_ <= control -> StoreHitsLoadBonus) { // // We don't have any free slot or we exceeded the // available storage size. This is likely to happen // after having loaded objects from persistent cache. // It's not a bad idea to discard some messages that // were restored but never referenced. // #ifdef TEST if (type == rating_for_clean) { *logofs << name() << ": Rate set to 0 with hits " << message -> hits_ << " as maximum storage size " << "was exceeded.\n" << logofs_flush; } else { *logofs << name() << ": Rate set to 0 with hits " << message -> hits_ << " as there are no available " << "slots in store.\n" << logofs_flush; } #endif return 0; } else if (type == rating_for_clean && (getTimestamp()).tv_sec - message -> last_ >= control -> StoreTimeLimit) { #ifdef TEST *logofs << name() << ": Rate set to 0 as last hit of object was " << (getTimestamp()).tv_sec - message -> last_ << " seconds ago with limit set to " << control -> StoreTimeLimit << ".\n" << logofs_flush; #endif return 0; } else { #ifdef TEST if (message -> hits_ < 0) { *logofs << name() << ": PANIC! Rate of object shouldn't be " << message -> hits_ << ".\n" << logofs_flush; cerr << "Error" << ": Rate of object of type " << name() << " shouldn't be " << message -> hits_ << ".\n"; HandleAbort(); } #endif #ifdef TEST *logofs << name() << ": Rate of object is " << message -> hits_ << " with last hit " << (getTimestamp()).tv_sec - message -> last_ << " seconds ago.\n" << logofs_flush; #endif return message -> hits_; } } int MessageStore::touch(Message *message) const { message -> last_ = (getTimestamp()).tv_sec; message -> hits_ += control -> StoreHitsTouch; if (message -> hits_ > control -> StoreHitsLimit) { message -> hits_ = control -> StoreHitsLimit; } #ifdef TEST *logofs << name() << ": Increased hits of object to " << message -> hits_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif return message -> hits_; } int MessageStore::untouch(Message *message) const { message -> hits_ -= control -> StoreHitsUntouch; if (message -> hits_ < 0) { message -> hits_ = 0; } #ifdef TEST *logofs << name() << ": Decreased hits of object to " << message -> hits_ << ".\n" << logofs_flush; #endif return message -> hits_; } int MessageStore::lock(const int position) const { Message *message = (*messages_)[position]; if (message == NULL) { #ifdef PANIC *logofs << name() << ": PANIC! Can't lock the null " << "object at position " << position << ".\n" << logofs_flush; #endif return -1; } #ifdef DEBUG *logofs << name() << ": Increasing locks of object to " << (int) message -> locks_ + 1 << ".\n" << logofs_flush; #endif return ++(message -> locks_); } int MessageStore::unlock(const int position) const { Message *message = (*messages_)[position]; if (message == NULL) { #ifdef PANIC *logofs << name() << ": PANIC! Can't unlock the null " << "object at position " << position << ".\n" << logofs_flush; #endif return -1; } #ifdef DEBUG *logofs << name() << ": Decreasing locks of object to " << (int) message -> locks_ - 1 << ".\n" << logofs_flush; #endif return --(message -> locks_); } int MessageStore::saveStore(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian) { Message *message; #ifdef TEST *logofs << name() << ": Opcode of this store is " << (unsigned int) opcode() << " default size of " << "identity is " << dataOffset << ".\n" << logofs_flush; #endif unsigned char *identityBuffer = new unsigned char[dataOffset]; unsigned char *sizeBuffer = new unsigned char[4 * 2]; unsigned char *positionBuffer = new unsigned char[4]; unsigned char *opcodeBuffer = new unsigned char[4]; #ifdef DUMP char *md5ClientDump = new char[dataOffset * 2 + 128]; #endif unsigned char value; int offset; int failed = 0; for (int position = 0; position < cacheSlots; position++) { message = (*messages_)[position]; // // Don't save split messages. // if (message != NULL && message -> locks_ == 0) { // // Use the total size if offset is // beyond the real end of message. // offset = dataOffset; if (offset > message -> size_) { offset = message -> size_; } #ifdef TEST *logofs << name() << ": Going to save message at position " << position << ".\n" << logofs_flush; #endif value = 1; PutULONG(position, positionBuffer, bigEndian); PutULONG(opcode(), opcodeBuffer, bigEndian); md5_append(md5StateClient, positionBuffer, 4); md5_append(md5StateClient, opcodeBuffer, 4); #ifdef DUMP *logofs << "Name=" << name() << logofs_flush; sprintf(md5ClientDump," Pos=%d Op=%d\n", position, opcode()); *logofs << md5ClientDump << logofs_flush; #endif if (PutData(cachefs, &value, 1) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure writing " << 1 << " bytes.\n" << logofs_flush; #endif failed = 1; break; } md5_append(md5StateStream, &value, 1); PutULONG(message -> size_, sizeBuffer, bigEndian); PutULONG(message -> c_size_, sizeBuffer + 4, bigEndian); // // Note that the identity size is not saved with // the message and will be determined from the // data read when restoring the identity. // if (PutData(cachefs, sizeBuffer, 4 * 2) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure writing " << 4 * 2 << " bytes.\n" << logofs_flush; #endif failed = 1; break; } md5_append(md5StateStream, sizeBuffer, 4 * 2); md5_append(md5StateClient, sizeBuffer, 4 * 2); #ifdef DUMP sprintf(md5ClientDump, "size = %d c_size = %d\n", message -> size_, message -> c_size_); *logofs << md5ClientDump << logofs_flush; #endif // // Prepare a clean buffer for unparse. // CleanData(identityBuffer, offset); unparseIdentity(message, identityBuffer, offset, bigEndian); if (PutData(cachefs, identityBuffer, offset) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure writing " << offset << " bytes.\n" << logofs_flush; #endif failed = 1; break; } md5_append(md5StateStream, identityBuffer, offset); md5_append(md5StateClient, identityBuffer, offset); #ifdef DUMP for (int i = 0; i < offset; i++) { sprintf(md5ClientDump + (i * 2), "%02X", identityBuffer[i]); } *logofs << "Identity = " << md5ClientDump << "\n" << logofs_flush; #endif // // Set the real identity size before // saving the data. // offset = message -> i_size_; if (offset > message -> size_) { offset = message -> size_; } if (checksumAction == use_checksum) { if (PutData(cachefs, message -> md5_digest_, MD5_LENGTH) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure writing " << MD5_LENGTH << " bytes.\n" << logofs_flush; #endif failed = 1; break; } md5_append(md5StateStream, message -> md5_digest_, MD5_LENGTH); } else if (dataAction == use_data) { int dataSize = (message -> c_size_ == 0 ? message -> size_ - offset : message -> c_size_ - offset); if (dataSize > 0) { if (PutData(cachefs, message -> data_.begin(), dataSize) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure writing " << dataSize << " bytes.\n" << logofs_flush; #endif failed = 1; break; } md5_append(md5StateStream, message -> data_.begin(), dataSize); } } } else { #ifdef TEST *logofs << name() << ": Not saving message at position " << position << ".\n" << logofs_flush; #endif value = 0; if (PutData(cachefs, &value, 1) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure writing " << 1 << " bytes.\n" << logofs_flush; #endif failed = 1; break; } md5_append(md5StateStream, &value, 1); } } if (failed == 1) { #ifdef PANIC *logofs << name() << ": PANIC! Write to persistent cache file failed.\n" << logofs_flush; #endif cerr << "Error" << ": Write to persistent cache file failed.\n"; } delete [] identityBuffer; delete [] sizeBuffer; delete [] positionBuffer; delete [] opcodeBuffer; #ifdef DUMP delete [] md5ClientDump; #endif return (failed == 0 ? 1 : -1); } int MessageStore::loadStore(istream *cachefs, md5_state_t *md5StateStream, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian) { Message *message; #ifdef TEST *logofs << name() << ": Opcode of this store is " << (unsigned int) opcode() << " default size of " << "identity is " << dataOffset << " slots are " << cacheSlots << ".\n" << logofs_flush; #endif // // If packed images or the render extension has been // disabled we don't need to restore these messages // in the cache. Encoding of RENDER in 1.4.0 is also // changed so we want to skip messages saved using // the old format. We want to restore all the other // messages so we'll need to skip these one by one. // int skip = 0; if ((opcode() == X_NXPutPackedImage && control -> PersistentCacheLoadPacked == 0) || (opcode() == X_NXInternalRenderExtension && control -> PersistentCacheLoadRender == 0)) { #ifdef TEST *logofs << name() << ": All messages for OPCODE#" << (unsigned int) opcode() << " will be discarded.\n" << logofs_flush; #endif skip = 1; } unsigned char *identityBuffer = new unsigned char[dataOffset]; unsigned char *sizeBuffer = new unsigned char[4 * 2]; unsigned char value; int offset; int failed = 0; for (int position = 0; position < cacheSlots; position++) { if (GetData(cachefs, &value, 1) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure reading " << 1 << " bytes.\n" << logofs_flush; #endif failed = 1; break; } md5_append(md5StateStream, &value, 1); if (value == 1) { #ifdef TEST *logofs << name() << ": Going to load message at position " << position << ".\n" << logofs_flush; #endif if (GetData(cachefs, sizeBuffer, 4 * 2) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure reading " << 4 * 2 << " bytes.\n" << logofs_flush; #endif failed = 1; break; } md5_append(md5StateStream, sizeBuffer, 4 * 2); message = getTemporary(); if (message == NULL) { #ifdef PANIC *logofs << name() << ": PANIC! Can't access temporary storage " << "for message in context [B].\n" << logofs_flush; #endif cerr << "Error" << ": Can't access temporary storage " << "for message in context [B].\n"; failed = 1; break; } message -> size_ = GetULONG(sizeBuffer, bigEndian); message -> c_size_ = GetULONG(sizeBuffer + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Size is " << message -> size_ << " compressed size is " << message -> c_size_ << ".\n" << logofs_flush; #endif // // Use the total size if offset is // beyond the real end of message. // offset = dataOffset; if (offset > message -> size_) { offset = message -> size_; } if (GetData(cachefs, identityBuffer, offset) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure reading " << offset << " bytes.\n" << logofs_flush; #endif failed = 1; break; } md5_append(md5StateStream, identityBuffer, offset); // // Get the real identity size based on the value // reported by the message store. The dataOffset // value is guaranteed to be greater or equal to // the maximum identity size of the messages in // the major store. // offset = identitySize(identityBuffer, offset); if (offset > message -> size_) { offset = message -> size_; } message -> i_size_ = offset; // // Get identity of message from the buffer we just // created. Don't calculate neither checksum nor // data, restore them from stream. Don't pass the // message's size but the default size of identity. // parseIdentity(message, identityBuffer, offset, bigEndian); if (checksumAction == use_checksum) { if (message -> md5_digest_ == NULL) { message -> md5_digest_ = new md5_byte_t[MD5_LENGTH]; } if (GetData(cachefs, message -> md5_digest_, MD5_LENGTH) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure reading " << MD5_LENGTH << " bytes.\n" << logofs_flush; #endif failed = 1; break; } // // Add message's checksum to checksum that will // be saved together with this cache. Checksum // will be verified when cache file is restored // to ensure file is not corrupted. // md5_append(md5StateStream, message -> md5_digest_, MD5_LENGTH); if (skip == 1) { #ifdef TEST *logofs << name() << ": Discarding message for OPCODE#" << (unsigned int) opcode() << ".\n" << logofs_flush; #endif continue; } } else if (dataAction == use_data) { // // Restore the data part. // int dataSize = (message -> c_size_ == 0 ? message -> size_ - offset : message -> c_size_ - offset); if (dataSize < 0 || dataSize > control -> MaximumMessageSize) { #ifdef PANIC *logofs << name() << ": PANIC! Bad data size " << dataSize << " loading persistent cache.\n" << logofs_flush; #endif cerr << "Error" << ": Bad data size " << dataSize << " loading persistent cache.\n"; failed = 1; break; } else if (dataSize > 0) { // // If need to skip the message let anyway // it to be part of the calculated MD5. // if (skip == 1) { unsigned char *dummy = new unsigned char[dataSize]; if (dummy == NULL) { #ifdef PANIC *logofs << name() << ": PANIC! Can't allocate dummy buffer " << "of size " << dataSize << " loading cache.\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate dummy buffer " << "of size " << dataSize << " loading cache.\n"; failed = 1; break; } if (GetData(cachefs, dummy, dataSize) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure reading " << dataSize << " bytes.\n" << logofs_flush; #endif failed = 1; break; } md5_append(md5StateStream, dummy, dataSize); delete [] dummy; #ifdef TEST *logofs << name() << ": Discarding message for OPCODE#" << (unsigned int) opcode() << ".\n" << logofs_flush; #endif continue; } else { message -> data_.clear(); message -> data_.resize(dataSize); if (GetData(cachefs, message -> data_.begin(), dataSize) < 0) { #ifdef DEBUG *logofs << name() << ": PANIC! Failure reading " << dataSize << " bytes.\n" << logofs_flush; #endif failed = 1; break; } // // Add message's data to cache checksum. // md5_append(md5StateStream, message -> data_.begin(), dataSize); } } else { // // We are here if data part is zero. // if (skip == 1) { #ifdef TEST *logofs << name() << ": Discarding message for OPCODE#" << (unsigned int) opcode() << ".\n" << logofs_flush; #endif continue; } } } int added; added = add(message, position, checksumAction, dataAction); if (added != position) { #ifdef PANIC *logofs << name() << ": PANIC! Can't store message " << "in the cache at position " << position << ".\n" << logofs_flush; #endif cerr << "Error" << ": Can't store message " << "in the cache at position " << position << ".\n"; failed = 1; break; } else { // // Replace default value of hits set by add // function. Messages read from cache start // with a lower bonus than fresh messages // inserted. // message -> hits_ = control -> StoreHitsLoadBonus; #ifdef DEBUG *logofs << name() << ": Updated last hit of object at " << strMsTimestamp() << " with a bonus of " << message -> hits_ << ".\n" << logofs_flush; #endif resetTemporary(); } } else if ((*messages_)[position] != NULL) { #ifdef TEST *logofs << name() << ": Going to remove message at position " << position << ".\n" << logofs_flush; #endif int removed; removed = remove(position, checksumAction, dataAction); if (removed != position) { #ifdef PANIC *logofs << name() << ": PANIC! Can't remove message from cache " << "at position " << position << ".\n" << logofs_flush; #endif cerr << "Error" << ": Can't remove message from cache " << "at position " << position << ".\n"; failed = 1; break; } } #ifdef TEST else { *logofs << name() << ": Not loading message at position " << position << ".\n" << logofs_flush; } #endif } #ifdef WARNING if (failed == 1) { *logofs << name() << ": WARNING! Read from persistent cache file failed.\n" << logofs_flush; } #endif delete [] identityBuffer; delete [] sizeBuffer; return (failed == 0 ? 1 : -1); } void MessageStore::storageSize(const Message *message, unsigned int &local, unsigned int &remote) const { local = remote = storage(); // // Encoding side includes 48 bytes for // the map of checksums and 24 bytes // of adjustment for total overhead. // local += MD5_LENGTH + 48 + 24; // // At decoding side we include size of // data part and 24 bytes of adjustment // for total overhead. // if (message -> c_size_ == 0) { remote += message -> size_ + 24; } else { remote += message -> c_size_ + 24; } // // Check if we are the encoding or the // decoding side and, if needed, swap // the values. // if (message -> md5_digest_ == NULL) { unsigned int t = local; local = remote; remote = t; } } void MessageStore::printStorageSize() { #ifdef TEST *logofs << name() << ": There are " << checksums_ -> size() << " checksums in this store " << "out of " << cacheSlots << " slots.\n" << logofs_flush; *logofs << name() << ": Size of this store is " << localStorageSize_ << " bytes at local side and " << remoteStorageSize_ << " bytes at remote side.\n" << logofs_flush; *logofs << name() << ": Size of total cache is " << totalLocalStorageSize_ << " bytes at local side and " << totalRemoteStorageSize_ << " bytes at remote side.\n" << logofs_flush; #endif } nxcomp/Transport.cpp0000644000076400007640000020444411323113026015032 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include #include "Transport.h" #include "Statistics.h" // // Set the verbosity level. You also // need to define DUMP in Misc.cpp // if DUMP is defined here. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef INSPECT #undef DUMP // // Used to lock and unlock the transport // buffers before they are accessed by // different threads. // #undef THREADS // // Define this to get logging all the // operations performed by the parent // thread, the one that enqueues and // dequeues data. // #define PARENT // // Define this to know when a channel // is created or destroyed. // #undef REFERENCES // // Reference count for allocated buffers. // #ifdef REFERENCES int Transport::references_; int ProxyTransport::references_; int InternalTransport::references_; #endif // // This is the base class providing methods for read // and write buffering. // Transport::Transport(int fd) : fd_(fd) { #ifdef TEST *logofs << "Transport: Going to create base transport " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif type_ = transport_base; // // Set up the write buffer. // w_buffer_.length_ = 0; w_buffer_.start_ = 0; initialSize_ = TRANSPORT_BUFFER_DEFAULT_SIZE; thresholdSize_ = TRANSPORT_BUFFER_DEFAULT_SIZE << 1; maximumSize_ = TRANSPORT_BUFFER_DEFAULT_SIZE << 4; w_buffer_.data_.resize(initialSize_); // // Set non-blocking IO on socket. // SetNonBlocking(fd_, 1); blocked_ = 0; finish_ = 0; #ifdef REFERENCES *logofs << "Transport: Created new object at " << this << " out of " << ++references_ << " allocated references.\n" << logofs_flush; #endif } Transport::~Transport() { #ifdef TEST *logofs << "Transport: Going to destroy base class " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif ::close(fd_); #ifdef REFERENCES *logofs << "Transport: Deleted object at " << this << " out of " << --references_ << " allocated references.\n" << logofs_flush; #endif } // // Read data from its file descriptor. // int Transport::read(unsigned char *data, unsigned int size) { #ifdef DEBUG *logofs << "Transport: Going to read " << size << " bytes from " << "FD#" << fd_ << ".\n" << logofs_flush; #endif // // Read the available data from the socket. // int result = ::read(fd_, data, size); // // Update the current timestamp as the read // can have scheduled some other process. // getNewTimestamp(); if (result < 0) { if (EGET() == EAGAIN) { #ifdef TEST *logofs << "Transport: WARNING! Read of " << size << " bytes from " << "FD#" << fd_ << " would block.\n" << logofs_flush; #endif return 0; } else if (EGET() == EINTR) { #ifdef TEST *logofs << "Transport: Read of " << size << " bytes from " << "FD#" << fd_ << " was interrupted.\n" << logofs_flush; #endif return 0; } else { #ifdef TEST *logofs << "Transport: Error reading from " << "FD#" << fd_ << ".\n" << logofs_flush; #endif finish(); return -1; } } else if (result == 0) { #ifdef TEST *logofs << "Transport: No data read from " << "FD#" << fd_ << ".\n" << logofs_flush; #endif finish(); return -1; } #ifdef TEST *logofs << "Transport: Read " << result << " bytes out of " << size << " from FD#" << fd_ << ".\n" << logofs_flush; #endif #ifdef DUMP *logofs << "Transport: Dumping content of read data.\n" << logofs_flush; DumpData(data, result); #endif return result; } // // Write as many bytes as possible to socket. // Append the remaining data bytes to the end // of the buffer and update length to reflect // changes. // int Transport::write(T_write type, const unsigned char *data, const unsigned int size) { // // If an immediate write was requested then // flush the enqueued data first. // // Alternatively may try to write only if // the socket is not blocked. // // if (w_buffer_.length_ > 0 && blocked_ == 0 && // type == write_immediate) // { // ... // } // if (w_buffer_.length_ > 0 && type == write_immediate) { #ifdef TEST *logofs << "Transport: Writing " << w_buffer_.length_ << " bytes of previous data to FD#" << fd_ << ".\n" << logofs_flush; #endif int result = Transport::flush(); if (result < 0) { return -1; } } // // If nothing is remained, write immediately // to the socket. // unsigned int written = 0; if (w_buffer_.length_ == 0 && blocked_ == 0 && type == write_immediate) { // // Limit the amount of data sent. // unsigned int toWrite = size; #ifdef DUMP *logofs << "Transport: Going to write " << toWrite << " bytes to FD#" << fd_ << " with checksum "; DumpChecksum(data, size); *logofs << ".\n" << logofs_flush; #endif T_timestamp writeTs; int diffTs; while (written < toWrite) { // // Trace system time spent writing data. // writeTs = getTimestamp(); int result = ::write(fd_, data + written, toWrite - written); diffTs = diffTimestamp(writeTs, getNewTimestamp()); statistics -> addWriteTime(diffTs); if (result <= 0) { if (EGET() == EAGAIN) { #ifdef TEST *logofs << "Transport: Write of " << toWrite - written << " bytes on FD#" << fd_ << " would block.\n" << logofs_flush; #endif blocked_ = 1; break; } else if (EGET() == EINTR) { #ifdef TEST *logofs << "Transport: Write of " << toWrite - written << " bytes on FD#" << fd_ << " was interrupted.\n" << logofs_flush; #endif continue; } else { #ifdef TEST *logofs << "Transport: Write to " << "FD#" << fd_ << " failed.\n" << logofs_flush; #endif finish(); return -1; } } else { #ifdef TEST *logofs << "Transport: Immediately written " << result << " bytes on " << "FD#" << fd_ << ".\n" << logofs_flush; #endif written += result; } } #ifdef DUMP if (written > 0) { *logofs << "Transport: Dumping content of immediately written data.\n" << logofs_flush; DumpData(data, written); } #endif } if (written == size) { // // We will not affect the write buffer. // return written; } #ifdef DEBUG *logofs << "Transport: Going to append " << size - written << " bytes to write buffer for " << "FD#" << fd_ << ".\n" << logofs_flush; #endif if (resize(w_buffer_, size - written) < 0) { return -1; } memmove(w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_, data + written, size - written); w_buffer_.length_ += size - written; #ifdef TEST *logofs << "Transport: Write buffer for FD#" << fd_ << " has data for " << w_buffer_.length_ << " bytes.\n" << logofs_flush; *logofs << "Transport: Start is " << w_buffer_.start_ << " length is " << w_buffer_.length_ << " size is " << w_buffer_.data_.size() << " capacity is " << w_buffer_.data_.capacity() << ".\n" << logofs_flush; #endif // // Note that this function always returns the whole // size of buffer that was provided, either if not // all the data could be actually written. // return size; } // // Write pending data to its file descriptor. // int Transport::flush() { if (w_buffer_.length_ == 0) { #ifdef TEST *logofs << "Transport: No data to flush on " << "FD#" << fd_ << ".\n" << logofs_flush; #endif #ifdef WARNING if (blocked_ != 0) { *logofs << "Transport: Blocked flag is " << blocked_ << " with no data to flush on FD#" << fd_ << ".\n" << logofs_flush; } #endif return 0; } // // It's time to move data from the // write buffer to the real link. // int written = 0; int toWrite = w_buffer_.length_; // // We will do our best to write any available // data to the socket, so let's say we start // from a clean state. // blocked_ = 0; #ifdef TEST *logofs << "Transport: Going to flush " << toWrite << " bytes on FD#" << fd_ << ".\n" << logofs_flush; #endif T_timestamp writeTs; int diffTs; while (written < toWrite) { writeTs = getTimestamp(); int result = ::write(fd_, w_buffer_.data_.begin() + w_buffer_.start_ + written, toWrite - written); diffTs = diffTimestamp(writeTs, getNewTimestamp()); statistics -> addWriteTime(diffTs); if (result <= 0) { if (EGET() == EAGAIN) { #ifdef TEST *logofs << "Transport: Write of " << toWrite - written << " bytes on FD#" << fd_ << " would block.\n" << logofs_flush; #endif blocked_ = 1; break; } else if (EGET() == EINTR) { #ifdef TEST *logofs << "Transport: Write of " << toWrite - written << " bytes on FD#" << fd_ << " was interrupted.\n" << logofs_flush; #endif continue; } else { #ifdef TEST *logofs << "Transport: Write to " << "FD#" << fd_ << " failed.\n" << logofs_flush; #endif finish(); return -1; } } else { #ifdef TEST *logofs << "Transport: Flushed " << result << " bytes on " << "FD#" << fd_ << ".\n" << logofs_flush; #endif written += result; } } if (written > 0) { #ifdef DUMP *logofs << "Transport: Dumping content of flushed data.\n" << logofs_flush; DumpData(w_buffer_.data_.begin() + w_buffer_.start_, written); #endif // // Update the buffer status. // w_buffer_.length_ -= written; if (w_buffer_.length_ == 0) { w_buffer_.start_ = 0; } else { w_buffer_.start_ += written; } } // // It can be that we wrote less bytes than // available because of the write limit. // if (w_buffer_.length_ > 0) { #ifdef TEST *logofs << "Transport: There are still " << w_buffer_.length_ << " bytes in write buffer for " << "FD#" << fd_ << ".\n" << logofs_flush; #endif blocked_ = 1; } #ifdef TEST *logofs << "Transport: Write buffer for FD#" << fd_ << " has data for " << w_buffer_.length_ << " bytes.\n" << logofs_flush; *logofs << "Transport: Start is " << w_buffer_.start_ << " length is " << w_buffer_.length_ << " size is " << w_buffer_.data_.size() << " capacity is " << w_buffer_.data_.capacity() << ".\n" << logofs_flush; #endif // // No new data was produced for the link except // any outstanding data from previous writes. // return 0; } int Transport::drain(int limit, int timeout) { if (w_buffer_.length_ <= limit) { return 1; } // // Write the data accumulated in the write // buffer until it is below the limit or // the timeout is elapsed. // int toWrite = w_buffer_.length_; int written = 0; #ifdef TEST *logofs << "Transport: Draining " << toWrite - limit << " bytes on FD#" << fd_ << " with limit set to " << limit << ".\n" << logofs_flush; #endif T_timestamp startTs = getNewTimestamp(); T_timestamp selectTs; T_timestamp writeTs; T_timestamp idleTs; T_timestamp nowTs = startTs; int diffTs; fd_set writeSet; fd_set readSet; FD_ZERO(&writeSet); FD_ZERO(&readSet); int result; int ready; while (w_buffer_.length_ - written > limit) { nowTs = getNewTimestamp(); // // Wait for descriptor to become // readable or writable. // FD_SET(fd_, &writeSet); FD_SET(fd_, &readSet); setTimestamp(selectTs, timeout / 2); idleTs = nowTs; result = select(fd_ + 1, &readSet, &writeSet, NULL, &selectTs); nowTs = getNewTimestamp(); diffTs = diffTimestamp(idleTs, nowTs); statistics -> addIdleTime(diffTs); statistics -> subReadTime(diffTs); if (result < 0) { if (EGET() == EINTR) { #ifdef TEST *logofs << "Transport: Select on FD#" << fd_ << " was interrupted.\n" << logofs_flush; #endif continue; } else { #ifdef TEST *logofs << "Transport: Select on FD#" << fd_ << " failed.\n" << logofs_flush; #endif finish(); return -1; } } else if (result > 0) { ready = result; if (FD_ISSET(fd_, &writeSet)) { writeTs = getNewTimestamp(); result = ::write(fd_, w_buffer_.data_.begin() + w_buffer_.start_ + written, toWrite - written); nowTs = getNewTimestamp(); diffTs = diffTimestamp(writeTs, nowTs); statistics -> addWriteTime(diffTs); if (result > 0) { #ifdef TEST *logofs << "Transport: Forced flush of " << result << " bytes on " << "FD#" << fd_ << ".\n" << logofs_flush; #endif written += result; } else if (result < 0 && EGET() == EINTR) { #ifdef TEST *logofs << "Transport: Write to FD#" << fd_ << " was interrupted.\n" << logofs_flush; #endif continue; } else { #ifdef TEST *logofs << "Transport: Write to FD#" << fd_ << " failed.\n" << logofs_flush; #endif finish(); return -1; } ready--; } if (ready > 0) { if (FD_ISSET(fd_, &readSet)) { #ifdef TEST *logofs << "Transport: Not draining further " << "due to data readable on FD#" << fd_ << ".\n" << logofs_flush; #endif break; } } } #ifdef TEST else { *logofs << "Transport: Timeout encountered " << "waiting for FD#" << fd_ << ".\n" << logofs_flush; } #endif nowTs = getNewTimestamp(); diffTs = diffTimestamp(startTs, nowTs); if (diffTs >= timeout) { #ifdef TEST *logofs << "Transport: Not draining further " << "due to the timeout on FD#" << fd_ << ".\n" << logofs_flush; #endif break; } } if (written > 0) { #ifdef DUMP *logofs << "Transport: Dumping content of flushed data.\n" << logofs_flush; DumpData(w_buffer_.data_.begin() + w_buffer_.start_, written); #endif // // Update the buffer status. // w_buffer_.length_ -= written; if (w_buffer_.length_ == 0) { w_buffer_.start_ = 0; blocked_ = 0; } else { w_buffer_.start_ += written; #ifdef TEST *logofs << "Transport: There are still " << w_buffer_.length_ << " bytes in write buffer for " << "FD#" << fd_ << ".\n" << logofs_flush; #endif blocked_ = 1; } } #ifdef TEST else { *logofs << "Transport: WARNING! No data written to FD#" << fd_ << " with " << toWrite << " bytes to drain and limit " << "set to " << limit << ".\n" << logofs_flush; } #endif #ifdef TEST *logofs << "Transport: Write buffer for FD#" << fd_ << " has data for " << w_buffer_.length_ << " bytes.\n" << logofs_flush; *logofs << "Transport: Start is " << w_buffer_.start_ << " length is " << w_buffer_.length_ << " size is " << w_buffer_.data_.size() << " capacity is " << w_buffer_.data_.capacity() << ".\n" << logofs_flush; #endif return (w_buffer_.length_ <= limit); } int Transport::wait(int timeout) const { T_timestamp startTs = getNewTimestamp(); T_timestamp idleTs; T_timestamp selectTs; T_timestamp nowTs = startTs; long available = 0; int result = 0; int diffTs; fd_set readSet; FD_ZERO(&readSet); FD_SET(fd_, &readSet); for (;;) { available = readable(); diffTs = diffTimestamp(startTs, nowTs); if (available != 0 || timeout == 0 || (diffTs + (timeout / 10)) >= timeout) { #ifdef TEST *logofs << "Transport: There are " << available << " bytes on FD#" << fd_ << " after " << diffTs << " Ms.\n" << logofs_flush; #endif return available; } else if (available == 0 && result > 0) { #ifdef TEST *logofs << "Transport: Read on " << "FD#" << fd_ << " failed.\n" << logofs_flush; #endif return -1; } // // TODO: Should subtract the time // already spent in select. // selectTs.tv_sec = 0; selectTs.tv_usec = timeout * 1000; idleTs = nowTs; // // Wait for descriptor to become readable. // result = select(fd_ + 1, &readSet, NULL, NULL, &selectTs); nowTs = getNewTimestamp(); diffTs = diffTimestamp(idleTs, nowTs); statistics -> addIdleTime(diffTs); statistics -> subReadTime(diffTs); if (result < 0) { if (EGET() == EINTR) { #ifdef TEST *logofs << "Transport: Select on FD#" << fd_ << " was interrupted.\n" << logofs_flush; #endif continue; } else { #ifdef TEST *logofs << "Transport: Select on " << "FD#" << fd_ << " failed.\n" << logofs_flush; #endif return -1; } } #ifdef TEST else if (result == 0) { *logofs << "Transport: No data available on FD#" << fd_ << " after " << diffTimestamp(startTs, nowTs) << " Ms.\n" << logofs_flush; } else { *logofs << "Transport: Data became available on FD#" << fd_ << " after " << diffTimestamp(startTs, nowTs) << " Ms.\n" << logofs_flush; } #endif } } void Transport::setSize(unsigned int initialSize, unsigned int thresholdSize, unsigned int maximumSize) { initialSize_ = initialSize; thresholdSize_ = thresholdSize; maximumSize_ = maximumSize; #ifdef TEST *logofs << "Transport: Set buffer sizes for FD#" << fd_ << " to " << initialSize_ << "/" << thresholdSize_ << "/" << maximumSize_ << ".\n" << logofs_flush; #endif } void Transport::fullReset() { blocked_ = 0; finish_ = 0; fullReset(w_buffer_); } int Transport::resize(T_buffer &buffer, const int &size) { if ((int) buffer.data_.size() >= (buffer.length_ + size) && (buffer.start_ + buffer.length_ + size) > (int) buffer.data_.size()) { if (buffer.length_ > 0) { // // There is enough space in buffer but we need // to move existing data at the beginning. // #ifdef TEST *logofs << "Transport: Moving " << buffer.length_ << " bytes of data for " << "FD#" << fd_ << " to make room in the buffer.\n" << logofs_flush; #endif memmove(buffer.data_.begin(), buffer.data_.begin() + buffer.start_, buffer.length_); } buffer.start_ = 0; #ifdef DEBUG *logofs << "Transport: Made room for " << buffer.data_.size() - buffer.start_ << " bytes in buffer for " << "FD#" << fd_ << ".\n" << logofs_flush; #endif } else if ((buffer.length_ + size) > (int) buffer.data_.size()) { // // Not enough space, so increase // the size of the buffer. // if (buffer.start_ != 0 && buffer.length_ > 0) { #ifdef TEST *logofs << "Transport: Moving " << buffer.length_ << " bytes of data for " << "FD#" << fd_ << " to resize the buffer.\n" << logofs_flush; #endif memmove(buffer.data_.begin(), buffer.data_.begin() + buffer.start_, buffer.length_); } buffer.start_ = 0; unsigned int newSize = thresholdSize_; while (newSize < (unsigned int) buffer.length_ + size) { newSize <<= 1; if (newSize >= maximumSize_) { newSize = buffer.length_ + size + initialSize_; } } #ifdef DEBUG *logofs << "Transport: Buffer for " << "FD#" << fd_ << " will be enlarged from " << buffer.data_.size() << " to at least " << buffer.length_ + size << " bytes.\n" << logofs_flush; #endif buffer.data_.resize(newSize); #ifdef TEST if (newSize >= maximumSize_) { *logofs << "Transport: WARNING! Buffer for FD#" << fd_ << " grown to reach size of " << newSize << " bytes.\n" << logofs_flush; } #endif #ifdef TEST *logofs << "Transport: Data buffer for " << "FD#" << fd_ << " has now size " << buffer.data_.size() << " and capacity " << buffer.data_.capacity() << ".\n" << logofs_flush; #endif } return (buffer.length_ + size); } void Transport::fullReset(T_buffer &buffer) { // // Force deallocation and allocation // of the initial size. // #ifdef TEST *logofs << "Transport: Resetting buffer for " << "FD#" << fd_ << " with size " << buffer.data_.size() << " and capacity " << buffer.data_.capacity() << ".\n" << logofs_flush; #endif buffer.start_ = 0; buffer.length_ = 0; if (buffer.data_.size() > (unsigned int) initialSize_ && buffer.data_.capacity() > (unsigned int) initialSize_) { buffer.data_.clear(); buffer.data_.resize(initialSize_); #ifdef TEST *logofs << "Transport: Data buffer for " << "FD#" << fd_ << " shrunk to size " << buffer.data_.size() << " and capacity " << buffer.data_.capacity() << ".\n" << logofs_flush; #endif } } ProxyTransport::ProxyTransport(int fd) : Transport(fd) { #ifdef TEST *logofs << "ProxyTransport: Going to create proxy transport " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif type_ = transport_proxy; // // Set up the read buffer. // r_buffer_.length_ = 0; r_buffer_.start_ = 0; r_buffer_.data_.resize(initialSize_); // // For now we own the buffer. // owner_ = 1; // // Set up ZLIB compression. // int result; r_stream_.zalloc = NULL; r_stream_.zfree = NULL; r_stream_.opaque = NULL; r_stream_.next_in = NULL; r_stream_.avail_in = 0; if ((result = inflateInit2(&r_stream_, 15)) != Z_OK) { #ifdef PANIC *logofs << "ProxyTransport: PANIC! Failed initialization of ZLIB read stream. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed initialization of ZLIB read stream. " << "Error is '" << zError(result) << "'.\n"; HandleCleanup(); } if (control -> LocalStreamCompression) { w_stream_.zalloc = NULL; w_stream_.zfree = NULL; w_stream_.opaque = NULL; if ((result = deflateInit2(&w_stream_, control -> LocalStreamCompressionLevel, Z_DEFLATED, 15, 9, Z_DEFAULT_STRATEGY)) != Z_OK) { #ifdef PANIC *logofs << "ProxyTransport: PANIC! Failed initialization of ZLIB write stream. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed initialization of ZLIB write stream. " << "Error is '" << zError(result) << "'.\n"; HandleCleanup(); } } // // No ZLIB stream to flush yet. // flush_ = 0; #ifdef REFERENCES *logofs << "ProxyTransport: Created new object at " << this << " out of " << ++references_ << " allocated references.\n" << logofs_flush; #endif } ProxyTransport::~ProxyTransport() { #ifdef TEST *logofs << "ProxyTransport: Going to destroy derived class " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif // // Deallocate the ZLIB stream state. // inflateEnd(&r_stream_); if (control -> LocalStreamCompression) { deflateEnd(&w_stream_); } #ifdef REFERENCES *logofs << "ProxyTransport: Deleted object at " << this << " out of " << --references_ << " allocated references.\n" << logofs_flush; #endif } // // Read data from its file descriptor. // int ProxyTransport::read(unsigned char *data, unsigned int size) { // // If the remote peer is not compressing // the stream then just return any byte // read from the socket. // if (control -> RemoteStreamCompression == 0) { int result = Transport::read(data, size); if (result <= 0) { return result; } statistics -> addBytesIn(result); return result; } // // Return any pending data first. // if (r_buffer_.length_ > 0) { // // If the size of the buffer doesn't // match the amount of data pending, // force the caller to retry. // if ((int) size < r_buffer_.length_) { #ifdef TEST *logofs << "ProxyTransport: WARNING! Forcing a retry with " << r_buffer_.length_ << " bytes pending and " << size << " in the buffer.\n" << logofs_flush; #endif ESET(EAGAIN); return -1; } int copied = (r_buffer_.length_ > ((int) size) ? ((int) size) : r_buffer_.length_); memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied); // // Update the buffer status. // #ifdef DEBUG *logofs << "ProxyTransport: Going to immediately return " << copied << " bytes from proxy FD#" << fd_ << ".\n" << logofs_flush; #endif r_buffer_.length_ -= copied; if (r_buffer_.length_ == 0) { r_buffer_.start_ = 0; } else { r_buffer_.start_ += copied; #ifdef TEST *logofs << "ProxyTransport: There are still " << r_buffer_.length_ << " bytes in read buffer for proxy " << "FD#" << fd_ << ".\n" << logofs_flush; #endif } return copied; } // // Read data in the user buffer. // int result = Transport::read(data, size); if (result <= 0) { return result; } statistics -> addBytesIn(result); // // Decompress the data into the read // buffer. // #ifdef DEBUG *logofs << "ProxyTransport: Going to decompress data for " << "proxy FD#" << fd_ << ".\n" << logofs_flush; #endif int saveTotalIn = r_stream_.total_in; int saveTotalOut = r_stream_.total_out; int oldTotalIn = saveTotalIn; int oldTotalOut = saveTotalOut; int diffTotalIn; int diffTotalOut; #ifdef INSPECT *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut << ".\n" << logofs_flush; #endif r_stream_.next_in = (Bytef *) data; r_stream_.avail_in = result; // // Let ZLIB use all the space already // available in the buffer. // unsigned int newAvailOut = r_buffer_.data_.size() - r_buffer_.start_ - r_buffer_.length_; #ifdef TEST *logofs << "ProxyTransport: Initial decompress buffer is " << newAvailOut << " bytes.\n" << logofs_flush; #endif for (;;) { #ifdef INSPECT *logofs << "\nProxyTransport: Running the decompress loop.\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: r_buffer_.length_ = " << r_buffer_.length_ << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: r_buffer_.data_.size() = " << r_buffer_.data_.size() << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: newAvailOut = " << newAvailOut << ".\n" << logofs_flush; #endif if (resize(r_buffer_, newAvailOut) < 0) { return -1; } #ifdef INSPECT *logofs << "ProxyTransport: r_buffer_.data_.size() = " << r_buffer_.data_.size() << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: r_stream_.next_in = " << (void *) r_stream_.next_in << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: r_stream_.avail_in = " << r_stream_.avail_in << ".\n" << logofs_flush; #endif r_stream_.next_out = r_buffer_.data_.begin() + r_buffer_.start_ + r_buffer_.length_; r_stream_.avail_out = newAvailOut; #ifdef INSPECT *logofs << "ProxyTransport: r_stream_.next_out = " << (void *) r_stream_.next_out << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: r_stream_.avail_out = " << r_stream_.avail_out << ".\n" << logofs_flush; #endif int result = inflate(&r_stream_, Z_SYNC_FLUSH); #ifdef INSPECT *logofs << "ProxyTransport: Called inflate() result is " << result << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: r_stream_.avail_in = " << r_stream_.avail_in << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: r_stream_.avail_out = " << r_stream_.avail_out << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: r_stream_.total_in = " << r_stream_.total_in << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: r_stream_.total_out = " << r_stream_.total_out << ".\n" << logofs_flush; #endif diffTotalIn = r_stream_.total_in - oldTotalIn; diffTotalOut = r_stream_.total_out - oldTotalOut; #ifdef INSPECT *logofs << "ProxyTransport: diffTotalIn = " << diffTotalIn << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: diffTotalOut = " << diffTotalOut << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: r_buffer_.length_ = " << r_buffer_.length_ << ".\n" << logofs_flush; #endif r_buffer_.length_ += diffTotalOut; #ifdef INSPECT *logofs << "ProxyTransport: r_buffer_.length_ = " << r_buffer_.length_ << ".\n" << logofs_flush; #endif oldTotalIn = r_stream_.total_in; oldTotalOut = r_stream_.total_out; if (result == Z_OK) { if (r_stream_.avail_in == 0 && r_stream_.avail_out > 0) { break; } } else if (result == Z_BUF_ERROR && r_stream_.avail_out > 0 && r_stream_.avail_in == 0) { #ifdef TEST *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR decompressing data.\n" << logofs_flush; #endif break; } else { #ifdef PANIC *logofs << "ProxyTransport: PANIC! Decompression of data failed. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Decompression of data failed. Error is '" << zError(result) << "'.\n"; finish(); return -1; } // // Add more bytes to the buffer. // if (newAvailOut < thresholdSize_) { newAvailOut = thresholdSize_; } #ifdef TEST *logofs << "ProxyTransport: Need to add " << newAvailOut << " bytes to the decompress buffer in read.\n" << logofs_flush; #endif } diffTotalIn = r_stream_.total_in - saveTotalIn; diffTotalOut = r_stream_.total_out - saveTotalOut; #ifdef DEBUG *logofs << "ProxyTransport: Decompressed data from " << diffTotalIn << " to " << diffTotalOut << " bytes.\n" << logofs_flush; #endif statistics -> addDecompressedBytes(diffTotalIn, diffTotalOut); // // Check if the size of the buffer // matches the produced data. // if ((int) size < r_buffer_.length_) { #ifdef TEST *logofs << "ProxyTransport: WARNING! Forcing a retry with " << r_buffer_.length_ << " bytes pending and " << size << " in the buffer.\n" << logofs_flush; #endif ESET(EAGAIN); return -1; } // // Copy the decompressed data to the // provided buffer. // int copied = (r_buffer_.length_ > ((int) size) ? ((int) size) : r_buffer_.length_); #ifdef DEBUG *logofs << "ProxyTransport: Going to return " << copied << " bytes from proxy FD#" << fd_ << ".\n" << logofs_flush; #endif memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied); // // Update the buffer status. // r_buffer_.length_ -= copied; if (r_buffer_.length_ == 0) { r_buffer_.start_ = 0; } else { r_buffer_.start_ += copied; #ifdef TEST *logofs << "ProxyTransport: There are still " << r_buffer_.length_ << " bytes in read buffer for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif } return copied; } // // If required compress data, else write it to socket. // int ProxyTransport::write(T_write type, const unsigned char *data, const unsigned int size) { #ifdef TEST if (size == 0) { *logofs << "ProxyTransport: WARNING! Write called for FD#" << fd_ << " without any data to write.\n" << logofs_flush; return 0; } #endif // // If there is no compression revert to // plain socket management. // if (control -> LocalStreamCompression == 0) { int result = Transport::write(type, data, size); if (result <= 0) { return result; } statistics -> addBytesOut(result); statistics -> updateBitrate(result); FlushCallback(result); return result; } #ifdef DEBUG *logofs << "ProxyTransport: Going to compress " << size << " bytes to write buffer for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif // // Compress data into the write buffer. // int saveTotalIn = w_stream_.total_in; int saveTotalOut = w_stream_.total_out; int oldTotalIn = saveTotalIn; int oldTotalOut = saveTotalOut; int diffTotalIn; int diffTotalOut; #ifdef INSPECT *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut << ".\n" << logofs_flush; #endif w_stream_.next_in = (Bytef *) data; w_stream_.avail_in = size; // // Let ZLIB use all the space already // available in the buffer. // unsigned int newAvailOut = w_buffer_.data_.size() - w_buffer_.start_ - w_buffer_.length_; #ifdef TEST *logofs << "ProxyTransport: Initial compress buffer is " << newAvailOut << " bytes.\n" << logofs_flush; #endif for (;;) { #ifdef INSPECT *logofs << "\nProxyTransport: Running the compress loop.\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_buffer_.length_ = " << w_buffer_.length_ << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_buffer_.data_.size() = " << w_buffer_.data_.size() << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: newAvailOut = " << newAvailOut << ".\n" << logofs_flush; #endif if (resize(w_buffer_, newAvailOut) < 0) { return -1; } #ifdef INSPECT *logofs << "ProxyTransport: w_buffer_.data_.size() = " << w_buffer_.data_.size() << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.next_in = " << (void *) w_stream_.next_in << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.avail_in = " << w_stream_.avail_in << ".\n" << logofs_flush; #endif w_stream_.next_out = w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_; w_stream_.avail_out = newAvailOut; #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.next_out = " << (void *) w_stream_.next_out << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.avail_out = " << w_stream_.avail_out << ".\n" << logofs_flush; #endif int result = deflate(&w_stream_, (type == write_delayed ? Z_NO_FLUSH : Z_SYNC_FLUSH)); #ifdef INSPECT *logofs << "ProxyTransport: Called deflate() result is " << result << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.avail_in = " << w_stream_.avail_in << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.avail_out = " << w_stream_.avail_out << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.total_in = " << w_stream_.total_in << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.total_out = " << w_stream_.total_out << ".\n" << logofs_flush; #endif diffTotalOut = w_stream_.total_out - oldTotalOut; diffTotalIn = w_stream_.total_in - oldTotalIn; #ifdef INSPECT *logofs << "ProxyTransport: diffTotalIn = " << diffTotalIn << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: diffTotalOut = " << diffTotalOut << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_buffer_.length_ = " << w_buffer_.length_ << ".\n" << logofs_flush; #endif w_buffer_.length_ += diffTotalOut; #ifdef INSPECT *logofs << "ProxyTransport: w_buffer_.length_ = " << w_buffer_.length_ << ".\n" << logofs_flush; #endif oldTotalOut = w_stream_.total_out; oldTotalIn = w_stream_.total_in; if (result == Z_OK) { if (w_stream_.avail_in == 0 && w_stream_.avail_out > 0) { break; } } else if (result == Z_BUF_ERROR && w_stream_.avail_out > 0 && w_stream_.avail_in == 0) { #ifdef TEST *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR compressing data.\n" << logofs_flush; #endif break; } else { #ifdef PANIC *logofs << "ProxyTransport: PANIC! Compression of data failed. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Compression of data failed. Error is '" << zError(result) << "'.\n"; finish(); return -1; } // // Add more bytes to the buffer. // if (newAvailOut < thresholdSize_) { newAvailOut = thresholdSize_; } #ifdef TEST *logofs << "ProxyTransport: Need to add " << newAvailOut << " bytes to the compress buffer in write.\n" << logofs_flush; #endif } diffTotalIn = w_stream_.total_in - saveTotalIn; diffTotalOut = w_stream_.total_out - saveTotalOut; #ifdef TEST *logofs << "ProxyTransport: Compressed data from " << diffTotalIn << " to " << diffTotalOut << " bytes.\n" << logofs_flush; if (diffTotalIn != (int) size) { #ifdef PANIC *logofs << "ProxyTransport: PANIC! Bytes provided to ZLIB stream " << "should be " << size << " but they look to be " << diffTotalIn << ".\n" << logofs_flush; #endif } #endif // // Find out what we have to do with the // produced data. // if (type == write_immediate) { // // If user requested an immediate write we // flushed the ZLIB buffer. We can now reset // the counter and write data to socket. // flush_ = 0; #ifdef TEST *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_ << " has data for " << w_buffer_.length_ << " bytes.\n" << logofs_flush; *logofs << "ProxyTransport: Start is " << w_buffer_.start_ << " length is " << w_buffer_.length_ << " flush is " << flush_ << " size is " << w_buffer_.data_.size() << " capacity is " << w_buffer_.data_.capacity() << ".\n" << logofs_flush; #endif // // Alternatively may try to write only if // the socket is not blocked. // // if (w_buffer_.length_ > 0 && blocked_ == 0) // { // ... // } // if (w_buffer_.length_ > 0) { #ifdef TEST *logofs << "ProxyTransport: Writing " << w_buffer_.length_ << " bytes of produced data to FD#" << fd_ << ".\n" << logofs_flush; #endif int result = Transport::flush(); if (result < 0) { return -1; } } } else { // // We haven't flushed the ZLIB compression // buffer, so user will have to call proxy // transport's flush explicitly. // flush_ += diffTotalIn; } // // We either wrote the data or added it to the // write buffer. It's convenient to update the // counters at this stage to get the current // bitrate earlier. // statistics -> addCompressedBytes(diffTotalIn, diffTotalOut); statistics -> addBytesOut(diffTotalOut); statistics -> updateBitrate(diffTotalOut); FlushCallback(diffTotalOut); #ifdef TEST *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_ << " has data for " << w_buffer_.length_ << " bytes.\n" << logofs_flush; *logofs << "ProxyTransport: Start is " << w_buffer_.start_ << " length is " << w_buffer_.length_ << " flush is " << flush_ << " size is " << w_buffer_.data_.size() << " capacity is " << w_buffer_.data_.capacity() << ".\n" << logofs_flush; #endif return size; } // // Write data to its file descriptor. // int ProxyTransport::flush() { // // If there is no compression or we already compressed // outgoing data and just need to write it to socket // because of previous incomplete writes then revert // to plain socket management. // if (flush_ == 0 || control -> LocalStreamCompression == 0) { int result = Transport::flush(); if (result < 0) { return -1; } return result; } #ifdef DEBUG *logofs << "ProxyTransport: Going to flush compression on " << "proxy FD#" << fd_ << ".\n" << logofs_flush; #endif #ifdef TEST *logofs << "ProxyTransport: Flush counter for proxy FD#" << fd_ << " is " << flush_ << " bytes.\n" << logofs_flush; #endif // // Flush ZLIB stream into the write buffer. // int saveTotalIn = w_stream_.total_in; int saveTotalOut = w_stream_.total_out; int oldTotalIn = saveTotalIn; int oldTotalOut = saveTotalOut; int diffTotalOut; int diffTotalIn; #ifdef INSPECT *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut << ".\n" << logofs_flush; #endif w_stream_.next_in = w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_; w_stream_.avail_in = 0; // // Let ZLIB use all the space already // available in the buffer. // unsigned int newAvailOut = w_buffer_.data_.size() - w_buffer_.start_ - w_buffer_.length_; #ifdef DEBUG *logofs << "ProxyTransport: Initial flush buffer is " << newAvailOut << " bytes.\n" << logofs_flush; #endif for (;;) { #ifdef INSPECT *logofs << "\nProxyTransport: Running the flush loop.\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_buffer_.length_ = " << w_buffer_.length_ << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_buffer_.data_.size() = " << w_buffer_.data_.size() << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: newAvailOut = " << newAvailOut << ".\n" << logofs_flush; #endif if (resize(w_buffer_, newAvailOut) < 0) { return -1; } #ifdef INSPECT *logofs << "ProxyTransport: w_buffer_.data_.size() = " << w_buffer_.data_.size() << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.next_in = " << (void *) w_stream_.next_in << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.avail_in = " << w_stream_.avail_in << ".\n" << logofs_flush; #endif w_stream_.next_out = w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_; w_stream_.avail_out = newAvailOut; #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.next_out = " << (void *) w_stream_.next_out << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.avail_out = " << w_stream_.avail_out << ".\n" << logofs_flush; #endif int result = deflate(&w_stream_, Z_SYNC_FLUSH); #ifdef INSPECT *logofs << "ProxyTransport: Called deflate() result is " << result << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.avail_in = " << w_stream_.avail_in << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.avail_out = " << w_stream_.avail_out << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.total_in = " << w_stream_.total_in << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_stream_.total_out = " << w_stream_.total_out << ".\n" << logofs_flush; #endif diffTotalOut = w_stream_.total_out - oldTotalOut; diffTotalIn = w_stream_.total_in - oldTotalIn; #ifdef INSPECT *logofs << "ProxyTransport: diffTotalIn = " << diffTotalIn << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: diffTotalOut = " << diffTotalOut << ".\n" << logofs_flush; #endif #ifdef INSPECT *logofs << "ProxyTransport: w_buffer_.length_ = " << w_buffer_.length_ << ".\n" << logofs_flush; #endif w_buffer_.length_ += diffTotalOut; #ifdef INSPECT *logofs << "ProxyTransport: w_buffer_.length_ = " << w_buffer_.length_ << ".\n" << logofs_flush; #endif oldTotalOut = w_stream_.total_out; oldTotalIn = w_stream_.total_in; if (result == Z_OK) { if (w_stream_.avail_in == 0 && w_stream_.avail_out > 0) { break; } } else if (result == Z_BUF_ERROR && w_stream_.avail_out > 0 && w_stream_.avail_in == 0) { #ifdef TEST *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR flushing data.\n" << logofs_flush; #endif break; } else { #ifdef PANIC *logofs << "ProxyTransport: PANIC! Flush of compressed data failed. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Flush of compressed data failed. Error is '" << zError(result) << "'.\n"; finish(); return -1; } // // Add more bytes to the buffer. // if (newAvailOut < thresholdSize_) { newAvailOut = thresholdSize_; } #ifdef TEST *logofs << "ProxyTransport: Need to add " << newAvailOut << " bytes to the compress buffer in flush.\n" << logofs_flush; #endif } diffTotalIn = w_stream_.total_in - saveTotalIn; diffTotalOut = w_stream_.total_out - saveTotalOut; #ifdef TEST *logofs << "ProxyTransport: Compressed flush data from " << diffTotalIn << " to " << diffTotalOut << " bytes.\n" << logofs_flush; #endif // // Time to move data from the write // buffer to the real link. // #ifdef DEBUG *logofs << "ProxyTransport: Reset flush counter for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif flush_ = 0; #ifdef TEST *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_ << " has data for " << w_buffer_.length_ << " bytes.\n" << logofs_flush; *logofs << "ProxyTransport: Start is " << w_buffer_.start_ << " length is " << w_buffer_.length_ << " flush is " << flush_ << " size is " << w_buffer_.data_.size() << " capacity is " << w_buffer_.data_.capacity() << ".\n" << logofs_flush; #endif int result = Transport::flush(); if (result < 0) { return -1; } // // Update all the counters. // statistics -> addCompressedBytes(diffTotalIn, diffTotalOut); statistics -> addBytesOut(diffTotalOut); statistics -> updateBitrate(diffTotalOut); FlushCallback(diffTotalOut); return result; } unsigned int ProxyTransport::getPending(unsigned char *&data) { // // Return a pointer to the data in the // read buffer. It is up to the caller // to ensure that the data is consumed // before the read buffer is reused. // if (r_buffer_.length_ > 0) { unsigned int size = r_buffer_.length_; data = r_buffer_.data_.begin() + r_buffer_.start_; #ifdef DEBUG *logofs << "ProxyTransport: Returning " << size << " pending bytes from proxy FD#" << fd_ << ".\n" << logofs_flush; #endif r_buffer_.length_ = 0; r_buffer_.start_ = 0; // // Prevent the deletion of the buffer. // owner_ = 0; return size; } #ifdef TEST *logofs << "ProxyTransport: WARNING! No pending data " << "for proxy FD#" << fd_ << ".\n" << logofs_flush; #endif data = NULL; return 0; } void ProxyTransport::fullReset() { blocked_ = 0; finish_ = 0; flush_ = 0; if (control -> RemoteStreamCompression) { inflateReset(&r_stream_); } if (control -> LocalStreamCompression) { deflateReset(&w_stream_); } if (owner_ == 1) { Transport::fullReset(r_buffer_); } Transport::fullReset(w_buffer_); } AgentTransport::AgentTransport(int fd) : Transport(fd) { #ifdef TEST *logofs << "AgentTransport: Going to create agent transport " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif type_ = transport_agent; // // Set up the read buffer. // r_buffer_.length_ = 0; r_buffer_.start_ = 0; r_buffer_.data_.resize(initialSize_); // // For now we own the buffer. // owner_ = 1; // // Set up the mutexes. // #ifdef THREADS pthread_mutexattr_t m_attributes; pthread_mutexattr_init(&m_attributes); // // Interfaces in pthread to handle mutex // type do not work in current version. // m_attributes.__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP; if (pthread_mutex_init(&m_read_, &m_attributes) != 0) { #ifdef TEST *logofs << "AgentTransport: Child: Creation of read mutex failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif } if (pthread_mutex_init(&m_write_, &m_attributes) != 0) { #ifdef TEST *logofs << "AgentTransport: Child: Creation of write mutex failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif } #endif #ifdef REFERENCES *logofs << "AgentTransport: Child: Created new object at " << this << " out of " << ++references_ << " allocated references.\n" << logofs_flush; #endif } AgentTransport::~AgentTransport() { #ifdef TEST *logofs << "AgentTransport: Going to destroy derived class " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif // // Unlock and free all mutexes. // #ifdef THREADS pthread_mutex_unlock(&m_read_); pthread_mutex_unlock(&m_write_); pthread_mutex_destroy(&m_read_); pthread_mutex_destroy(&m_write_); #endif #ifdef REFERENCES *logofs << "AgentTransport: Child: Deleted object at " << this << " out of " << --references_ << " allocated references.\n" << logofs_flush; #endif } // // Read data enqueued by the other thread. // int AgentTransport::read(unsigned char *data, unsigned int size) { #ifdef THREADS lockRead(); #endif #ifdef DEBUG *logofs << "AgentTransport: Child: Going to read " << size << " bytes from " << "FD#" << fd_ << ".\n" << logofs_flush; #endif int copied = -1; if (r_buffer_.length_ > 0) { if ((int) size < r_buffer_.length_) { #ifdef TEST *logofs << "AgentTransport: WARNING! Forcing a retry with " << r_buffer_.length_ << " bytes pending and " << size << " in the buffer.\n" << logofs_flush; #endif ESET(EAGAIN); } else { copied = (r_buffer_.length_ > ((int) size) ? ((int) size) : r_buffer_.length_); memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied); // // Update the buffer status. // #ifdef TEST *logofs << "AgentTransport: Child: Going to immediately return " << copied << " bytes from FD#" << fd_ << ".\n" << logofs_flush; #endif #ifdef DUMP *logofs << "AgentTransport: Child: Dumping content of read data.\n" << logofs_flush; DumpData(data, copied); #endif r_buffer_.length_ -= copied; if (r_buffer_.length_ == 0) { r_buffer_.start_ = 0; } else { r_buffer_.start_ += copied; #ifdef TEST *logofs << "AgentTransport: Child: There are still " << r_buffer_.length_ << " bytes in read buffer for " << "FD#" << fd_ << ".\n" << logofs_flush; #endif } } } else { #ifdef DEBUG *logofs << "AgentTransport: Child: No data can be got " << "from read buffer for FD#" << fd_ << ".\n" << logofs_flush; #endif ESET(EAGAIN); } #ifdef THREADS unlockRead(); #endif return copied; } // // Write data to buffer so that the other // thread can get it. // int AgentTransport::write(T_write type, const unsigned char *data, const unsigned int size) { #ifdef THREADS lockWrite(); #endif // // Just append data to socket's write buffer. // Note that we don't care if buffer exceeds // the size limits set for this type of // transport. // #ifdef TEST *logofs << "AgentTransport: Child: Going to append " << size << " bytes to write buffer for " << "FD#" << fd_ << ".\n" << logofs_flush; #endif int copied = -1; if (resize(w_buffer_, size) < 0) { finish(); ESET(EPIPE); } else { memmove(w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_, data, size); w_buffer_.length_ += size; #ifdef DUMP *logofs << "AgentTransport: Child: Dumping content of written data.\n" << logofs_flush; DumpData(data, size); #endif #ifdef TEST *logofs << "AgentTransport: Child: Write buffer for FD#" << fd_ << " has data for " << w_buffer_.length_ << " bytes.\n" << logofs_flush; *logofs << "AgentTransport: Child: Start is " << w_buffer_.start_ << " length is " << w_buffer_.length_ << " size is " << w_buffer_.data_.size() << " capacity is " << w_buffer_.data_.capacity() << ".\n" << logofs_flush; #endif copied = size; } // // Let child access again the read buffer. // #ifdef THREADS unlockWrite(); #endif return copied; } int AgentTransport::flush() { // // In case of memory-to-memory transport // this function should never be called. // #ifdef PANIC *logofs << "AgentTransport: Child: PANIC! Called flush() for " << "memory to memory transport on " << "FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Called flush() for " << "memory to memory transport on " << "FD#" << fd_ << ".\n"; HandleAbort(); } int AgentTransport::drain(int limit, int timeout) { // // We can't drain the channel in the case // of the memory-to-memory transport. Data // is enqueued for the agent to read but // the agent could require multiple loops // to read it all. // // // In case of memory-to-memory transport // this function should never be called. // #ifdef PANIC *logofs << "AgentTransport: Child: PANIC! Called drain() for " << "memory to memory transport on " << "FD#" << fd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Called drain() for " << "memory to memory transport on " << "FD#" << fd_ << ".\n"; HandleAbort(); } unsigned int AgentTransport::getPending(unsigned char *&data) { #ifdef THREADS lockRead(); #endif if (r_buffer_.length_ > 0) { unsigned int size = r_buffer_.length_; data = r_buffer_.data_.begin() + r_buffer_.start_; #ifdef DEBUG *logofs << "AgentTransport: Child: Returning " << size << " pending bytes from FD#" << fd_ << ".\n" << logofs_flush; #endif r_buffer_.length_ = 0; r_buffer_.start_ = 0; #ifdef THREADS unlockRead(); #endif // // Prevent the deletion of the buffer. // owner_ = 0; return size; } #ifdef TEST *logofs << "AgentTransport: WARNING! No pending data " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif #ifdef THREADS unlockRead(); #endif data = NULL; return 0; } void AgentTransport::fullReset() { #ifdef THREADS lockRead(); lockWrite(); #endif #ifdef TEST *logofs << "AgentTransport: Child: Resetting transport " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif blocked_ = 0; finish_ = 0; if (owner_ == 1) { Transport::fullReset(r_buffer_); } Transport::fullReset(w_buffer_); } int AgentTransport::enqueue(const char *data, const int size) { #ifdef THREADS lockRead(); #endif if (finish_ == 1) { #if defined(PARENT) && defined(TEST) *logofs << "AgentTransport: Parent: Returning EPIPE in " << "write for finishing FD#" << fd_ << ".\n" << logofs_flush; #endif ESET(EPIPE); return -1; } // // Always allow the agent to write // all its data. // int toPut = size; #if defined(PARENT) && defined(TEST) *logofs << "AgentTransport: Parent: Going to put " << toPut << " bytes into read buffer for FD#" << fd_ << ". Buffer length is " << r_buffer_.length_ << ".\n" << logofs_flush; #endif if (resize(r_buffer_, toPut) < 0) { finish(); #ifdef THREADS unlockRead(); #endif return -1; } memcpy(r_buffer_.data_.begin() + r_buffer_.start_ + r_buffer_.length_, data, toPut); r_buffer_.length_ += toPut; #if defined(DUMP) && defined(PARENT) *logofs << "AgentTransport: Parent: Dumping content of enqueued data.\n" << logofs_flush; DumpData((const unsigned char *) data, toPut); #endif #if defined(PARENT) && defined(TEST) *logofs << "AgentTransport: Parent: Read buffer for FD#" << fd_ << " has now data for " << r_buffer_.length_ << " bytes.\n" << logofs_flush; *logofs << "AgentTransport: Parent: Start is " << r_buffer_.start_ << " length is " << r_buffer_.length_ << " size is " << r_buffer_.data_.size() << " capacity is " << r_buffer_.data_.capacity() << ".\n" << logofs_flush; #endif #ifdef THREADS unlockRead(); #endif return toPut; } int AgentTransport::dequeue(char *data, int size) { #ifdef THREADS lockWrite(); #endif if (w_buffer_.length_ == 0) { if (finish_ == 1) { #if defined(PARENT) && defined(TEST) *logofs << "AgentTransport: Parent: Returning 0 in read " << "for finishing FD#" << fd_ << ".\n" << logofs_flush; #endif return 0; } #if defined(PARENT) && defined(TEST) *logofs << "AgentTransport: Parent: No data can be read " << "from write buffer for FD#" << fd_ << ".\n" << logofs_flush; #endif ESET(EAGAIN); #ifdef THREADS unlockWrite(); #endif return -1; } // // Return as many bytes as possible. // int toGet = ((int) size > w_buffer_.length_ ? w_buffer_.length_ : size); #if defined(PARENT) && defined(TEST) *logofs << "AgentTransport: Parent: Going to get " << toGet << " bytes from write buffer for FD#" << fd_ << ".\n" << logofs_flush; #endif memcpy(data, w_buffer_.data_.begin() + w_buffer_.start_, toGet); w_buffer_.start_ += toGet; w_buffer_.length_ -= toGet; #if defined(DUMP) && defined(PARENT) *logofs << "AgentTransport: Parent: Dumping content of dequeued data.\n" << logofs_flush; DumpData((const unsigned char *) data, toGet); #endif #if defined(PARENT) && defined(TEST) *logofs << "AgentTransport: Parent: Write buffer for FD#" << fd_ << " has now data for " << length() << " bytes.\n" << logofs_flush; *logofs << "AgentTransport: Parent: Start is " << w_buffer_.start_ << " length is " << w_buffer_.length_ << " size is " << w_buffer_.data_.size() << " capacity is " << w_buffer_.data_.capacity() << ".\n" << logofs_flush; #endif #ifdef THREADS unlockWrite(); #endif return toGet; } int AgentTransport::dequeuable() { if (finish_ == 1) { #if defined(PARENT) && defined(TEST) *logofs << "AgentTransport: Parent: Returning EPIPE in " << "readable for finishing FD#" << fd_ << ".\n" << logofs_flush; #endif ESET(EPIPE); return -1; } #if defined(PARENT) && defined(TEST) *logofs << "AgentTransport: Parent: Returning " << w_buffer_.length_ << " as data readable " << "from read buffer for FD#" << fd_ << ".\n" << logofs_flush; #endif return w_buffer_.length_; } #ifdef THREADS int AgentTransport::lockRead() { for (;;) { int result = pthread_mutex_lock(&m_read_); if (result == 0) { #ifdef DEBUG *logofs << "AgentTransport: Read mutex locked by thread id " << pthread_self() << ".\n" << logofs_flush; #endif return 0; } else if (EGET() == EINTR) { continue; } else { #ifdef WARNING *logofs << "AgentTransport: WARNING! Locking of read mutex by thread id " << pthread_self() << " returned " << result << ". Error is '" << ESTR() << "'.\n" << logofs_flush; #endif return result; } } } int AgentTransport::lockWrite() { for (;;) { int result = pthread_mutex_lock(&m_write_); if (result == 0) { #ifdef DEBUG *logofs << "AgentTransport: Write mutex locked by thread id " << pthread_self() << ".\n" << logofs_flush; #endif return 0; } else if (EGET() == EINTR) { continue; } else { #ifdef WARNING *logofs << "AgentTransport: WARNING! Locking of write mutex by thread id " << pthread_self() << " returned " << result << ". Error is '" << ESTR() << "'.\n" << logofs_flush; #endif return result; } } } int AgentTransport::unlockRead() { for (;;) { int result = pthread_mutex_unlock(&m_read_); if (result == 0) { #ifdef DEBUG *logofs << "AgentTransport: Read mutex unlocked by thread id " << pthread_self() << ".\n" << logofs_flush; #endif return 0; } else if (EGET() == EINTR) { continue; } else { #ifdef WARNING *logofs << "AgentTransport: WARNING! Unlocking of read mutex by thread id " << pthread_self() << " returned " << result << ". Error is '" << ESTR() << "'.\n" << logofs_flush; #endif return result; } } } int AgentTransport::unlockWrite() { for (;;) { int result = pthread_mutex_unlock(&m_write_); if (result == 0) { #ifdef DEBUG *logofs << "AgentTransport: Write mutex unlocked by thread id " << pthread_self() << ".\n" << logofs_flush; #endif return 0; } else if (EGET() == EINTR) { continue; } else { #ifdef WARNING *logofs << "AgentTransport: WARNING! Unlocking of write mutex by thread id " << pthread_self() << " returned " << result << ". Error is '" << ESTR() << "'.\n" << logofs_flush; #endif return result; } } } #endif nxcomp/WriteBuffer.h0000644000076400007640000000606411323113030014720 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef WriteBuffer_H #define WriteBuffer_H #include "Misc.h" #define PANIC #define WARNING #undef TEST #undef DEBUG #define WRITE_BUFFER_DEFAULT_SIZE 16384 // // Adjust for the biggest reply that we could receive. // This is likely to be a reply to a X_ListFonts where // user has a large amount of installed fonts. // #define WRITE_BUFFER_OVERFLOW_SIZE 4194304 class WriteBuffer { public: WriteBuffer(); ~WriteBuffer(); void setSize(unsigned int initialSize, unsigned int thresholdSize, unsigned int maximumSize); unsigned char *addMessage(unsigned int numBytes); unsigned char *removeMessage(unsigned int numBytes); unsigned char *addScratchMessage(unsigned int numBytes); // // This form allows user to provide its own // buffer as write buffer's scratch area. // unsigned char *addScratchMessage(unsigned char *newBuffer, unsigned int numBytes); void removeScratchMessage(); void partialReset(); void fullReset(); unsigned char *getData() const { return buffer_; } unsigned int getLength() const { return length_; } unsigned int getAvailable() const { return (size_ - length_); } unsigned char *getScratchData() const { return scratchBuffer_; } unsigned int getScratchLength() const { return scratchLength_; } unsigned int getTotalLength() const { return (length_ + scratchLength_); } void registerPointer(unsigned char **pointer) { index_ = pointer; } void unregisterPointer() { index_ = 0; } private: unsigned int size_; unsigned int length_; unsigned char *buffer_; unsigned char **index_; unsigned int scratchLength_; unsigned char *scratchBuffer_; int scratchOwner_; unsigned int initialSize_; unsigned int thresholdSize_; unsigned int maximumSize_; }; #endif /* WriteBuffer_H */ nxcomp/PolyFillArc.h0000644000076400007640000001120011323113026014645 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PolyFillArc_H #define PolyFillArc_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define POLYFILLARC_ENABLE_CACHE 1 #define POLYFILLARC_ENABLE_DATA 0 #define POLYFILLARC_ENABLE_SPLIT 0 #define POLYFILLARC_ENABLE_COMPRESS 0 #define POLYFILLARC_DATA_LIMIT 6144 #define POLYFILLARC_DATA_OFFSET 12 #define POLYFILLARC_CACHE_SLOTS 2000 #define POLYFILLARC_CACHE_THRESHOLD 2 #define POLYFILLARC_CACHE_LOWER_THRESHOLD 1 // // The message class. // class PolyFillArcMessage : public Message { friend class PolyFillArcStore; public: PolyFillArcMessage() { } ~PolyFillArcMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int drawable; unsigned int gcontext; }; class PolyFillArcStore : public MessageStore { // // Constructors and destructors. // public: PolyFillArcStore() : MessageStore() { enableCache = POLYFILLARC_ENABLE_CACHE; enableData = POLYFILLARC_ENABLE_DATA; enableSplit = POLYFILLARC_ENABLE_SPLIT; enableCompress = POLYFILLARC_ENABLE_COMPRESS; dataLimit = POLYFILLARC_DATA_LIMIT; dataOffset = POLYFILLARC_DATA_OFFSET; cacheSlots = POLYFILLARC_CACHE_SLOTS; cacheThreshold = POLYFILLARC_CACHE_THRESHOLD; cacheLowerThreshold = POLYFILLARC_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~PolyFillArcStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "PolyFillArc"; } virtual unsigned char opcode() const { return X_PolyFillArc; } virtual unsigned int storage() const { return sizeof(PolyFillArcMessage); } // // Message handling methods. // public: virtual Message *create() const { return new PolyFillArcMessage(); } virtual Message *create(const Message &message) const { return new PolyFillArcMessage((const PolyFillArcMessage &) message); } virtual void destroy(Message *message) const { delete (PolyFillArcMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* PolyFillArc_H */ nxcomp/Rle.h0000644000076400007640000000301611323113031013211 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Rle_H #define Rle_H #include "Unpack.h" int UnpackRle(T_geometry *geometry, unsigned char method, unsigned char *src_data, int src_size, int dst_bpp, int dst_width, int dst_height, unsigned char *dst_data, int dst_size); #endif /* Rle_H */ nxcomp/RenderPictureClip.h0000644000076400007640000000464311323113030016060 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderPictureClip_H #define RenderPictureClip_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderPictureClip" #undef MESSAGE_STORE #define MESSAGE_STORE RenderPictureClipStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 12 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderPictureClip_H */ nxcomp/GetProperty.h0000644000076400007640000001060411323113030014753 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef GetProperty_H #define GetProperty_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define GETPROPERTY_ENABLE_CACHE 1 #define GETPROPERTY_ENABLE_DATA 0 #define GETPROPERTY_ENABLE_SPLIT 0 #define GETPROPERTY_ENABLE_COMPRESS 0 #define GETPROPERTY_DATA_LIMIT 0 #define GETPROPERTY_DATA_OFFSET 24 #define GETPROPERTY_CACHE_SLOTS 2000 #define GETPROPERTY_CACHE_THRESHOLD 2 #define GETPROPERTY_CACHE_LOWER_THRESHOLD 1 // // The message class. // class GetPropertyMessage : public Message { friend class GetPropertyStore; public: GetPropertyMessage() { } ~GetPropertyMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char property_delete; unsigned long window; unsigned long property; unsigned long type; unsigned long long_offset; unsigned long long_length; }; class GetPropertyStore : public MessageStore { // // Constructors and destructors. // public: GetPropertyStore() : MessageStore() { enableCache = GETPROPERTY_ENABLE_CACHE; enableData = GETPROPERTY_ENABLE_DATA; enableSplit = GETPROPERTY_ENABLE_SPLIT; enableCompress = GETPROPERTY_ENABLE_COMPRESS; dataLimit = GETPROPERTY_DATA_LIMIT; dataOffset = GETPROPERTY_DATA_OFFSET; cacheSlots = GETPROPERTY_CACHE_SLOTS; cacheThreshold = GETPROPERTY_CACHE_THRESHOLD; cacheLowerThreshold = GETPROPERTY_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~GetPropertyStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "GetProperty"; } virtual unsigned char opcode() const { return X_GetProperty; } virtual unsigned int storage() const { return sizeof(GetPropertyMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new GetPropertyMessage(); } virtual Message *create(const Message &message) const { return new GetPropertyMessage((const GetPropertyMessage &) message); } virtual void destroy(Message *message) const { delete (GetPropertyMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* GetProperty_H */ nxcomp/Auth.cpp0000644000076400007640000003636511342774053013762 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Auth.h" #include "Misc.h" #include "Control.h" #include "Timestamp.h" #include "Pipe.h" #define DEFAULT_STRING_LIMIT 512 // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Store the provided cookie as our 'fake' cookie, then // read the 'real' cookie from the current X authority // file. // Auth::Auth(char *display, char *cookie) { display_ = NULL; file_ = NULL; last_ = nullTimestamp(); fakeCookie_ = NULL; realCookie_ = NULL; fakeData_ = NULL; realData_ = NULL; dataSize_ = 0; generatedCookie_ = 0; if (display == NULL || *display == '\0' || cookie == NULL || *cookie == '\0' || strlen(cookie) != 32) { #ifdef PANIC *logofs << "Auth: PANIC! Can't create the X authorization data " << "with cookie '" << cookie << "' and display '" << display << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't create the X authorization data " << "with cookie '" << cookie << "' and display '" << display << "'.\n"; return; } #ifdef TEST *logofs << "Auth: Creating X authorization data with cookie '" << cookie << "' and display '" << display << "'.\n" << logofs_flush; #endif // // Get a local copy of all parameters. // display_ = new char[strlen(display) + 1]; file_ = new char[DEFAULT_STRING_LIMIT]; fakeCookie_ = new char[strlen(cookie) + 1]; realCookie_ = new char[DEFAULT_STRING_LIMIT]; if (display_ == NULL || file_ == NULL || fakeCookie_ == NULL || realCookie_ == NULL) { #ifdef PANIC *logofs << "Auth: PANIC! Cannot allocate memory for the X " << "authorization data.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot allocate memory for the X " << "authorization data.\n"; return; } strcpy(display_, display); *file_ = '\0'; strcpy(fakeCookie_, cookie); *realCookie_ = '\0'; // // Get the real cookie from the authorization file. // updateCookie(); } Auth::~Auth() { delete [] display_; delete [] file_; delete [] fakeCookie_; delete [] realCookie_; delete [] fakeData_; delete [] realData_; } // // At the present moment the cookie is read only once, // at the time the instance is initialized. If the auth // file changes along the life of the session, the old // cookie will be used. This works with X servers beca- // use of an undocumented "feature". See nx-X11. // int Auth::updateCookie() { if (isTimestamp(last_) == 0) { #ifdef TEST *logofs << "Auth: Reading the X authorization file " << "with last update at " << strMsTimestamp(last_) << ".\n" << logofs_flush; #endif if (getCookie() == 1 && validateCookie() == 1) { // // It should rather be the modification time // the auth file, so we can read it again if // the file is changed. // #ifdef TEST *logofs << "Auth: Setting last X authorization file " << "update at " << strMsTimestamp() << ".\n" << logofs_flush; #endif last_ = getTimestamp(); return 1; } #ifdef PANIC *logofs << "Auth: PANIC! Cannot read the cookie from the X " << "authorization file.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot read the cookie from the X " << "authorization file.\n"; return -1; } #ifdef TEST *logofs << "Auth: WARNING! Skipping check on the X " << "authorization file.\n" << logofs_flush; #endif return 0; } int Auth::getCookie() { // // Check the name of the auth file that we are going to use. // It can be either the value of the XAUTHORITY environment // or the default .Xauthority file in the user's home. // char *environment; environment = getenv("XAUTHORITY"); if (environment != NULL && *environment != '\0') { strncpy(file_, environment, DEFAULT_STRING_LIMIT - 1); } else { snprintf(file_, DEFAULT_STRING_LIMIT - 1, "%s/.Xauthority", control -> HomePath); } *(file_ + DEFAULT_STRING_LIMIT - 1) = '\0'; #ifdef TEST *logofs << "Auth: Using X authorization file '" << file_ << "'.\n" << logofs_flush; #endif // // Use the nxauth command on Windows and the Mac, xauth // on all the other platforms. On Windows and on the Mac // we assume that the nxauth command is located under // bin in the client installation directory. On all the // other platforms we use the default xauth command that // is in our path. // char command[DEFAULT_STRING_LIMIT]; #if defined(__CYGWIN32__) || defined(__APPLE__) snprintf(command, DEFAULT_STRING_LIMIT - 1, "%s/bin/nxauth", control -> SystemPath); *(command + DEFAULT_STRING_LIMIT - 1) = '\0'; #else strcpy(command, "xauth"); #endif #ifdef TEST *logofs << "Auth: Using X auth command '" << command << "'.\n" << logofs_flush; #endif // // The SSH code forces using the unix:n port when passing localhost:n. // This is probably because localhost:n can fail to return a valid // entry on machines where the hostname for localhost doesn't match // exactly the 'localhost' string. For example, on a freshly installed // Fedora Core 3 I get a 'localhost.localdomain/unix:0' entry. Query- // ing 'xauth list localhost:0' results in an empty result, while the // query 'xauth list unix:0' works as expected. Note anyway that if // the cookie for the TCP connection on 'localhost' is set to a dif- // ferent cookie than the one for the Unix connections, both SSH and // NX will match the wrong cookie and session will fail. // char line[DEFAULT_STRING_LIMIT]; if (strncmp(display_, "localhost:", 10) == 0) { snprintf(line, DEFAULT_STRING_LIMIT, "unix:%s", display_ + 10); } else { snprintf(line, DEFAULT_STRING_LIMIT, "%.200s", display_); } const char *parameters[256]; parameters[0] = command; parameters[1] = command; parameters[2] = "-f"; parameters[3] = file_; parameters[4] = "list"; parameters[5] = line; parameters[6] = NULL; #ifdef TEST *logofs << "Auth: Executing command "; for (int i = 0; i < 256 && parameters[i] != NULL; i++) { *logofs << "[" << parameters[i] << "]"; } *logofs << ".\n" << logofs_flush; #endif // // Use the popen() function to read the result // of the command. We would better use our own // implementation. // FILE *data = Popen((char *const *) parameters, "r"); int result = -1; if (data == NULL) { #ifdef PANIC *logofs << "Auth: PANIC! Failed to execute the X auth command.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to execute the X auth command.\n"; goto AuthGetCookieResult; } if (fgets(line, DEFAULT_STRING_LIMIT, data) == NULL) { #ifdef WARNING *logofs << "Auth: WARNING! Failed to read data from the X " << "auth command.\n" << logofs_flush; #endif #ifdef TEST cerr << "Warning" << ": Failed to read data from the X " << "auth command.\n"; #endif #ifdef PANIC *logofs << "Auth: WARNING! Generating a fake cookie for " << "X authentication.\n" << logofs_flush; #endif #ifdef TEST cerr << "Warning" << ": Generating a fake cookie for " << "X authentication.\n"; #endif generateCookie(realCookie_); } else { #ifdef TEST *logofs << "Auth: Checking cookie in string '" << line << "'.\n" << logofs_flush; #endif // // Skip the hostname in the authority entry // just in case it includes some white spaces. // char *cookie = NULL; cookie = index(line, ':'); if (cookie == NULL) { cookie = line; } if (sscanf(cookie, "%*s %*s %511s", realCookie_) != 1) { #ifdef PANIC *logofs << "Auth: PANIC! Failed to identify the cookie " << "in string '" << line << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to identify the cookie " << "in string '" << line << "'.\n"; goto AuthGetCookieResult; } #ifdef TEST *logofs << "Auth: Got cookie '" << realCookie_ << "' from file '" << file_ << "'.\n" << logofs_flush; #endif } result = 1; AuthGetCookieResult: if (data != NULL) { Pclose(data); } return result; } int Auth::validateCookie() { unsigned int length = strlen(realCookie_); if (length > DEFAULT_STRING_LIMIT / 2 - 1 || strlen(fakeCookie_) != length) { #ifdef PANIC *logofs << "Auth: PANIC! Size mismatch between cookies '" << realCookie_ << "' and '" << fakeCookie_ << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Size mismatch between cookies '" << realCookie_ << "' and '" << fakeCookie_ << "'.\n"; goto AuthValidateCookieError; } // // The length of the resulting data will be // half the size of the Hex cookie. // length = length / 2; fakeData_ = new char[length]; realData_ = new char[length]; if (fakeData_ == NULL || realData_ == NULL) { #ifdef PANIC *logofs << "Auth: PANIC! Cannot allocate memory for the binary X " << "authorization data.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot allocate memory for the binary X " << "authorization data.\n"; goto AuthValidateCookieError; } // // Translate the real cookie from Hex data // to its binary representation. // unsigned int value; for (unsigned int i = 0; i < length; i++) { if (sscanf(realCookie_ + 2 * i, "%2x", &value) != 1) { #ifdef PANIC *logofs << "Auth: PANIC! Bad X authorization data in real " << "cookie '" << realCookie_ << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Bad X authorization data in real cookie '" << realCookie_ << "'.\n"; goto AuthValidateCookieError; } realData_[i] = value; if (sscanf(fakeCookie_ + 2 * i, "%2x", &value) != 1) { #ifdef PANIC *logofs << "Auth: PANIC! Bad X authorization data in fake " << "cookie '" << fakeCookie_ << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Bad X authorization data in fake cookie '" << fakeCookie_ << "'.\n"; goto AuthValidateCookieError; } fakeData_[i] = value; } dataSize_ = length; #ifdef TEST *logofs << "Auth: Validated real cookie '" << realCookie_ << "' and fake cookie '" << fakeCookie_ << "' with data with size " << dataSize_ << ".\n" << logofs_flush; *logofs << "Auth: Ready to accept incoming connections.\n" << logofs_flush; #endif return 1; AuthValidateCookieError: delete [] fakeData_; delete [] realData_; fakeData_ = NULL; realData_ = NULL; dataSize_ = 0; return -1; } int Auth::checkCookie(unsigned char *buffer) { if (isValid() != 1) { #ifdef PANIC *logofs << "Auth: PANIC! Attempt to check the X cookie with " << "invalid authorization data.\n" << logofs_flush; #endif cerr << "Error" << ": Attempt to check the X cookie with " << "invalid authorization data.\n"; return -1; } const char *protoName = "MIT-MAGIC-COOKIE-1"; int protoSize = strlen(protoName); int matchedProtoSize; int matchedDataSize; if (buffer[0] == 0x42) { // // Byte order is MSB first. // matchedProtoSize = 256 * buffer[6] + buffer[7]; matchedDataSize = 256 * buffer[8] + buffer[9]; } else if (buffer[0] == 0x6c) { // // Byte order is LSB first. // matchedProtoSize = buffer[6] + 256 * buffer[7]; matchedDataSize = buffer[8] + 256 * buffer[9]; } else { #ifdef WARNING *logofs << "Auth: WARNING! Bad X connection data in the buffer.\n" << logofs_flush; #endif cerr << "Warning" << ": Bad X connection data in the buffer.\n"; return -1; } // // Check if both the authentication protocol // and the fake cookie match our data. // int protoOffset = 12; #ifdef TEST *logofs << "Auth: Received a protocol size of " << matchedProtoSize << " bytes.\n" << logofs_flush; #endif if (matchedProtoSize != protoSize || memcmp(buffer + protoOffset, protoName, protoSize) != 0) { #ifdef WARNING *logofs << "Auth: WARNING! Protocol mismatch or no X " << "authentication data.\n" << logofs_flush; #endif cerr << "Warning" << ": Protocol mismatch or no X " << "authentication data.\n"; return -1; } int dataOffset = protoOffset + ((matchedProtoSize + 3) & ~3); #ifdef TEST *logofs << "Auth: Received a data size of " << matchedDataSize << " bytes.\n" << logofs_flush; #endif if (matchedDataSize != dataSize_ || memcmp(buffer + dataOffset, fakeData_, dataSize_) != 0) { #ifdef WARNING *logofs << "Auth: WARNING! Cookie mismatch in the X " << "authentication data.\n" << logofs_flush; #endif cerr << "Warning" << ": Cookie mismatch in the X " << "authentication data.\n"; return -1; } // // Everything is OK. Replace the fake data. // #ifdef TEST *logofs << "Auth: Replacing fake X authentication data " << "with the real data.\n" << logofs_flush; #endif memcpy(buffer + dataOffset, realData_, dataSize_); return 1; } void Auth::generateCookie(char *cookie) { // // Code is from the SSH implementation, except that // we use a much weaker random number generator. // This is not critical, anyway, as this is just a // fake cookie. The X server doesn't have a cookie // for the display, so it will ignore the value we // feed to it. // T_timestamp timer = getTimestamp(); srand((unsigned int) timer.tv_usec); unsigned int data = rand(); for (int i = 0; i < 16; i++) { if (i % 4 == 0) { data = rand(); } snprintf(cookie + 2 * i, 3, "%02x", data & 0xff); data >>= 8; } generatedCookie_ = 1; #ifdef TEST *logofs << "Auth: Generated X cookie string '" << cookie << "'.\n" << logofs_flush; #endif } nxcomp/ActionCache.h0000644000076400007640000000300711323113030014627 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ActionCache_H #define ActionCache_H #include "IntCache.h" class ActionCache { friend class EncodeBuffer; friend class DecodeBuffer; public: ActionCache(); ~ActionCache(); private: IntCache *base_[256]; unsigned int slot_; unsigned short last_; }; #endif /* ActionCache_H */ nxcomp/DecodeBuffer.h0000644000076400007640000001070211323113031015004 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef DecodeBuffer_H #define DecodeBuffer_H #include #include "IntCache.h" #include "CharCache.h" #include "XidCache.h" #include "FreeCache.h" #include "OpcodeCache.h" #include "ActionCache.h" #include "ActionCacheCompat.h" #include "PositionCacheCompat.h" #define DECODE_BUFFER_OVERFLOW_SIZE 4194304 #define DECODE_BUFFER_POSTFIX_SIZE 1 class DecodeBuffer { public: DecodeBuffer(const unsigned char *data, unsigned int length); ~DecodeBuffer() { } int decodeValue(unsigned int &value, unsigned int numBits, unsigned int blockSize = 0, int endOkay = 0); int decodeCachedValue(unsigned int &value, unsigned int numBits, IntCache &cache, unsigned int blockSize = 0, int endOkay = 0); int decodeCachedValue(unsigned char &value, unsigned int numBits, CharCache &cache, unsigned int blockSize = 0, int endOkay = 0); void decodeDiffCachedValue(unsigned int &value, unsigned int &previous, unsigned int numBits, IntCache &cache, unsigned int blockSize = 0) { decodeCachedValue(value, numBits, cache, blockSize); previous += (value + 1); previous &= (0xffffffff >> (32 - numBits)); value = previous; } void decodeBoolValue(unsigned int &value) { decodeValue(value, 1); } int decodeOpcodeValue(unsigned char &value, OpcodeCache &cache, int endOkay = 0) { int result = decodeCachedValue(value, 8, cache.base_[cache.slot_], 8, endOkay); if (result == 1) { cache.slot_ = value; } return result; } void decodeActionValue(unsigned char &value, unsigned short &position, ActionCache &cache); void decodeNewXidValue(unsigned int &value, unsigned int &lastId, IntCache &lastIdCache, IntCache &cache, FreeCache &freeCache); void decodeNewXidValue(unsigned int &value, unsigned int &lastId, IntCache &lastIdCache, XidCache &cache, FreeCache &freeCache); void decodeXidValue(unsigned int &value, XidCache &cache); void decodeFreeXidValue(unsigned int &value, FreeCache &cache); void decodeActionValueCompat(unsigned char &value, ActionCacheCompat &cache) { decodeCachedValue(value, 2, cache.base_[cache.slot_]); cache.slot_ = value; } void decodePositionValueCompat(short int &value, PositionCacheCompat &cache); void decodeTextData(unsigned char *buffer, unsigned int numBytes) { decodeMemory(buffer, numBytes); } void decodeIntData(unsigned char *buffer, unsigned int numBytes) { decodeMemory(buffer, numBytes); } void decodeLongData(unsigned char *buffer, unsigned int numBytes) { decodeMemory(buffer, numBytes); } const unsigned char *decodeMemory(unsigned int numBytes); void decodeMemory(unsigned char *buffer, unsigned int numBytes) { memcpy(buffer, decodeMemory(numBytes), numBytes); } private: const unsigned char *buffer_; const unsigned char *end_; const unsigned char *nextSrc_; unsigned char srcMask_; }; #endif /* DecodeBuffer_H */ nxcomp/ChannelStore.h0000644000076400007640000000306411323113030015056 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ChannelStore_H #define ChannelStore_H // // One message store for each opcode. // #define CHANNEL_STORE_OPCODE_LIMIT 256 // // One split store for each resource. // #define CHANNEL_STORE_RESOURCE_LIMIT 256 class ChannelStore { public: ChannelStore() { } virtual ~ChannelStore() { } }; #endif /* ChannelStore_H */ nxcomp/ServerCache.cpp0000644000076400007640000001106711323113026015225 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ServerCache.h" // // Some global caches used to store information // common to all X connections. // BlockCache ServerCache::lastInitReply; BlockCache ServerCache::lastKeymap; unsigned char ServerCache::getKeyboardMappingLastKeysymsPerKeycode = 0; BlockCache ServerCache::getKeyboardMappingLastMap; BlockCache ServerCache::getModifierMappingLastMap; BlockCache ServerCache::xResources; BlockCacheSet ServerCache::queryFontFontCache(16); ServerCache::ServerCache() : replySequenceCache(6), eventSequenceCache(6), lastTimestamp(0), visualCache(8), colormapCache(8), errorMinorCache(8), colormapNotifyWindowCache(8), colormapNotifyColormapCache(8), createNotifyWindowCache(8), createNotifyLastWindow(0), exposeWindowCache(12), focusInWindowCache(8), keyPressLastKey(0), mapNotifyEventCache(8), mapNotifyWindowCache(8), motionNotifyTimestampCache(8), motionNotifyLastRootX(0), motionNotifyLastRootY(0), motionNotifyRootXCache(8), motionNotifyRootYCache(8), motionNotifyEventXCache(8), motionNotifyEventYCache(8), motionNotifyStateCache(8), noExposeDrawableCache(8), noExposeMinorCache(8), propertyNotifyWindowCache(8), propertyNotifyAtomCache(8), reparentNotifyWindowCache(8), selectionClearWindowCache(8), selectionClearAtomCache(8), visibilityNotifyWindowCache(8), getGeometryRootCache(8), getInputFocusWindowCache(8), getKeyboardMappingKeysymCache(8), getPropertyTypeCache(8), getPropertyTextCompressor(textCache, SERVER_TEXT_CACHE_SIZE), getSelectionOwnerCache(8), getWindowAttributesClassCache(8), getWindowAttributesPlanesCache(8), getWindowAttributesPixelCache(8), getWindowAttributesAllEventsCache(8), getWindowAttributesYourEventsCache(8), getWindowAttributesDontPropagateCache(8), queryPointerRootCache(8), queryPointerChildCache(8), translateCoordsChildCache(8), translateCoordsXCache(8), translateCoordsYCache(8), queryTreeWindowCache(8), getAtomNameTextCompressor(textCache, SERVER_TEXT_CACHE_SIZE) { unsigned int i; for (i = 0; i < 3; i++) { configureNotifyWindowCache[i] = new IntCache(8); } for (i = 0; i < 5; i++) { configureNotifyGeomCache[i] = new IntCache(8); } for (i = 0; i < 5; i++) { exposeGeomCache[i] = new IntCache(8); } for (i = 0; i < 3; i++) { motionNotifyWindowCache[i] = new IntCache(8); } for (i = 0; i < 5; i++) { getGeometryGeomCache[i] = new IntCache(8); } for (i = 0; i < 23; i++) { keyPressCache[i] = 0; } for (i = 0; i < 6; i++) { queryFontCharInfoCache[i] = new IntCache(8); queryFontLastCharInfo[i] = 0; } for (i = 0; i < 12; i++) { genericReplyIntCache[i] = new IntCache(8); } for (i = 0; i < 14; i++) { genericEventIntCache[i] = new IntCache(8); } } ServerCache::~ServerCache() { unsigned int i; for (i = 0; i < 3; i++) { delete configureNotifyWindowCache[i]; } for (i = 0; i < 5; i++) { delete configureNotifyGeomCache[i]; } for (i = 0; i < 5; i++) { delete exposeGeomCache[i]; } for (i = 0; i < 3; i++) { delete motionNotifyWindowCache[i]; } for (i = 0; i < 5; i++) { delete getGeometryGeomCache[i]; } for (i = 0; i < 6; i++) { delete queryFontCharInfoCache[i]; } for (i = 0; i < 12; i++) { delete genericReplyIntCache[i]; } for (i = 0; i < 14; i++) { delete genericEventIntCache[i]; } } nxcomp/RenderCreateGlyphSetCompat.h0000644000076400007640000000471711323113027017674 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderCreateGlyphSetCompat_H #define RenderCreateGlyphSetCompat_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderCreateGlyphSetCompat" #undef MESSAGE_STORE #define MESSAGE_STORE RenderCreateGlyphSetCompatStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 8 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderCreateGlyphSetCompat_H */ nxcomp/RenderMinorExtensionHeaders.h0000644000076400007640000000277511323113030020116 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderMinorExtensionHeaders_H #define RenderMinorExtensionHeaders_H #include "NXrender.h" #include "Message.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" #include "RenderExtension.h" #endif /* RenderMinorExtensionHeaders_H */ nxcomp/Z.h0000644000076400007640000000307411323113027012711 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Z_H #define Z_H #include int ZCompress(z_stream *stream, unsigned char *dest, unsigned int *destLen, const unsigned char *source, unsigned int sourceLen); int ZDecompress(z_stream *stream, unsigned char *dest, unsigned int *destLen, const unsigned char *source, unsigned int sourceLen); #endif /* Z_H */ nxcomp/ActionCacheCompat.h0000644000076400007640000000306011323113027016000 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ActionCacheCompat_H #define ActionCacheCompat_H #include "CharCache.h" class ActionCacheCompat { friend class EncodeBuffer; friend class DecodeBuffer; public: ActionCacheCompat() { slot_ = 0; } ~ActionCacheCompat() { } private: CharCache base_[4]; unsigned char slot_; }; #endif /* ActionCacheCompat_H */ nxcomp/ServerCache.h0000644000076400007640000001420411323113030014661 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ServerCache_H #define ServerCache_H #include "Misc.h" #include "IntCache.h" #include "CharCache.h" #include "OpcodeCache.h" #include "TextCompressor.h" #include "BlockCache.h" #include "BlockCacheSet.h" #include "ChannelCache.h" class ServerCache : public ChannelCache { public: ServerCache(); ~ServerCache(); // // Opcode prediction caches. // OpcodeCache opcodeCache; // // General-purpose caches. // CharCache textCache[SERVER_TEXT_CACHE_SIZE]; IntCache replySequenceCache; IntCache eventSequenceCache; unsigned int lastTimestamp; CharCache depthCache; IntCache visualCache; IntCache colormapCache; CharCache resourceCache; // // X connection startup. // static BlockCache lastInitReply; // // X errors. // CharCache errorCodeCache; IntCache errorMinorCache; CharCache errorMajorCache; // // ButtonPress and ButtonRelease events. // CharCache buttonCache; // // ColormapNotify event. // IntCache colormapNotifyWindowCache; IntCache colormapNotifyColormapCache; // // ConfigureNotify event. // IntCache *configureNotifyWindowCache[3]; IntCache *configureNotifyGeomCache[5]; // // CreateNotify event. // IntCache createNotifyWindowCache; unsigned int createNotifyLastWindow; // // Expose event. // IntCache exposeWindowCache; IntCache *exposeGeomCache[5]; // // FocusIn event (also used for FocusOut). // IntCache focusInWindowCache; // // KeymapNotify event. // static BlockCache lastKeymap; // // KeyPress event. // unsigned char keyPressLastKey; unsigned char keyPressCache[23]; // // MapNotify event (also used for UnmapNotify). // IntCache mapNotifyEventCache; IntCache mapNotifyWindowCache; // // MotionNotify event (also used for KeyPress, // KeyRelease, ButtonPress, ButtonRelease, // EnterNotify, and LeaveNotify events and // QueryPointer reply). // IntCache motionNotifyTimestampCache; unsigned int motionNotifyLastRootX; unsigned int motionNotifyLastRootY; IntCache motionNotifyRootXCache; IntCache motionNotifyRootYCache; IntCache motionNotifyEventXCache; IntCache motionNotifyEventYCache; IntCache motionNotifyStateCache; IntCache *motionNotifyWindowCache[3]; // // NoExpose event. // IntCache noExposeDrawableCache; IntCache noExposeMinorCache; CharCache noExposeMajorCache; // // PropertyNotify event. // IntCache propertyNotifyWindowCache; IntCache propertyNotifyAtomCache; // // ReparentNotify event. // IntCache reparentNotifyWindowCache; // // SelectionClear event. // IntCache selectionClearWindowCache; IntCache selectionClearAtomCache; // // VisibilityNotify event. // IntCache visibilityNotifyWindowCache; // // GetGeometry reply. // IntCache getGeometryRootCache; IntCache *getGeometryGeomCache[5]; // // GetInputFocus reply. // IntCache getInputFocusWindowCache; // // GetKeyboardMapping reply. // static unsigned char getKeyboardMappingLastKeysymsPerKeycode; static BlockCache getKeyboardMappingLastMap; IntCache getKeyboardMappingKeysymCache; CharCache getKeyboardMappingLastByteCache; // // GetModifierMapping reply. // static BlockCache getModifierMappingLastMap; // // GetProperty reply. // CharCache getPropertyFormatCache; IntCache getPropertyTypeCache; TextCompressor getPropertyTextCompressor; static BlockCache xResources; // // GetSelection reply. // IntCache getSelectionOwnerCache; // // GetWindowAttributes reply. // IntCache getWindowAttributesClassCache; CharCache getWindowAttributesBitGravityCache; CharCache getWindowAttributesWinGravityCache; IntCache getWindowAttributesPlanesCache; IntCache getWindowAttributesPixelCache; IntCache getWindowAttributesAllEventsCache; IntCache getWindowAttributesYourEventsCache; IntCache getWindowAttributesDontPropagateCache; // // QueryColors reply. // BlockCache queryColorsLastReply; // // QueryFont reply. // static BlockCacheSet queryFontFontCache; IntCache *queryFontCharInfoCache[6]; unsigned int queryFontLastCharInfo[6]; // // QueryPointer reply. // IntCache queryPointerRootCache; IntCache queryPointerChildCache; // // TranslateCoords reply. // IntCache translateCoordsChildCache; IntCache translateCoordsXCache; IntCache translateCoordsYCache; // // QueryTree reply. // IntCache queryTreeWindowCache; // // GetAtomName reply in protocol // versions >= 3. // TextCompressor getAtomNameTextCompressor; // // Generic reply. Use short data // in protocol versions >= 3. // CharCache genericReplyCharCache; IntCache *genericReplyIntCache[12]; // // Generic event. Only in protocol // versions >= 3. // CharCache genericEventCharCache; IntCache *genericEventIntCache[14]; // // Used in the abort split events. // OpcodeCache abortOpcodeCache; }; #endif /* ServerCache_H */ nxcomp/ProxyReadBuffer.cpp0000644000076400007640000001205011323113030016066 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ProxyReadBuffer.h" #include "Transport.h" // // Set the verbosity level. You also // need to define DUMP in Misc.cpp // if DUMP is defined here. // #define WARNING #define PANIC #undef TEST #undef DEBUG #undef DUMP unsigned int ProxyReadBuffer::suggestedLength(unsigned int pendingLength) { // // Always read all the data that // is available. // int readable = transport_ -> readable(); unsigned int readLength = (readable == -1 ? 0 : (unsigned int) readable); if (readLength < pendingLength) { readLength = pendingLength; } // // Even if the readable data is not // enough to make a complete message, // resize the buffer to accomodate // it all. // if (pendingLength < remaining_) { readLength = remaining_; } return readLength; } int ProxyReadBuffer::locateMessage(const unsigned char *start, const unsigned char *end, unsigned int &controlLength, unsigned int &dataLength, unsigned int &trailerLength) { unsigned int lengthLength = 0; const unsigned char *nextSrc = start; unsigned char next; dataLength = 0; #ifdef TEST *logofs << "ProxyReadBuffer: Locating message for FD#" << transport_ -> fd() << " with " << end - start << " bytes.\n" << logofs_flush; #endif // // Use something like the following if // you are looking for errors. // #ifdef DUMP if (control -> ProxyMode == proxy_server && start < end && transport_ -> fd() == 6 || transport_ -> fd() == 11) { *logofs << "ProxyReadBuffer: Partial checksums are:\n"; DumpBlockChecksums(start, end - start, 256); *logofs << logofs_flush; } #endif do { if (nextSrc >= end) { remaining_ = 1; #ifdef TEST *logofs << "ProxyReadBuffer: No message was located " << "with remaining " << remaining_ << ".\n" << logofs_flush; #endif return 0; } next = *nextSrc++; dataLength <<= 7; dataLength |= (unsigned int) (next & 0x7f); lengthLength++; } while (next & 0x80); unsigned int totalLength; if (dataLength == 0) { trailerLength = 0; controlLength = 3; totalLength = controlLength; } else { trailerLength = lengthLength; controlLength = 0; totalLength = dataLength + trailerLength; } if (start + totalLength > end) { // // When having to decompress a ZLIB stream, // a single byte can be enough to complete // the frame. // if (control -> RemoteStreamCompression == 0) { remaining_ = totalLength - (end - start); } else { remaining_ = 1; } #ifdef TEST *logofs << "ProxyReadBuffer: No message was located " << "with remaining " << remaining_ << ".\n" << logofs_flush; #endif return 0; } else { #ifdef DUMP *logofs << "ProxyReadBuffer: Received " << totalLength << " bytes of data " << "with checksum "; DumpChecksum(start, totalLength); *logofs << " on proxy FD#" << transport_ -> fd() << ".\n" << logofs_flush; #endif #if defined(TEST) || defined(INFO) *logofs << "ProxyReadBuffer: Produced plain input for " << dataLength << "+" << trailerLength << "+" << controlLength << " bytes out of " << totalLength << " bytes.\n" << logofs_flush; #endif #ifdef DUMP *logofs << "ProxyReadBuffer: Partial checksums are:\n"; DumpBlockChecksums(start, totalLength, 256); *logofs << logofs_flush; #endif remaining_ = 0; #ifdef TEST *logofs << "ProxyReadBuffer: Located message with " << "remaining " << remaining_ << ".\n" << logofs_flush; #endif return 1; } } nxcomp/CHANGELOG0000644000076400007640000040026311576152177013565 0ustar svetonisvetoniChangeLog: nxcomp-3.5.0-2 - Fixed TR11H02398. Solved a race condition when creating channels. nxcomp-3.5.0-1 - Opened the 3.5.0 branch based on nxcomp-3.4.0-7. - Updated copyright to year 2011. nxcomp-3.4.0-7 - Fixed TR03H02334. Modified the UNIX domain socket checks on MacOSX to be compliant with the standard introduced in OSX 10.6.3. nxcomp-3.4.0-6 - Solved compilation problems on Solaris. nxcomp-3.4.0-5 - Solved compilation problems on GCC 4.4. nxcomp-3.4.0-4 - Added reference to fixed TR02H02325. nxcomp-3.4.0-3 - Updated copyright to year 2010. nxcomp-3.4.0-2 - Fixed TR03G02204. Changed the parsing of X authority entries in order to handle the case where the hostname includes white spaces. - Fixed TR02H02325. Bug in PNG decompression on 16bpp displays. nxcomp-3.4.0-1 - Opened the 3.4.0 branch based on nxcomp-3.3.0-4. - Changed version number. - Updated copyright to year 2009. nxcomp-3.3.0-4 - Check if the variable storing the ping time exceeded the maximum integer value. - Recover incorrect sequence number when the proxy is not connected to an agent. nxcomp-3.3.0-3 - Removed a condition in ClientChannel that caused a loss in event sequence numbers. nxcomp-3.3.0-2 - Updated VERSION. nxcomp-3.3.0-1 - Opened the 3.3.0 branch based on nxcomp-3.2.0-7. nxcomp-3.2.0-7 - Solved a compilation problem on GCC 4.3. nxcomp-3.2.0-6 - Changes considering that unsetenv() returns void on Mac OS X. nxcomp-3.2.0-5 - Fixed TR03F02024. Assume the launchd unix socket as X socket. - Changed the authorization cookie retrieval when using the launchd socket on MacOSX, in order to wait for the X server start. nxcomp-3.2.0-4 - Fixed TR03F02026. Unset environment variable LD_LIBRARY_PATH before calling the exec function running nxclient. nxcomp-3.2.0-3 - Fix addMsTimestamp() and subMsTimestamp(). Remove the check for a valid input in diffTimestamp() and diffUsTimestamp(). nxcomp-3.2.0-2 - Fixed TR12E01973. Now the correct number of bits are used for the number of points in a X_FillPoly request. nxcomp-3.2.0-1 - Opened the 3.2.0 branch based on nxcomp-3.1.0-6. nxcomp-3.1.0-6 - Always use a timeout of 50 ms to update the congestion counter. nxcomp-3.1.0-5 - Solve the possible deadlock caused by both proxies running out of tokens at the same time. - In ServerProxy::handleCheckDrop() copy the list since the function can delete the elements. nxcomp-3.1.0-4 - Classes ProxyReadBuffer and ServerReadBuffer returned an invalid number of characters to read in the case of a readable() failure. - Connections to proxy versions newer than 3.0.0 were not accepted. - Reading options from file could fail if options contained spaces or other characters interrupting the fscanf()'s matching of input. - The proxy link could not be flushed if a ping request was replied after the end of the read loop. nxcomp-3.1.0-3 - Fixed a compilation warning on GCC 2.95. nxcomp-3.1.0-2 - Solved TR09E01852. Modified the encoding of the number of rects in the XSetClipRectangles request. nxcomp-3.1.0-1 - Opened the 3.1.0 branch based on nxcomp-3.0.0-46. nxcomp-3.0.0-46 - Fixed TR09E01865 and TR08E01831. Assume the old behavior of compr- essing the CUPS, SMB, HTTP and Font channels when connected to an old proxy version. nxcomp-3.0.0-45 - Retry the fork() 30 times, but only on Cygwin (Sob!). nxcomp-3.0.0-44 - In the case of an error, try again to fork() after a second, for a maximum of 5 times. This seems to constitute a valid workaround to the intermittent fork() failures observed on Windows. - Fixed NXTransDestroy() to show the 'broken connection' dialog in the case of a link failure on the client. - Handled the 'terminating' and 'terminated' messages as new proxy stages. - Force all the directories and files created by the library to be readable only by the user. - Don't complain if the shared memory segment can't be initialized on Windows. nxcomp-3.0.0-43 - Prevent the reallocation of the read buffer in the proxy and the agent transport if the buffer was borrowed by an upper layer. - Let the house-keeping process remove the cache directories that are empty and have not be used since more than 30 days. - Added the missing destruction of the ZLIB stream used for image decompression at proxy shutdown. nxcomp-3.0.0-42 - In handleFastWriteReply(), moved the initialization of the 'next' pointer inside the '#ifndef __sun' block. This should fix the re- maining encoding problems on SPARCs. nxcomp-3.0.0-41 - VMWare virtual machines can have the system timer deadly broken. In the case of timeout, try to send a ping regardless we are the client or the server proxy to force a write by the remote. - Removed the messages 'Info: Using image cache parameters...' and 'Info: Using image streaming parameters...' from the session log if the image cache is not enabled. nxcomp-3.0.0-40 - Removed the messages 'Info: Shutting down the NX transport' and 'Info: End of NX transport requested by...' at the session tear down. - Removed the remaining log output. nxcomp-3.0.0-39 - Don't reset the timer on proxy shutdown. nxcomp-3.0.0-38 - Don't reinstall the signal handler if the signal was enabled and the NXTransSignal() action is NX_SIGNAL_FORWARD. nxcomp-3.0.0-37 - Return a congestion level 9 if the agent channel is in congestion state. - Set the idle timeout (used to update the congestion state) based on the link type. The timeout is 0 ms with link LAN and 50 ms in the case of MODEM, with the other values in the between. nxcomp-3.0.0-36 - Changed the encoding of the RenderCompositeGlyphs requests. Use a separate cache for X and Y. Send a single bit for offset X and offset Y if they match the source coordinates. - Always use the memcpy() in the handleFastWrite*() on Sun, as the alternative code doesn't work on SPARC if the buffer is not word aligned. nxcomp-3.0.0-35 - Ensure a smooth transition between different congestion states. If the current congestion counter is greater than the previous, ramp up the value quickly by taking the current counter. If the new congestion counter is less than the previous, ramp down the reported value slowly by calculating the average of the last 8 updates. Reset the congestion counter to 0 if no token reply is pending and the proxy is idle until a timeout. nxcomp-3.0.0-34 - Calculate the current congestion as the average of the last 3 frames sent. nxcomp-3.0.0-33 - When sending a X_RenderCompositeGlyph update, send a single bit if the source X and Y match the values in cache. - Save the check on the bytes readable from the proxy descriptor after the select. nxcomp-3.0.0-32 - Send the X_GetImage replies uncompressed to better leverage the stream compression. - Before entering in congestion state, force a read to see whether the remote proxy aborted the connection. nxcomp-3.0.0-31 - Try to avoid using memcpy() in the channels' handleFastWrite*(). - Changed the copyright notices at the beginning of the files that were referring to NXPROXY to refer to NXCOMP. nxcomp-3.0.0-30 - Set the agent and proxy descriptors as ready, when appropriate, also in the case of an interrupt. nxcomp-3.0.0-29 - On the X server side, read more events from the X server just be- fore and just after having written some data. On the X client si- de, apply the same to the proxy link. - X11 channels originate a congestion control code as soon as the- re is some data left from the previous write. nxcomp-3.0.0-28 - Displaced automatically the "channel unresponsive" and "no data received from remote" dialogs if the error condition is ceased. - Rewritten the channel drain procedure. - Tried a different approach to the channel congestion handling. The X11 channels are not drained after a blocking write but only if a further write is requested and the channel is still in con- gestion state. Benchmarks show that this is still sub-optimal, compared to sending the congestion code immediately also for X11 channels. See the next version. nxcomp-3.0.0-27 - Ignored the agent option 'defer'. - Dropped support for the 1.4.0 versions. - More fixes to support the 1.5.0. nxcomp-3.0.0-26 - The channel read buffer can inherit the data to be encoded from the caller, instead of taking it from the transport. This is le- veraged by the agent channel to save the copy. - Ignored the agent option 'clients'. - Removed the deprecated code checking the CPU limits. - Removed the counters that were duplicated in the Statistics and the Control classes. nxcomp-3.0.0-25 - Improved the effectiveness of the caching of the RenderComposite- Glyphs by using a differential encoding for the length, delta x and delta y of the first string. - Better handle the compatibility between different formats of the cache files. nxcomp-3.0.0-24 - Fixed the compatibility problem with the 1.5.0 versions. - Removed the constants NXDisplayReadable, NXDisplayFlushable and NXDisplayCongestion. Now nxcompext has three distinct functions. nxcomp-3.0.0-23 - Removed support for Rdp, Tight and Hextile packed images decod- ing since they have been made obsolete by the new NX server. - Use the value 0 for the action is_hit and 1 for is_added. This increases the frequency of zeroes in the encode buffer, giving better compression. - Changed the copyright attribution from Medialogic to NoMachine. nxcomp-3.0.0-22 - Implemented an alternate encoding reducing the size of the int caches to 16 elements and transmitting the values in blocks of 4 bits. Although this encoding reduces the effectiveness of the differential compression, it allows better compression of the final stream, resulting in a 10% to 20% gain in all condi- tions. The new encoding is experimental, as it would make the 3.0.0 incompatible with all the previous NX versions. Use this package as a reference for future implementations. nxcomp-3.0.0-21 - Encode the first two coordinates of a FillPoly message by using a differential encoding, thus the origin of the polygon is not included in the checksum. This is aimed to work in combination with the agent converting all the FillPoly requests to the rela- tive coordinate mode. - Add the coordinate mode to the checksum of the PolyPoint and Po- lyLine messages so that it doesn't need to be encoded separately. - Disable the streaming for all messages. This saves the encoding of a boolean for each message that could potentially be streamed. - Don't try to match the block size when encoding a value based on an IntCache. This saves the additional bool in 90% of the cases. - Removed support for the former RDP and RFB session types. - Due to the above changes, this version is incompatible with the previous 3.0.0 releases. nxcomp-3.0.0-20 - Added support for the 'lossy', 'lossless' and 'adaptive' pack me- thod literals. These values activate the dynamic selection of the pack method by the agent. - Made the 'adaptive' pack method the default for all link types. - The default stream compression level is now 6 for link type ISDN, 4 for ADSL and 1 for WAN. The default data compression level is always 1, except for LAN that is 0. nxcomp-3.0.0-19 - Print in the session log the X11 display that the proxy is imper- sonating or where it is forwarding the connections. nxcomp-3.0.0-18 - Implemented decoding of the PACK_BITMAP_16M_COLORS pack method. This is a very simple encoder removing the 4th byte in 32 bits- per-plane images. This encoder is intended to better leverage the stream compression on low bandwidth links. - Removed the code cleaning the padding bytes in RenderAddGlyphs and RenderCompositeGlyphs as the operation is now performed in Xrender. - Removed the "Info: Synchronizing local and remote caches." and "Info: Using remote server '...'." lines from the session log. nxcomp-3.0.0-17 - Determine the CPU endianess at compile time only on Linux and Cygwin. nxcomp-3.0.0-16 - Optimized the memory allocation of the ReadBuffer classes. The buffers from the Transport classes are used, when possible, to avoid the copy. nxcomp-3.0.0-15 - Reworked the code dealing with the channel congestion control messages for increased efficiency. - Removed the pending_ flag from the proxy and channel class mem- bers. If needed, the classes will query the transport buffers to see whether there is data pending. - Removed the additional parameter from NXTransFlush(). - The proxy doesn't issue anymore a sync control message on a X reply. Anyway it will respond to the request for compatibility with older versions. - Removed the dynamic selection of the encoding method for the X_QueryColors request and X_ListFonts, X_QueryColors, X_Query- Font replies. Now all of them use the differential encoding to better leverage the stream compression. - Fixed to build on SUNs where we don't have endian.h. nxcomp-3.0.0-14 - When handling partial proxy messages, don't try to determine how many bytes still need to be read if the ZLIB stream compression is enabled. - If a message can't be located, the ReadBuffer class checks if it is possible to consume the data already in the transport buffer. - Optimize the MD5 routines by determining the CPU endianess at compile time. - Force Drawable and GContext to be CARD32 in NXproto.h. nxcomp-3.0.0-13 - Changed the semantic of the NXDisplayFlushHandler callback. The function is called when new data is sent to the remote proxy or, with a 0 length, upon receiving a new token reply, so that the agent can update its congestion state. - Added the NXTransCongestion() function. It returns a value bet- ween 0 and 9 indicating the congestion level based on the tokens remaining. A value of 9 means that the link is congested and no further data can be sent. - Removed the congestion and synchronization callbacks. They are superseded by the new NXTransCongestion() implementation. nxcomp-3.0.0-12 - Send the token replies at the end of the encode loop instead of flushing the buffer immediately. - Fixed TR05E01687. Correctly detect CTRL-ALT-SHIFT-ESC keystrokes. - Renamed the option 'block' as 'tile'. - Ignored the option 'menu', targeting the X11 agent. nxcomp-3.0.0-11 - Removed the 'interactive' attribute from generic channels. We now use the 'prioritized' attribute which yields the same result. - Simplified the channels' read loop by always reading only once. - The channels flush the link as soon as the token is exceeded even if the flush policy is set to deferred. - Reserved more cache memory for the images when running a shadow session. nxcomp-3.0.0-10 - Added the NXDisplayWriteHandler callback definition. - Removed the code that forced the proxy to retain in the cache the images referenced at session startup. - Disabled the persistent image cache since this is not supported by the current agent. This will save the overhead caused by the image house-keeping process. nxcomp-3.0.0-9 - Fixed a cast problem in GetBytesReadable(). nxcomp-3.0.0-8 - Added the NXTransTime() utility. It return the time in milliseconds elapsed since the last call to the same function. Useful for bench- marking the Xlib layer. - Ignored the option 'block' targeting the X11 agent. nxcomp-3.0.0-7 - Ignored the options 'shadow', 'shadowmode' targeting the X11 agent. - Added the session type 'shadow'. nxcomp-3.0.0-6 - Fixed TR08D01484. Fixed a bug in the cleanup of the RenderAddGlyphs messages. We clean up only the glyphs with depth 8, width greater than 1 and pixmap byte pad 4. - Added file COPYING. nxcomp-3.0.0-5 - Updated the copyright notices to the current year. nxcomp-3.0.0-4 - Imported changes up to nxcomp-2.1.0-6. - Fixed TR12D01563. Changed the configure script to always build the library with the -fPIC option. This is related to the way SELinux works. - Fixed the dependency from local endianess for 16-bit color RDP un- packing functions. This caused wrong colors to be displayed when the proxy and the X server were running on two separate machines having different endianess. - Fixed TR10D01523. The Channel::handleDrain() method now accounts for the fact that agent connections can't be drained. - Changed the Tight and Hextile decoding functions to use the X ser- ver's byte order. nxcomp-3.0.0-3 - Changed the main loop to handle the 3.0.0 version. nxcomp-3.0.0-2 - Updated the file VERSION. nxcomp-3.0.0-1 - Opened the 3.0.0 branch based on nxcomp-2.0.0-81. nxcomp-2.0.0-81 - Fixed the dependency from local endianess for JPEG and PNG decomp- ression functions. This caused wrong colors to be displayed when the proxy and the X server were running on two separate machines having different endianess. - Fixed the number of entries in the handleColormap() function. nxcomp-2.0.0-80 - Fixed TR06D01404. Removed the warning issued when calling the AgentTransport::drain() method. nxcomp-2.0.0-79 - Updated copyright to year 2006. nxcomp-2.0.0-78 - Fixed a bug in the cleanup of the RenderAddGlyphs messages. - Minor improvements to the encoding of the RenderCompositeGlyphs and RenderComposite requests. - Added 'client' to the list of the ignored options. nxcomp-2.0.0-77 - Added cleanup of the padding bytes in the RenderCompositeGlyphs and RenderAddGlyphs requests. nxcomp-2.0.0-76 - Before adding the frame's bytes to the tokens, the frame length is normalized based on the stream compression ratio of the last frame sent. This avoids entering the drain loop too early when the data is highly compressible. - Moved the render messages' private data in a union to reduce the footprint of the render store. nxcomp-2.0.0-75 - The action performed on the remote message store and the affected position are encoded as a single value. This saves approximately 1 bit per each message. - Created more Compat classes to handle the backward compatibility with the 1.4.0 and 1.5.0 versions. nxcomp-2.0.0-74 - Added support for the RLE pack method and made it the default if the selected link type is LAN. - Removed the legacy support for the 'render' option provided at X server side. - When defining LAME in ClientChannel.h, the channel has the chance of suppressing more opcodes by the handleTaintLameRequest() call. This is for test purposes. nxcomp-2.0.0-73 - The X_NXSetUnpackColormap and X_NXSetUnpackAlpha now carry their data in compressed form. The alpha data is compressed using the ZLIB RLE encoding, while the colormap data is compressed using the default ZLIB deflate. - Thanks to the above changes, ZLIB data compression of all messa- ges which do not have a specific encoding is turned off, so that we can make better use of the final stream compression. - Created new message structures to handle the compatibility with the old proxy versions. When connected to an old proxy version the agent should use the NXSetUnpackColormapCompat() and NXSet- UnpackAlpha() interfaces. nxcomp-2.0.0-72 - Solved a compatibility problem with the 1.X.X versions due to the new render encodings. nxcomp-2.0.0-71 - Improvements to X_RenderSetPictureClipRectangles. - Fixed handleUnpack() to pass the correct byte order parameter to to the unpack alpha function. - Added the --parent parameter to the client even in pulldown mode. - Fixed a deallocation bug in StaticCompressor. nxcomp-2.0.0-70 - Added compression of X_RenderSetPictureFilter, X_RenderSetPicture- Transform, X_RenderTrapezoids and X_RenderTriangles. nxcomp-2.0.0-69 - Changed the format of the persistent cache to accomodate the new encoding of the render opcodes. Caches from the 1.4.0 and 1.5.0 should be still loaded and saved correctly when connected to and old version of the library. - Improved the encoding of the X_CreatePixmap, X_ChangeGC, X_Render- CompositeGlyphs, X_RenderComposite, X_RenderCreateGlyphSet, X_Rend- erFreePicture and X_RenderCreatePicture messages. Added new classes to handle the backward compatibility. - Made the static compression and decompression code reside in the same class. nxcomp-2.0.0-68 - Improved the encoding of the X_RenderCreateGlyphSet and X_Render- FreeGlyphSet requests. nxcomp-2.0.0-67 - Modified the following session messages: From: "Session: Session starting at..." To: "Session: Starting session at..." From: "Session: Session terminating at..." To: "Session: Terminating session at..." - Improved the encoding of the requests creating or freeing an XID. This affects X_CreateGC, X_CreateWindow, X_CreatePixmap, X_Render- CreatePicture, X_FreeGC, X_DestroyWindow, X_FreePixmap, X_Render- FreePicture. - Added 'streaming' and 'backingstore' to the list of options used by agents. - Modified the info messages to print the ZLIB compression paramet- ers to be more compact and only use two lines. nxcomp-2.0.0-66 - When part of an agent, the proxy will wait for the NXTransDestroy() function to initiate the shutdown. nxcomp-2.0.0-65 - Starting from this version the NX client can control the behaviour of the client proxy by monitoring the messages marked as "Session". At the early initialization the proxy will print: "Session: Session starting at '...'. The ellipsis here represent the current timestamp, as reported by the POSIX function ctime(): Once the session is running, the proxy will print: "Session: Session started at '...'. At the time the session is being shut down, the proxy will print: "Session: Session terminating at '...'. Followed by: Session: Session terminated at '...'. The message "Info: Starting X protocol compression." is removed, replaced by the message 'Session started at...' with the same meaning. Once the message 'Session: Session terminated at..." is read from the output, the client can optionally wait for the proxy process to complete and terminate with an exit code 0. - The "Session" messages are not printed when the proxy is running on the NX server side, as part of an agent. nxcomp-2.0.0-64 - Reduced the default token limit from 32 to 24. The synchronizat- ion callback is triggered by the tokens going below half the li- mit. nxcomp-2.0.0-63 - Added -I/usr/include/c++ to makedepend. nxcomp-2.0.0-62 - The house-keeping process is now started upon the initialization, if the memory cache is enabled, and, on the X server side, when the remote actually requested access to the image cache. nxcomp-2.0.0-61 - Added a NX_HANDLER_SYNCHRONIZATION callback to NXTransHandler(). The handler is called when the agent can use the available band- width to synchronize X objects that are corrupted or incomplete. The reason can be: NX_SYNCHRONIZATION_BEGIN: The synchronizationis is allowed. NX_SYNCHRONIZATION_END: The synchronizationis must stop. The reason can be determined by agents by using the NXBeginSynch- ronization and NXEndSynchronization values defined in NXvars.h. - Bytes from 14 to 24 in the X_NXGetControlParameters reply report, respectively, the frame timeout, the ping timeout, the preferred image split mode and the split size threshold. nxcomp-2.0.0-60 - The following messages used by the NX client and server have been changed. The NX client and server should be modified accordingly. From: "Info: Shutting down the link and exiting." To: "Info: Shutting down the NX transport." From: "Info: End of session requested by remote proxy." To: "Info: End of NX transport requested by remote" From: "Info: End of session requested by agent termination." To: "Info: End of NX transport requested by agent." From: "Info: End of session requested by signal '...'." To: "Info: End of NX transport requested by signal '...'." From: "Info: Shutting down the link and exiting." To: "Info: Shutting down the NX transport." From: "Info: Waiting for cleanup timeout to complete." To: "Info: Waiting the cleanup timeout to complete." From: "Info: Waiting for watchdog process to complete." To: "Info: Waiting the watchdog process to complete." nxcomp-2.0.0-59 - Added more debug information regarding the timing of packets sent and received. - Added the possibility to redirect the log output to a file that can be later shared with nxssh running in "binder" mode. This is useful when testing the forwarding of the SSHD connection by nxssh to the agent. Activated by defining BINDER in Loop.cpp. nxcomp-2.0.0-58 - Fixed the unpack alpha procedure to only rely on the image's byte order. - Solved the X_PutImage encoding problem introduced in the 2.0.0-56 version and restored the compatibility with the 1.5.0 version. nxcomp-2.0.0-57 - Added a NXTransFile() function. It returns the name of the files used by the proxy for the current session. The type parameter can be: NX_FILE_SESSION: Usually the file 'session' in the user's session directory. NX_FILE_ERRORS: The file used for the diagnostic output. Usually the file 'errors' in the session directory. NX_FILE_OPTIONS: The file containing the NX options, if any. NX_FILE_STATS: The file used for the statistics output. The returned string is allocated in static memory. The caller must copy the string upon returning from the function, without freeing the pointer. - Fixed the PANIC in the handling of the abort split messages. - The encode and decode routines may write one byte past the nominal end of the encode buffer. This additional byte is now included in the payload. This is actually a bug, but harmless. nxcomp-2.0.0-56 - Added the X_NXAbortSplit request. It can be used to interrupt the streaming of all the messages currently in the split store for the specified resource. Depending if the split store did or did not contain split messages, the client will receive an immediate end-split or no-split notification. - Removed the split modes NXSplitModeEager and NXSplitModeLazy. The available modes are now NXSplitModeSync and NXSplitModeAsync. The mode NXSplitModeAsync replaces the two old modes and is now the default. Similarly to NXSplitModeEager, it makes the proxy split only messages that exceed a size threshold, but the threshold is generally smaller than in the previous versions. #define NXSplitModeDefault 0 #define NXSplitModeAsync 1 #define NXSplitModeSync 2 - Renamed X_NXSplit to X_NXSplitData, X_NXAbortSplit to X_NXSplitEv- ent. - The proxy now enters a drain loop, when in congestion state, and waits until the decongestion. Also the X channels are drained if blocked. - The select is restarted immediately if only the proxy is selected and the data read doesn't make a complete message. - The size of the shared memory segment used by the X server proxy is now negotiated at startup and the selected value is printed in the session log. - Added the option 'encrypted'. It tells the local proxy if it is running as part of a program providing encryption of the point to point communication. This is useful to determine if the proxy can block waiting for data or needs to yield to the client to let it handle the remote connection. nxcomp-2.0.0-55 - Fixed handleRestart() to correctly send the end-split event only after the split sequence has been closed by the client. - Fixed a possible memory error in the Keeper class occurring if the image caches were erased while in the middle of a loop. nxcomp-2.0.0-54 - Added a '--parent' option when running the client in dialog mode. This is the process id that needs to be signaled in the case the user chooses to do so. nxcomp-2.0.0-53 - Solved a bug in the abort split procedure. The procedure assumed that the split had to be at the head of the list. This may not be the case if the split was loaded from disk asynchronously, after a different split with the same checksum was recomposed. - By defining STRICT in Loop.cpp, the number of available tokens is set to 1. This can be used to trigger the congestion more often than it would possible when testing the software with the default settings. - Added more debug output to the procedures handling the streaming of the images. - Reset the channel's split pending and congestion flags in the fin- ish procedure. Not doing so caused some additional loops at chan- nel shutdown. - Fixed a bug on Cygwin that prevented the log files to be succes- sfully reopened when the size limit was exceeded. - Failures to write to the agent transport are reported sooner when the channel is finished. This saves a few more loops at session shutdown. nxcomp-2.0.0-52 - Started implementing a new handler to let the agent include arbit- rary data in the transport statistics. The parameter, in this case, is a pointer to a pointer to a null terminated string. The pointer is set at the time the handler is registered. The pointed string will have to be filled by the agent with its statistics data. For now, only the interfaces and the stubs exist. - Disabled the timestamp cache as it still causes rounding problems when calculating the current bitrate. nxcomp-2.0.0-51 - Removed SelectPackMethod(), not used anymore, from Pack.c. nxcomp-2.0.0-50 - Added the X_NXFinishSplit request. It forces the proxy to comple- tely transfer all the split messages for the given resource, and then notify the agent. - Fixed the statistics to not account the split bits to the control token. nxcomp-2.0.0-49 - Fixed a bug that caused the split timeout to be reset even if a different channel had splits to send. - Removed an error condition that arose when a split operation was requested by a different channel than the first to connect. The reason is that an auxiliary channel (like the one created by the server to store the keyboard configuration) can be the first to open a connection to the X server, then can come the agent, that is actually using the NX opcodes. The proxy will now simply print a debug message. - Removed a further error condition that was assumed by the proxy if a pending flush was detected while no channel was selected for output. This can actually happen if the channel was dropped and, in the meanwhile, the user requested the proxy statistics. nxcomp-2.0.0-48 - Added a 'type' parameter to NXTransFlush(). The new NX_FLUSH_IDLE type should be used by agents when they have finished handling all their clients and are going to wait for more input. This is the case, for example, of the X agent returning from WaitForSomething() after having set a small timeout to find out if there are clients ready, or the case of a RDP agent finding that there are no opcodes to read from the RDP server. The 'idle' flush should be requested also when the X11 agent is waiting for a split to complete. When the flag is used, the proxy uses the available bandwidth to encode more low-priority data, like the payload of images being streamed. nxcomp-2.0.0-47 - Added a new RGB image encoder. For now the encoder uses a static Z stream to compress the image data in the destination buffer and allows the agent to use the simplest encoding by still separating the alpha channel from the image data. The new encoder can be the the base for implementing color reduction by dithering or a color- mapped translation of the image similar to PNG, but without the PNG overhead and with the colormap being sent to the client using the NXSetUnpackColormap() opcode. - Put the routines implementing static deflating and inflating of a ZLIB buffer from the Compressor and Decompressor classes to Z.h and Z.cpp, so that they can be reused by the new RGB encoder. nxcomp-2.0.0-46 - Fixed a segfault arising when trying to start a child without the NX transport. nxcomp-2.0.0-45 - Undefined the MATCH directive. nxcomp-2.0.0-44 - Changed the encoding of the X_CreatePixmap, X_CreateGC, X_Render- CreatePicture, X_CreateWindow, RenderCreateGlyphSet.cpp. The Xid of the drawable is included in the checksum of the X_CreateGC and X_CreatePixmap message. This leverages the way GC and Pixmaps are allocated in the X agent to obtain better compression. - Removed the code handling the reset of the proxy connection. This functionality was unused since when the persistence is handled by the agent. - Added a specific section in the statistics output for the render extension. nxcomp-2.0.0-43 - Fixed a bug that could have caused the sending of multiple 'end split' messages for the same split sequence. - Added handling of the option 'strict'. nxcomp-2.0.0-42 - Added a missing channel switch directive when encoding abort split messages at the server side. This could cause a decoding error on the client. nxcomp-2.0.0-41 - Ensured that the link is flushed immediately on the X server side when important keyboard or mouse activity is detected in input. - Implemented a timestamp cache to avoid calling gettimeofday() when it is enough to use the last timestamp gotten from the system. The cache can be disabled by undefining CACHE_TIMESTAMP in Timestamp.h. nxcomp-2.0.0-40 - Added the karma delay field to the X_NXGetControlParameters reply. - Solved a bug with the decoding of split messages likely to happen only when using link type LAN. nxcomp-2.0.0-39 - Implemented separate flow control for the generic channels and the image streaming data. - Improved the split procedure to load a message from the disk cache if found after it was originally discarded because locked. nxcomp-2.0.0-38 - Renamed the 'shmem' option as 'shseg'. The 'shmem' option becomes specific to the agent and will be used together with 'shpix' option to indicate if, respectively, the shared memory extension and the use of the shared pixmaps must be enabled in the agent server. The new option will be instead used by the proxy to determine the size of the memory segment shared between the proxy and the X server. - Added the 'shmem', 'shpix', 'keyboard' and 'clipboard' to the list of ignored options. The 'keyboard' option should become a synonym of 'kbtype'. The 'clipboard' option will tell the NX agent if the clipboard support should be disabled for security reasons. nxcomp-2.0.0-37 - Ensured that, when running a raw session, the persistent cache is loaded and saved only once in the session lifetime. - Added the 'product' keyword to the list of recognized options. The option is ignored by the proxy, but can be used by the client or server to facilitate the support. nxcomp-2.0.0-36 - Added the remote version number in the X_NXGetControlParameters reply. - Solved a bug related to the removal of the split stores. - Removed the unused code dealing with the flush timeouts. nxcomp-2.0.0-35 - Added an alert to notify the user about the failure of the XDMCP session. nxcomp-2.0.0-34 - The active channels and the agent resources are now stored in a list container. - Added the X_NXFreeSplit request. The request is currently unused because the split store is freed automatically when it becomes empty, but can be useful in future if we want to implement other otpimizations. nxcomp-2.0.0-33 - There is now a split store for each agent resource. This allows the proxy to divide the bandwidth among the agent resources. - A simple round-robin load-balancing is implemented, dividing the bandwidth equally among all the split stores. - If there is a previous request being streamed for the same res- ource, cached messages are appended to the split store and then committed in the original order. This makes possible to stream the colormap and the alpha channel data even if the split invol- ves multiple PutImage or PutPackedImage operations in a single start-split/end-split sequence. - Implemented the commit store class. Made the commit store expand the message in the final buffer, instead of leveraging the mes- sage store. - Channels having a drop command pending are checked before trying to load or save the cache. This prevents a potential problem with the protocol violation error that could be reported by the client side proxy. - The current bitrate is now calculated at the time the transport actually writes the data to the network, not at the time the data is appended to the transport by the upper layers. The reason is that the data can be compressed by the stream compressor and so we can calculate the real bitrate only after having flushed the ZLIB stream. This fixes the wrong numbers that could be reported by the statistics in the previous versions. nxcomp-2.0.0-32 - More progress in the implementation of the new image streaming. This version has been tested to work when connecting to an 1.5.0 server. Due to the significant changes in the way the streaming works in the newer versions, split is never activated by a 2.0.0 server connecting to a 1.5.0 client. - Fixed the NXTransClose() function to check if the fd matches the NX agent connection. - Fixed the includes in Socket.h to compile on Solaris and MacOSX. nxcomp-2.0.0-31 - Changed the NX transport functions that didn't require a NX fd parameter to include one. The value can be NX_FD_ANY to signify any running NX transport. nxcomp-2.0.0-30 - More progress in the new implementation of image streaming. - This version is apparently compatible with the 1.5.0 client but needs further testing. nxcomp-2.0.0-29 - Implemented the NXTransHandler() interface and made the proxy use the callback, when it is installed, instead of the notifi- cation events. - Modified the agent transport to report an EOF condition as soon as the channel is dropped, instead of reporting the error only after the proxy shutdown. This allows the agent to handle its clients while the proxy is handling the grace timeout. - Added a test facility to verify if the client and server caches match at shutdown. - More advances in the new implementation of streaming. nxcomp-2.0.0-28 - Made the session shutdown faster, normally below 1 second. The grace time is now 500 ms once all channels have been dropped. nxcomp-2.0.0-27 - The house-keeping process will now run a number of iterations (currently 100) and then will exit. The proxy will check the exit code and, if it was requested by the child, will restart it. This is intended to keep the memory consumption low, as I noted that after a big number of iterations the performance of the allocator start to degrade quickly. nxcomp-2.0.0-26 - Started implementing the NXTransHandler() interface to let the X application hook a callback and be notified by the proxy when an event occurs. At the moment only congestion events are supported. The interface makes possible for the caller to provide a generic parameter. This parameter is passed back to the handler function. The parameter is expected to be the Xlib display pointer, given that the proxy doesn't have access to the Xlib structures. - Moved all the internal variables shared between Xlib, nxcompext and the X server in nxcomp. Declarations and function prototypes moved to NXvars.h. - Removed the NXGetCleanupParameters() and NXGetImageParameters() interfaces and the remaining references to the unused display buffer and image cleanup functions. - In this version the images streaming functionality is disabled. Because of that, expect compatibility problems when connecting to a different nxcomp version. nxcomp-2.0.0-25 - Forced the end of the session earlier if a shutdown has been re- quested by the local side but the remote proxy is not responding. nxcomp-2.0.0-24 - Changed the loop deleting the file items in Keeper to pick the first element instead of looping through the iterator. - More problems with the libstdc++ allocators. During the tests, I found that the Keeper class can leak industrial amounts of memory. Valgrind says that the objects that are not freed are the File items we allocate in collect(). As this looked suspicious, I log- ged the constructors and destructors and can say that everything is OK on our side. Unfortunately, by removing the spleeps used to let the program work silently in background, one can find that the memory occupation still grows by a MB every few seconds. This is less a problem on Windows, where the memory occupation remains almost constant. See the comment on __USE_MALLOC below. - Increased the maximum size of a message that the proxy is allo- wed to cache to 4MB. This makes possible to cache many replies (like the X_QueryFont replies) that often exceeded the previous limit. nxcomp-2.0.0-23 - Fixed a compatibility problem with the 1.5.0 that arose when encoding an image with caching and streaming temporarily disa- bled. - Removed the __USE_MALLOC define in Types.h because this is what the g++ developers want. I'll try to avoid flames and will not comment on this. - Added a --with-use-malloc configure option. This option should be used when building the release packages on GCC versions that support it. - Started fixing the commit procedure to be able to deal with the alpha channel. nxcomp-2.0.0-22 - Prevented channels from being dropped while in the middle of a read loop. - Added a small utility ensuring that the SIGCHLD we receive in the main loop can be always identified as pertaining to an our child. - Increased the space available for the control messages at the beginning of the encode buffer and ensured that there is always enough space for adding a token message, in the case this is required by the flush procedure. - Made all cache files readable only by the user. nxcomp-2.0.0-21 - Solved a bug in the streaming of images that caused the agent's clients to never resume when connected to an old proxy version. nxcomp-2.0.0-20 - Disabled again streaming of the alpha channel data. By enabling it, the agent randomly fails to repaint the exposed regions. The reason is to be investigated. nxcomp-2.0.0-19 - Implemented the split mode NXSplitModeSync. When this mode is selected, the proxy will not send data for the split at the head of the list until the remote peer has confirmed that the message is not available in the image cache. This is going to be useful in combination with the lazy encoding. - Improved the error detection in the case of failure when saving a recomposed split on disk. - Added X_NXSetUnpackAlpha to the set of requests that is possible to stream. nxcomp-2.0.0-18 - Enabled the 2.0.0 features that were disabled for test purposes. - Changed the LICENSE file to state that the software is only made available under the version 2 of the GPL. nxcomp-2.0.0-17 - Introduced a new channel of type 'slave'. Opening a slave channel should let the proxy fork a new NX client instance (or whatever is in the NX_SLAVE environment) and pass to it the channel's desc- riptors. This client instance should authenticate the peer, for example using the proxy cookie (see how this is implemented in nxsensor) and then negotiate the service to be used on the channel. This can be used to implement forwarding of arbitrary ports or ad- ditional services, like a simple file transfer protocol. For now the implementation is incomplete. Opening a slave channel will result in the remote proxy just dropping the channel connection. - The proxy is forcibly flushed at the end of each loop. This is a temporary fix until the agents are updated to flush the proxy link on demand. - Added a new alert to be used in the case the RDP session is closed and replaced with a new session using the same Windows account. - Added a forward declaration of class RenderMinorExtensionStore in file RenderExtension.h to compile using gcc 4.0.X. - Added removal of all .orig file when running a 'make clean'. - The code uses again the 2.0.0 style shutdown (the one waiting for the watchdog process to terminate). nxcomp-2.0.0-16 - Added the NXTransChannel() interface to allow agents to create new channels connected to the port that will be forwarded by the remote proxy. The function returns 1 on success, 0 if the NX transport is not running, or -1 in the case of error. On success, the descriptor provided by the caller can be used for all the subsequent I/O, including selecting the descriptor to check if I/O is possible. The type parameter not only tells to the proxy the remote port where the channel has to be connected, but gives a hint about the type of data that will be carried by the channel, so that the proxy can optimize the compression and schedule the traffic on the slow link. The type can be: NX_CHANNEL_X: The channel will carry X traffic and it will be connected to the remote X display. NX_CHANNEL_CUPS: The channel will carry CUPS/IPP protocol. NX_CHANNEL_SMB: The channel will carry SMB/CIFS protocol. NX_CHANNEL_MEDIA: The channel will transport audio or other multimedia data. NX_CHANNEL_HTTP: The channel will carry HTTP protocol. NX_CHANNEL_FONT: The channel will forward a X font server connection. Only the proxy running at the NX server/X client side will be able to create a X, CUPS, SMB, MEDIA and HTTP channel. A proxy running at the NX client/X server side can create font server connections. The channel creation will also fail if the remote end was not set up to forward the connection. To create a new channel the agent will have to set up a socket- pair and pass to the proxy one of the socket descriptors. Example: #include #include int fds[2]; if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) { ... } else { // // Use fds[0] locally and let the // proxy use fds[1]. // if (NXTransChannel(fds[1], NX_CHANNEL_X) <= 0) { ... } // // The agent can now use fds[0] in // read(), write() and select() // system calls. // ... } Note that all the I/O on the descriptor should be non-blocking, to give a chance to the NX transport to run in the background and handle the data that will be fed to the agent's side of the socketpair. This will happen automatically, as long as the agent uses the XSelect() version of the select() function (as it is normal whenever performing Xlib I/O). In all the other cases, like presumably in the agent's main loop, the agent will have to loop through NXTransPrepare(), NXTransSelect() and NXTrans- Execute() functions explicitly, adding to the sets the descript- ors that are awaited by the agent. Please check the implementa- tion of _XSelect() in nx-X11/lib/X11/XlibInt.c for an example. nxcomp-2.0.0-15 - Some changes aimed at finding a workaround to the unreliability of the font server connections. The X server seems to be really picky about the network latency, so we try to read immediately from the channel just being created, instead of waiting for a new loop. Additionally the proxy will keep reading from the font server connections even after the maximum length of the frame is exceeded, in the hope that it will be able to send a complete message in a single frame. - updated the various internal consistency tests run when the TEST or INFO flags are enabled. nxcomp-2.0.0-14 - It seems that the closure of the socket sometimes takes several seconds, on Windows, after the connection is broken, with the result that the alert can be shown long after the user has gone after the failed session. We now skip the closure of the proxy link on Windows if we are about to exit. - The code uses the 1.5.0 style shutdown (the one waiting for the term signal in the main process) to be able to test the library with the old server. To activate the new shutdown procedure be sure the define SHUTDOWN_COMPATIBLE_1_5_0 is unset. nxcomp-2.0.0-13 - Ensured that static members in channels are reset at the time a new proxy is created. - Adjusted the token parameters for link modes other than modem. nxcomp-2.0.0-12 - Handled compatibility with 1.X.X versions not sending the count field in the token. - The proxy link is now flushed automatically if the agent has not set the deferred flush mode. nxcomp-2.0.0-11 - Significant modifications to allow the proxy to keep data in the encode buffer across multiple loops, until the data is flushed because an explicit request by the agent or. Besides maximizing the amount of data sent in a single frame, this makes it possi- ble to move any packet coalescence strategy in the agent, while still using an 'immediate' flush policy. - Modified the procedure handling the proxy congestion. Each token now carries a counter which represents the multiple of TokenBytes data bytes that need to be confirmed by the remote end. The ser- ver side proxy will need to reply by including the value of the counter. When sending a new token, the tokens are decremented by the counter and the client side proxy enters in congestion when the available tokens becomes zero or negative. nxcomp-2.0.0-10 - Implemented specific encoding of the X_NXSetCacheParameters request. - Handled the compatibility with older versions not supporting the new request. In this case the request is handled at the local si- de, preventing the X requests to be cached or split, even without the cooperation of the remote side. nxcomp-2.0.0-9 - Modified the shutdown procedure to not rely on the TERM signal when the proxy needs to wait for the server to complete. We will now print the following message in the session log: Info: Watchdog running with pid 'n'. Info: Waiting for watchdog process to complete. The NX server will have to kill the process with the given pid to mandate the proxy shutdown. This solves the TR11C01192. - Added the X_NXSetCacheParameters request. It tells to the proxy how to handle caching of X requests, namely if requests should be stored in the memory cache, split in smaller data chunks and, in the case of images, saved on disk in the persistent image cache. The request affects all the X messages until the proxy is further configured. - Enabling and disabling loading and saving of persistent images due to a X_NXSetCacheParameters is still to be implemented. - Implemented font channel compatibility with older versions. If the font port is queried and the remote proxy doesn't support the tunneling of font server connections, an empty string is returned. - The option 'resize', used by agents, is now silently ignored. This solves the TR10C01061. - Added the missing reference to TR08C000981. nxcomp-2.0.0-8 - Discarded the first idea of setting the font path implicitly in the X server side proxy, by means of a multi-pass operation and added the NXGetFontParameters() request and reply. If the proxy has been configured accordingly, the request returns the X font path that can be set by the agent to tunnel the font server con- nections through the NX link. - Changed the parsing of the font server port to be able to handle the defaults. nxcomp-2.0.0-7 - More work on tunneling of font server connections. - The synchronization requests are now replied at the same time as a new request is received, by checking if the channel has already gone past the awaited sequence number. This avoids the delay caused by the previous versions in the case the expected reply had been handled asynchronously, inside the write loop. nxcomp-2.0.0-6 - The display is again passed to the client using the -display parameter. - The option 'type' is now ignored at X server side. Support at the X server side had been left for compatibility with older versions of the proxy. - Added direct support for more session types, namely 'unix-cde', 'unix-xdm', 'unix-console', 'unix-default'. The 'unix-console' and 'unix-default', similarly to 'unix-application', will now trigger the 'raw' mode. nxcomp-2.0.0-5 - Renamed Png.h and Png.cpp to Pgn.h and Pgn.cpp to avoid name clashes on Windows. - The distclean target now removes the autom4te.cache directory. nxcomp-2.0.0-4 - Added the 'kill=n' option to let the proxy add the process to the list of daemons that must be terminated at session shutdown. Multiple 'kill=n' options can be specified. The proxy will send to each process a SIGTERM signal before exiting. - Removed the code that forcibly disabled the RENDER extension on Solaris. Eventual compatibility problems will be now handled in the NX agent. - The path to the client executable is now assumed to be in the NX_CLIENT environment. If the variable is not set, the proxy will guess the location based on the target platform. This is part of the FR11C01215. nxcomp-2.0.0-3 - More code cleanup. Protocol step 5 and cache step 2 are assumed by default. This allows us to remove the various channel caches that are not used anymore. - Removed the outdated encodeBegin() and encodeEnd() methods. nxcomp-2.0.0-2 - Added compatibility with the 1.5.0 and 1.4.0 versions. Protocol version older than the 1.4.0 will cause the proxy to drop the connection. - Rewritten the version negotiatiation logic to better handle the compatibility between the local and the remote version. - Removed the conditional code handling the compatibility with the protocol encodings from 1 to 4. - Small adjustments to the session negotiation procedure to give more hints in the case of errors. A dialog is now issued if the remote proxy drops the connection due to a probable authentica- tion failure or when the remote proxy version is not compatible with our version. nxcomp-2.0.0-1 - Opened the 2.0.0 branch based on nxcomp-1.5.0-80. nxcomp-1.5.0-80 - Fixed a bug that prevented the Unix socket to be created in the right directory. This affected the ability of the server to run NX sessions without the agent encoding. nxcomp-1.5.0-79 - Solved a bug in the 15 to 16 bpp conversion for RDP sessions. - Corrected a typo in the PANIC message of Unpack16To24 function. - Optimized the 16 bpp RDP decompression routine. nxcomp-1.5.0-78 - Implemented the FR11C01215. The following environment variables are checked to determine the location of the relevant NX direct- ories: $NX_ROOT The root NX directory is the place where the session directory and the cache files are created. This is usually overridden by passing the 'root=' option. By default, the root NX directory is assumed to be the directory '.nx' in the user's home. $NX_SYSTEM The directory where NX programs and libraries reside. If not set, the value is assumed to be '/usr/NX'. Programs, libraries and data files are respectedly searched in the 'bin', 'lib' and 'share' subdirecto- ries. $NX_HOME The NX user's home directory. If $NX_ROOT is not set or invalid, the user's NX directory is created here. $NX_TEMP The directory where the X11 Unix Domain Sockets and all temporary files are to be created. $NX_CLIENT The full path to the nxclient executable. If the va- riable is not set, the nxclient executable will be run assuming that the program is in the system path. This can be useful on platforms like Windows and the Mac where nxclient is located in a different direct- ory compared to the other programs, to make easier for the user to execute the program from the shell. Other environment variables: $HOME The variable is checked in the case $NX_HOME is not set, null or invalid. $TEMP The variable is checked whenever the $NX_TEMP direct- ory is not set, null or invalid. $PATH The path where all executables are searched, except nxclient. If $NX_CLIENT is not set, also the client executable is searched in the system path. $XAUTHORITY This is the file containing the X11 authorization cookies. If not set, the file is assumed to be in the user's home (either $NX_HOME or $HOME). $LD_LIBRARY_PATH System-wide library search order. This should be set by the program invoking the proxy. - Extended the usage message to document the relevant environment. - Made the WaitChild() function interruptible. This allows the user to terminate the connection procedure immediately, by sending a signal to the proxy process while an abort dialog is shown in the foreground. This is part of the solution to the TR11C01216. - Implementation of the FR11C01215 is not yet complete. The client program and the nxauth/xauth are still searched using the previ- ous logic. In particular, the NX_CLIENT variable is ignored. nxcomp-1.5.0-77 - Corrected the typo in the alert dealing with VNC authentication failures. This solves the TR11C01199. nxcomp-1.5.0-76 - Added the missing initialization in the client and server proxy constructors. nxcomp-1.5.0-75 - Starting from this version the build procedure will automatically create a DLL on Windows. - Disabled exceptions in the compiled code. - Disabled run-time type information to further reduce the size of the executable. - Compiling with GCC 4.0.0 and -fno-exceptions produces a warning in some files, due to control apparently reaching the end of a non void function. This is bogus and has been confirmed to be a GCC 4.0.0 bug solved in more recent versions. nxcomp-1.5.0-74 - Imported changes from nxcomp-1.6.0-4. - Solved a compilation error on Solaris and MacOSX platforms. - The 'font' option now accepts either a numeric value (for a TCP port) or a 'unix/:port' or 'tcp/:port' specification. In the case of Unix ports, we assume that a port specification "unix/:7100" corresponds to the "/tmp/.font-unix/fs7100" socket and a port like "unix/:-1" corresponds to "/tmp/.font-unix/fs-1". - An absolute file path is also accepted as a valid socket. This is useful to test the forwarding of the font server connection, for example by running a normal X client and pointing the socket to the X server. - Protocol step 7 is now only assumed when connecting to a remote proxy version 1.6.0 or later. nxcomp-1.5.0-73 - Use the include files from nx-X11 if the nx-X11/include directory is found. The previous configure checked the presence of nx-X11/ exports/include, that might not be built at the time this library is compiled. nxcomp-1.5.0-72 - Fixed a problem on AMD64 due to the size of the area pointed by the argument of NXTransReadable(). The ioctl() requires a pointer to an int, at least on Linux. The original _X11TransBytesReadable() function simply calls the ioctl() by passing the pointer that is provided. Returning the value assuming a pointer to a long crashes some applications, among them xterm. Now NXTransReadable() follows the same schema of the ioctl() call and stores the result assuming a pointer to an int. nxcomp-1.5.0-71 - Ensured that the NX trasport is destroyed on NXTransExit() even if the parent has overridden our signal handlers. - Added attribute 'noreturn' to NXTransExit(). - Small change to the shutdown procedure to send the "Waiting for a further signal" just after the watchdog is started. nxcomp-1.5.0-70 - Added the definition of protocol step 7. The new protocol step is implicitly assumed when activating the font server channel, so that NX clients and servers may decide what to do by verifying the patch version of the peer software. This makes possible to activate the feature without stepping up the 1.5.0 version of the software. nxcomp-1.5.0-69 - Added the -fPIC GCC flag when compiling on AMD64 architectures. - Small changes to configure.in to have specific CFLAGS. - Created a new configure using autoconf 2.59. - Added the 'mask=n' option to determine how channel ids are distri- buted between client ans server. By default, channels whose ids are multiple of 8 (starting from 0) are reserved for the client side. All other channels can be allocated at the NX server side. - A check is missing on the protocol supported by the remote side, so it is likely that mixing this version with older 1.5.0 will not work. - The release has the debug logs enabled. nxcomp-1.5.0-68 - Added provision for opening new channels on both client and server side. This required small changes to the way channel ids are mapped and retrieved, so that both sides now support bidirectional mapping of file descriptors to channels. - Made the code handling allocation of new generic channels to reside in the base Proxy class. nxcomp-1.5.0-67 - Renamed the 'embedded keyboard' channels as 'aux' channel. - Renamed 'samba' channels as 'smb'. - The 'samba' and 'smb' options can now be used interchangeably. The same applies to the 'keybd' and 'aux' options, used to set up the auxiliary X channel. - Added a new font channel. The channel is used to forward X font server connections from the X server on the NX client to the remote NX server. The channel is not yet functional and requires change to the channel allocation mechanism. - Added a common interface to create new listening sockets. This is currently used for additional services, but not for the X display sockets. - Simplified the interface used to accept new connections to channels being forwarded. - Removed the -V option as nxproxy is not using dlopen() anymore. - Removed the inclusion of ClientChannel.h in Proxy.cpp that caused test symbols to be reverted to undefined. nxcomp-1.5.0-66 - Made failures setting the IPTOS_LOWDELAY on the proxy socket to cause a warning, instead of an error. - Made the clean target delete the Cygwin specific libraries. - Updated the configure and Message.h to deal with GCC 4. Solves the TR08C000981. nxcomp-1.5.0-65 - Removed the warning issued on parsing the agent option 'rootless'. Solves the TR08C00959. - MacOSX 10.4 defines socklen_t. Made the definition conditional so that we can still support older versions. Solves the TR07C00926. - Updated the ChangeLog to include references to the solved TRs. nxcomp-1.5.0-64 - Imported the 1.6.0 changes in the maintenance 1.5.0. - Removed a wrong assertion that might cause the session to fail when the software was compiled with TEST enabled in Proxy.cpp. - The nxclient dialog process is signaled with SIGKILL on Windows, as the SIGTERM is ignored. This solves the TR07C00929. - Ensured the JPEG error flag is always set before jumping out of the Jpeg decompression. - Skipped errors encontered setting the TCP_NODELAY flag on Mac. Solves TR08C00940. - Few cosmetic changes. nxcomp-1.5.0-63 - Ensured that the parent is checked often in the keeper process, so that, in the case of a premature death, the child is exited earlier. nxcomp-1.5.0-62 - Some performance tuning of the LAN and WAN link modes. nxcomp-1.5.0-61 - Fixed the problems arisen with loading or saving the image files on Windows by forcing the streams to be opened in binary mode. These problems have been triggered by the recent Cygwin upgrade. - Removed the logs that had be left enabled for test purposes. nxcomp-1.5.0-60 - Made all errors encountered while unpacking an image just print a warning in the session log. Such errors are not fatal but the client monitors the state of the session at startup, so that by marking image decompression errors as such may cause the session to be aborted. - Marked as warnings also errors encountered by trying to load an image from disk. nxcomp-1.5.0-59 - When detected, the CTRL+ALT+SHIFT+ESC sequence is removed from the event stream. - Also modified the message in the session log to issue a warning, instead of an error. nxcomp-1.5.0-58 - Added a setjmp() before yielding the control to the JPEG library. This makes possible to recover from the JPEG decompression errors that were previously fatal. - In the commit split request, the client id is now encoded by the client channel beside the propagate flag. The client id is igno- red by the decoding party. As in the old protocol versions, the committing client is taken from the client id that was originally sent together with the packed image. - Fixed the compilation problem when defining OPCODES in Misc.cpp. - Skipped the synchronous flush of the proxy link when connecting to a previous NX version. - Ensured that the new alerts are only requested when connected to a compatible proxy. - Ignored the option 'fullscreen', targeting the agents. nxcomp-1.5.0-57 - Added more alerts and changed the name of those dealing with the resume of a session. - Added the MIXED define. When set, the proxy will let all logs go to the standard error. This is useful to interleave the Xlib log output with the proxy output in a single file. - Added a new alert to report an I/O error at agent reconnection. This is one of those alert that can't be actually shown, but are included to offer a consistent view to the agent implementation. nxcomp-1.5.0-56 - Added the split mode NXSplitModeSync. By selecting this mode, the proxy will try to empty the split store immediately, until all messages marked in such mode will be synchronized. The im- plementation is left to future versions. #define NXSplitModeSync 3 - Added the messages specific to RDP and VNC sessions in NXalert.h. Included are also a few alerts related to changes to the color depth and desktop geometry that may be eventually mandated by the remote server. - Corrected a typo in NXalert.h nxcomp-1.5.0-55 - Added the 'mode' field in the X_NXStartSplit request. It determi- nes the strategy that the proxy will adopt to handle the image. If set to 'eager', the proxy will only split the messages whose size exceeds the split threshold (the threshold can be found in the X_NXGetControlParameters reply). If mode is set to lazy, the proxy will split any image that it is not able to find in its cache. The opcode and the two available modes are defined in NXproto.h, currently: #define NXSplitModeDefault 0 #define NXSplitModeEager 1 #define NXSplitModeLazy 2 - All requests related to image streaming now carry a 'resource' id. The id is currently ignored by the proxy in the case of X_NXCom- mitSplit requests. - Added a new NXSetSplitMode() request. It determines the strategy that the proxy will adopt to stream the images. If set to 'eager' the proxy will only split the messages whose size exceeds the split threshold, otherwise it will split all the images, regard- less their size. This is in preparation of the lazy encoding in agent. - Slightly decreased the startup timeout compared to the value it had in the 1.4.0. This timeout is used to retain the images used during session startup in the persistent cache. nxcomp-1.5.0-54 - Modified the text of the begin-reconnection alert to mention the speed of the connection detected by the agent. nxcomp-1.5.0-53 - Reworked the handling of the abort-split events issued by the re- remote proxy so that we now finalize the procedure and restart the client asynchronously, in the middle of the write loop. nxcomp-1.5.0-52 - Rewritten the internal interface to the notification events. There are now five different split notification events: NXNoSplitNotify The operation didn't cause any actual split. The client can be immediately restarted. NXStartSplitNotify A split is ongoing. The client should be suspended until the end of the split. NXCommitSplitNotify A complete request has been recomposed. The client can commit the given request to the X server. NXEndSplitNotify The split operation was duly completed. The client can be restarted. NXEmptySplitNotify No more split operation are pending. The agent can use this information to implement specific strategies, requiring for example that all messages have been recomposed at the remote end. This can include updating the drawables that were not synchronized because of the lazy encoding. - Removed the X_NXSync and X_NXKarma operations, not used anymore by the NX agents. - Increasing or decreasing a timeout makes it respectively equal to the base timeout or to the base timeout / 4. nxcomp-1.5.0-51 - Improved the mechanism by which synchronization replies are sent to the X client proxy. - Using the leftPad field as passed by the nxagent 1.5.0-58 doesn't seem to work with the MIT-SHM. The workaround is to disable the MIT-SHM operation in the case of 1 bpp images. - Removed the limitation on the size of the shared memory segment that had been introduced for test purposes. nxcomp-1.5.0-50 - Solved compilation problem when --with-info was not given. nxcomp-1.5.0-49 - Improved support for color matching from 15 and 16 bpp to 24 and 32 bpp in RDP sessions. - Solved TR05C00910. This was about "shadows" that might appear in RDP sessions when small elements like tooltips were drawn. nxcomp-1.5.0-48 - Added a 'commit' field in X_NXCommitSplit request. When zero, the X server side proxy will unlock the message in the message store, without actually committing the image to the X server. The new field required changes to the encoding. This means that this ver- sion is not compatible with older 1.5.0 nxcomp releases. - Added a new control message. This is used by NXTransFlush() to for- ce a roundtrip between the proxies and speed-up the handling of the synchronous replies. nxcomp-1.5.0-47 - Made the timeouts dynamic, based on the user input and the current bitrate. - Set the available tokens to 8. This allows for 16 KB to be sent on a modem before having to wait for a reply. - Added provision for removing a dialog on the remote end. This can be used, for example, to get rid of a message that was displayed by the agent at the beginning of a lengthy operation. - Fixed a fault that arose when trying to run the proxy on a display whose name began with the 'nx' string. - Running a client or server session on a display host beginning with 'nx' should be now possible. - Flush is now always immediate when there is already data queued in the TCP socket. nxcomp-1.5.0-46 - Created the NXalert.h header from Alert.h to share the alert codes with the agents. The new NXTransAlert() interface can be used to start an alert on the remote side. - Created the NXTransParse*() and NXTransCleanup() wrappers. - Temporarily using the single-dash style when passing parameters to nxclient until the double-dash style is fixed. nxcomp-1.5.0-45 - Motion events are now flushed at the time the X server channel is sending the token reply. - Renamed NXDialog() to NXTransDialog() and added a window ID parame- ter, to be used by the agent when starting the "pulldown" dialogs. - Also renamed the other NX utility functions. nxcomp-1.5.0-44 - Solved a bug that prevented auxiliary X connections from working with the agent. - Added the missing check on the proxy pointer in HandleTimer() as it may be actually called before the proxy is created. nxcomp-1.5.0-43 - Started tuning the performance. For now, the available tokens are set to an improbable value of 10000. nxcomp-1.5.0-42 - Disabled the X server side remote expose events for test purposes. This is intended uncover the display problems introduced by the new handling of exposures in the X11 agent. - Fixed some timestamp related functions to avoid rounding problems. - Moved the timestamps of split, frame and flush from the Control to the Proxy class. - Made the X server side proxy to skip updating the counters related to token management. - Made also the X server side use deferred flushes. - Modified handleFrame() to manage both pings and data frames and send the token before the actual write operation. - Cleaned the pseudo-files used to load the control parameters. - Cleaned up the FIXME's in the lower layers. More FIXME's remain in the frontend classes. - Added a further counter to the statistics tracking the number of network writes performed on the proxy link. nxcomp-1.5.0-41 - Now the watchdog process doesn't kill the parent. The termination event is determined by monitoring the child. This is advisable to avoid interfering with the signal handling in agent. - The house-keeping process is not started if differential compres- sion (and thus caching of images) is disabled. - Wait for the watchdog to terminate before starting the house-keep- ing process. - Fixed the problem of gray stripes in white areas when using 15bpp RDP compression. This solves the TR06C00916. - Added a log in Message.cpp to tell when an image is removed from the memory cache. - Removed most of the logs. Only left the logs directly related to the deferred flushes. - Removed a warning due to a missing definition in NX.h. nxcomp-1.5.0-40 - Added the signal '12' to the set of blocked signal on Cygwin. This is what is delivered to the application when it tries to create a shared memory segment when the cygserver is not running. The call later returns "Operation not implemented", only useful if you have survived to the signal. - Modified the configure script to not link against cygipc. - Improved error handling in the negotiation phase. - Reset the last signal received when aborting a connection attempt. Not doing that might cause a warning. nxcomp-1.5.0-39 - Both channels and proxy can now deal with incomplete messages by waiting for the full data to become available. This is not going to work when the transport is connected to an agent that is run- ning in-process, so the timeout must be really small. - Made the main loop more compact by letting a single function read from proxy and channels. The same change applies to writing to descriptors that are reported to be ready. - Optimized the loop to report as ready the agent descriptors that have become available after the proxy execution phase. - Now the proxy will try to asynchronously read from its descriptor after having read from the channel. - Unified the routine setting the routines setting the read and write descriptors and the timeouts. - Unified the routine handling the channel events. - Reintroduced the last ping timestamp to avoid sending more than a single ping every PingTimeout milliseconds. - The cleanup procedure now waits for the watchdog and house-keeping processes to terminate. - Reverted the change occurred in 1.5.0-35 about the byte-ordering problem in decompression of PNG and JPEG images. - More coding about deferred flushes. - Relevant debug logs are enabled in the main loop, in the proxy and in the channel classes, so use only for testing. nxcomp-1.5.0-38 - Started the implementation of deferred writes based on the new NX transport. - Solved a problem with the statistics file affecting the Windows platform. Basically it seems that Cygwin or the stdc++ library don't deal correctly with files that are reopened just after having unlinked them. - Implemented support for 15 bpp RDP bitmaps. - Added "rdpcolors" and "rdpcache" to the list of parameters that are ignored in ParseEnviromentOptions(). - This version has lot of logs enabled and some forced cleanups in the case of unexpected conditions. Avoid to use it for long-last- ing test sessions. nxcomp-1.5.0-37 - Now splitting of images is fully disabled when running with link LAN. Previously the split was not executed by the proxy, but the agent was configured to send the start/end sequence. - Added the check protecting the proxy from token underflow. In the previous code the check was left out to verify the conditions un- der which this event might be encountered. - NXTransWrite() and NXTransWriteVector() allocate a context before calling handleRead(), so they can correctly return in the case of a cleanup. nxcomp-1.5.0-36 - Modified the channel write loop to interleave abort split events between the writes to the X server socket. nxcomp-1.5.0-35 - Solved a byte-ordering problem in decompression of PNG and JPEG images. - Changed the handleToken() interface to make explicit if a token must be issued because of a ping. - Small fix to prevent a warning in Jpeg.cpp when TEST is enabled. nxcomp-1.5.0-34 - Solved a few problems introduced by the rewrite of the read loops in channels. - Added definition of NXCollectInputFocusNotify. nxcomp-1.5.0-33 - Implemented a new control flow system based on tokens exchanged bet- ween the proxies. When the clint side runs out of tokens it stops sending data until a new token is returned. Tokens are sent whenever the data written to the socket exceeds the amount of data set for a scheduled write, so the proxy is free to send a number of smaller frames before running out of tokens. - The reason the new system is introduced is because the old method had two outstanding problems: - It worked very well when the proxies were directly connected, but couldn't reliably detect a link congestion when tunneled over SSH. - In the attempt of reducing the data queued to the TCP layer, it didn't leverage all the available bandwidth. - Moved the final check on the state of the session at the end of execution stage, instead of the beginning. The signals sent by the user to request the statistics could be reset before the proxy had entered the right procedure. - Added the session type 'unix-rootless', to be used for single apps run with the agent in rootless mode. nxcomp-1.5.0-32 - Modified the main loop to skip the select when I/O is possible on any of the agent descriptors or the proxy link. - Now the house-keeping process is allocated in the heap instead of the stack. nxcomp-1.5.0-31 - The server channel now performs asynchronous reads from the display by interleaving them with data decoded from the remote proxy. - Improved the handling of the memory-to-memory transport. nxcomp-1.5.0-30 - Redesigned the handling of the congestion events to work more re- liably and to report the events to the remote peer earlier. - Increased the size of the shared memory segment when the MIT-SHM extension is activated. - Solved a bug that prevented the ping timeout to work as expected. - Ensured that, when entering the main select(), neither the chan- nels or the proxy have pending messages in the read buffers. - There is now only a single case where we can have pending messa- ges, namely in the handling of the MIT-SHM events. - Solved a bug introduced by the 1.5.0-29 that made the proxy en- ter the select with a null timeout. - Renamed LINK_TYPE_AUTO to LINK_TYPE_NONE in NXproto.h. nxcomp-1.5.0-29 - Now encoding data from the agent descriptors happens in the same context as data is written to the buffer. - Optimized the handling of the congestion events to avoid running further unneeded loops. - Added timer handling utilities. nxcomp-1.5.0-28 - Added code providing information about the reason of the failure encountered connecting to the local X server. This greatly helps when troubleshooting X authorization problems. - On connection failure a warning message is printed in the session log. - Removed the warning that was previously printed when the MIT-SHM extension failed to be initialized on Windows. Shared memory has problems on Cygwin and it currently doesn't work in NXWin. - Reworked the message printed in the case of link failure. nxcomp-1.5.0-27 - Solved a bug in the connection procedure introduced by 1.5.0-25. nxcomp-1.5.0-26 - Transformed the errors printed on failure of the fork() creating the children into warnings. This can happen quite often on Win- dows, due to well known Cygwin problems. See also the ChangeLog entry for nxcomp 1.4.0-28. This patch closes the TRSL052278 but it's obviously not a long-term solution. - Set the sticky bit when creating the '/tmp/.X11-unix' directory. - Modified the Makefile.in to remove the *.out.* files generated by Valgrind. - Updated the README files. nxcomp-1.5.0-25 - Changed the directory where the client for the Mac is searched if it is not found in the system path. - Modified Auth.cpp to use nxauth also on the Mac. - Reworked the procedure showing the alert dialog when a timeout is encountered in the initial connection. - Removed the experimental code from the official 1.5.0 branch. nxcomp-1.5.0-24 - Implemented more experimental classes. nxcomp-1.5.0-23 - Added the NX_SIGNAL_FORWARD action to NXTransSignal(). This can be used to let the proxy call the original signal handler of the agent after having blocked the signal. nxcomp-1.5.0-22 - Ensured that we always have a context, even before creating the transport. - Suppressed the error message printed when passing the -h option. - Added the experimental code that is currently under development. nxcomp-1.5.0-21 - Enabled the fake X cookie authentication. This requires checking the remote proxy version to verify that the server supports the new authorization mechanism. - It's worth noting that the X client side proxy doesn't care which cookie is sent over the connection. The problem is that clients connecting to 1.4.0 servers don't have a method to force the ser- ver to use the fake cookie. This means that we have to solve the problem by letting the proxy check the remote version so that it can omit to replace the cookie when connecting to older servers. nxcomp-1.5.0-20 - Added the NXTransCongestion() function. It returns true if proxy is in congestion state. - Removed an incorrect warning that was printed when calling force() for the memory-to-memory transport. The agent could actually re- quire multiple loops to read all data queued for it. nxcomp-1.5.0-19 - Small optimization in NXTransReadable() to run a new NXTransConti- nue() loop only after at least RetryTimeout milliseconds are pas- sed since the last call and still no data is available. This cuts the number of unneeded loops to 1/4th of the total, probably more, on faster machines. - Added NXCollectGrabPointerNotify to NXproto.h. nxcomp-1.5.0-18 - Minor changes to NXTransContinue(). nxcomp-1.5.0-17 - Moved respawning of a new nxclient instance in the cleanup procedu- re. This ensures that the respawn is executed whatever is the rea- son of the session shutdown. - Added a method to force closure of a given channel in proxy. - Removed code handling the special case triggered on Windows by the presence of a NX_SESSION variable in the environment. nxcomp-1.5.0-16 - Added the NXTransSignal() function to let agents tell to the proxy how to handle the standard POSIX signals. Given the SIGINT signal, for example, the caller can specify any of the following actions. NX_SIGNAL_ENABLE: A signal handler will have to be installed by the library, so that it can be intercepted by the proxy. NX_SIGNAL_DISABLE: The signal will be handled by the caller and, eventually, forwarded to the proxy by calling NXTransSignal() explicitly. NX_SIGNAL_RAISE: The signal must be handled now, as if it had been delivered by the operating system. This function can be called by the agent with the purpose of propagating a signal to the proxy. As a rule of thumb, agents should let the proxy handle SIGUSR1 and SIGUSR2, used for producing the NX protocol statistics, and SIGHUP, used for disconnecting the NX transport. - The following signals are blocked by default upon creation of the NX transport: SIGCHLD These signals should be always put under the control SIGUSR1 of the proxy. If agents are intercepting them, agents SIGUSR2 should later call NXTransSignal(..., NX_SIGNAL_RAISE) SIGHUP to forward the signal to the proxy. SIGINT These signals should be intercepted by agents. Agents SIGTERM should ensure that NXTransDestroy() is called before exiting, to give the proxy a chance to shut down the NX transport. SIGPIPE This signal is blocked by the proxy, but not used to implement any functionality. It can be handled by the NX agent without affecting the proxy. SIGALRM This is not blocked by the proxy, but could be used in future. SIGVTALRM These signals are not used and should not be used in SIGWINCH future versions of the library. SIGIO SIGTSTP SIGTTIN SIGTTOU - By calling NXTransSignal(..., NX_SIGNAL_DISABLE) nxcomp will res- tore the signal handler that was saved at the time the proxy hand- ler was installed. This means that you should call the function just after the XOpenDisplay() or any other function used to init- ialize the NX transport. nxcomp-1.5.0-15 - In NXTransContinue(), if the transport is gone, return immediately, that is without having to wait until the NXTransSelect() timeout. - Ensure that NXTransCreate() has a jump context, just in the case a subsequent operation would cause a cleanup. nxcomp-1.5.0-14 - Solved a problem with requests left in the agent's buffer when run- ning the NX transport. The agent could have enqueued data to our side and checked the available events but requests could not be written to the proxy because proxy might not have had a chance to enter a new select. We found that this behaviour was triggered by _XEventsQueued, so now a new loop is forced when agent is calling _X11TransDataReadable. The procedure can be optimized, by avoiding an expensive loop when no critical I/O is pending. - Added a few additional logs to ClientChannel and ServerChannel. nxcomp-1.5.0-13 - Added the code handling the special cases of an user not specifying a proxy cookie or the case of the X authorization file not contain- ing a value matching the display. In the first case we'll forward the same cookie that was feeded to the proxy, in the second case we will forward to the X server a random generated cookie, similarly to what SSH does in this same condition. - Rewritten the command line parser. Removed all the command line options parsed on behalf of nxproxy except: -C Specify that nxproxy has to run on the "X client" side, listening for connections and impersonating an X server.\n\ -S Specify that nxproxy has to run in "X server" mode, thus forwarding the connections to daemons running on the client.\n\ -V n.n.n Request nxproxy to load the given nxcomp version. This option is only present on Solaris and Linux. -v Print version information. host:port Put at the end, specifies the host and port of the listening proxy. value=name Set the NX option to the provided value. Multiple NX options can be specified in the DISPLAY environment or on the command line, by using the nx/nx,name=value notation. - The above information is printed on the console when incurring in a parse error, together with a list of the available option=value parameters. - Renamed the 'log' option to 'errors'. This makes sense as the de- fault name for the log file is actually 'errors'. - Now the "Established X server connection" message is printed to the session log only after the X connection has passed the X auth- entication phase. This means that the NX client should become able to show the details of the session log whenever the session fails due to a cookie problem. - When selecting the additional services without specifying a port, the client proxy will now automatically forward the connections to the corresponding well-known ports of the CUPS, SMB and HTTP servi- ces. Embedded X keyboard connections will be automatically forward- ed to the same display port used to connect to the X server. The user will still have to specify the port to be used for the media connections as we don't have a suitable well-known port. - Starting from this version, connections to the keybd port will cre- ate real X connection channels. This is required to let connections leverage the fake authorization cookie. - By testing the forwarding of keybd connections I found that, when letting X clients connect to the port, it is required to provide the X cookie for the unix display. Adding only the TCP cookie will not work. For example, by creating a cookie as in: xauth add localhost:2009 MIT-MAGIC-COOKIE-1 6f...f4 And running: xterm xterm -display localhost:2009 You will get the following error: Xlib: connection to "localhost:2009.0" refused by server Xlib: No protocol specified Adding also the unix cookie will fix it: xauth add localhost/unix:2009 MIT-MAGIC-COOKIE-1 6f...f4 This seems to be a Xlib problem, with Xlib trying to get the cookie for the UDS port even if the TCP port was requested by the user. - Fixed a bug that prevented the nxclient dialog to be displayed when the session was abruptedly shut down. - Fixed the compilation error on Apple MacOSX due to the sa_restorer field in sigaction. As long as this field is present on Linux we will keep following the safer route and will set it explicitly to NULL. - Included what needs to be included in Process.cpp to compile with older gcc. nxcomp-1.5.0-12 - Implemented a replacement for the popen() and pclose() that do not rely on a shell to run the command. They were required on Windows, where we don't ship a suitable shell in the install. - Removed code forcing the PATH to include the bin directory on Windows. nxcomp-1.5.0-11 - Forced Auth.cpp on Windows to have the directory containing the nxauth executable in the PATH. This is just for testing, until nxclient is fixed. - Fixed compilation errors on Cygwin and Sun. - Removed the NX_FORCE_* stubs. nxcomp-1.5.0-10 - Modified the memory management policies in ReadBuffer to fit all the available bytes in a single buffer allocation. - The locateMessage() methods now give hints on the amount of data that has to be read. - The read loop in channel now doesn't yield in the case of prio- ritized messages. This is experimental. - Removed the check on isTimeToYield() between encodings of multi- ple messages. This is aimed at reducing the risk of leaving pen- ding messages in channels. - Modified the channels' read loop to always read all the available data. - Disabled the log output that was selected when compiling with the configure option --with-info. This leaves space for other log out- put to be selected for more up-to-date scopes. - Implemented the NXTransReadVector() and the NXTransWriteVector() functions to replace READV() and WRITEV(). - Implemented memory-to-memory communication between the agent and the NX proxy by making use of the NXTransAgent() interface in the nx-X11/lib/X11/Xtranssock.c file. - Added a check in NXTransSelect() for the EBADF and, on Solaris, the EINVAL errors. It can happen in the X11 code that a descript- or has become invalid before it is removed from the managed set - Rewritten the signal handling functions to restore the old actions and masks when the NX transport is destroyed. - Added a NXTransAgent() function to let agents tell the proxy which descriptor must be used for the controlling connection. Setting a controlling connection has the effect of disabling further X client connections and makes the proxy terminate when the channel is shut down. - Solved a problem with setting the initial timeout in the select(). - Modified the Makefile.in to not include -Wno-deprecated when compi- ling C programs. nxcomp-1.5.0-9 - Fixed a problem that prevented the 1.5.0-8 to work on the NX server side. - This version has NX_FORCE_NULL_LISTEN_OPTION and NX_FORCE_NEW_SES- SION_OPTION undefined, so it should work in a way that is compati- ble with the old nxcomp. nxcomp-1.5.0-8 - The new code comes with a preliminary integration of nxcomp with SSH. It is now possible to create the NX transport by just calling the "switch" command as in the following example: NX> 299 Switching connection to: NX options: ... - Other possible forms for the NX switch command are: NX> 299 Switching connection to: NX mode: ... NX> 299 Switching connection to: NX mode: ... options: ... Or just: NX> 299 Switching connection to: NX The "mode" parameter is there to provide a way to run both enc- rypted and unencrypted connections. Possible values are "encrypt- ed", "unencrypted" or "default", the latter being an alias for "encrypted". Unfortunately I was not able to test unencrypted connections, so this may or may not work. - The top-level process can create the NX transport layer by calling NXTransCreate(). The user has to set up a socket pair and pass the higher descriptor to nxcomp. nxcomp will later monitor its end, by reading and writing NX-encoded traffic. The user has to call the NXTransExecute() function as often as it is possible, by letting first NXTransPrepare() combine the sets of NX descriptors with the descriptors that are used inside its process. A custom NXTransSel- ect() is provided to optionally replace the original select(). This function saves the original error code and the number of selected descriptors upon exit, so the user can call it, restore the original values as they were returned by the select() and run the rest of the loop unmodified. - Future versions of the library should provide appropriate methods for passing data to and from the proxy by means of a memcpy(), so that it will be possible to remove the even minimal TCP overhead. - Note that integration is far from complete. More work is required especially to manage the shutdown cleanly, in a way that gives to SSH a chance to free its resources, and on adding facilities for handling SSH and NX signals in a single function. - Rewritten the initialization procedure to make possible to run the proxy in-process in an arbitrary connection manager, like SSH or a HTTP utility. The same functionality can be used to embed nxcomp in the NX agents, so that nxcomp has not to run in a separate process. - A new state-machine handles the advances through the connection stages until the remote proxy (or the forwarder process ) is auth- enticated, options have been negotiated and the session is started. - The option "session" is now used to pass the name of the session file to the proxy. The parameter was previously used to pass the literal name of the session, as set by the user, and was ignored by the proxy. By default the session log is the standard error of the process. It is anyway required to set the option when running inside SSH, otherwise output will go to the same file as the SSH output. In the NX client this would be the 'sshlog' file. - This version can be easily crafted to test the new integration by setting the following define: NX_FORCE_NULL_LISTEN_OPTION This makes possible to test the nxcomp/nxssh integration by using any production 1.4.0 client. To run this version you are required to use nxproxy 1.5.0-4 and nxssh-1.5.0-6. - New functions handling enabling and disabling signals, based on sigprocmask(). - Improved error reporting when failures are encountered while ne- gotiating the session. nxcomp-1.5.0-7 - Caches are saved with a version identifier 1.4.0 so that they are not discarded after upgrading the software to the 1.5.0. - Made values of T_* enumerations to be all lower case. - Updated copyright to year 2005. - Started working on an interface for running nxcomp in-process, as a additional transport layer of nxssh or nxagent. nxcomp-1.5.0-6 - Modified the configure script and the makefiles to not include the -Wstrict-prototypes and -Wmissing-prototypes compilation flags. The -Wnested-externs and -Wmissing-declarations flags are not included when using GCC 3. - Removed the initial newline from string "NXPROXY - Version" printed at program startup. - Made X authentication compatible with 1.4.0 clients. This is a tem- porary solution while code is updated to handle the new X authori- zation scheme. nxcomp-1.5.0-5 - Added an Auth class to handle the X authentication. The NX server should now use the same proxie cookie that is passed by the client at session startup. The X server side proxy will forward the autho- rization credentials by replacing the fake cookie with the real co- okie, as it is read from the auth file using the xauth utility. The implementation is based on the corresponding code found in the SSH client and comes with the same limitations: only MIT-MAGIC-COOKIE-1 cookies are checked and the authorization file is read only once, at the time the instance is initialized. If the auth file changes along the life of the session, the old cookie will still be used. This works with X servers because of an undocumented "feature". See also nx-X11. nxcomp-1.5.0-4 - Adjusted for alarm(0) returning an inconsistent value in ConnectTo- Remote(). - Small changes to Types.h, Jpeg.cpp and Png.cpp to compile with gcc 3.4.2. - Cosmetic changes to the ChangeLog file. nxcomp-1.5.0-3 - Removed test code from ServerChannel.cpp. - Small cosmetic change in Loop.cpp. - Tested for compatibility against 1.4.1-8. nxcomp-1.5.0-2 - Changed VERSION file according to TRCL052336. nxcomp-1.5.0-1 - Opened the 1.5.0 branch based on 1.4.1-9. nxcomp-1.4.1-9 - Implemented methods PACK_RDP_PLAIN_64K_COLORS and PACK_RDP_PLAIN_- 16M_COLORS. It seems that 16 bpp plain bitmaps can be seldom recei- ved. This doesn't seem to be the case of 24 bpp bitmaps. nxcomp-1.4.1-8 - The new code enables use of 16 bpp and 32 bpp RDP bitmaps. This re- quired modifications to the following files: Misc.h Misc.cpp Loop.cpp Control.cpp ServerChannel.cpp Unpack.h Unpack.cpp - Lots of cosmetic changes compared to the original code in 1.4.1-7. - Merged the 1.4.0 and the 1.4.1 branches by importing changes up to the 1.4.1-7. nxcomp-1.4.0-30 - Modified the function checking the Unix socket where X connections will be forwarded. The function will not fail if the socket itself doesn't exist yet. This solves the TRCL042203. - Moved the code checking for the CTRL+ALT+SHIFT+ESC sequence to a separate function. The implementation will now look for a different sequence on the MacOS/X platform. This should solve the TRCL042182. nxcomp-1.4.0-29 - Solved a bug that could cause proxy to stop reading data from the X channels due to an incorrect calculation of the bytes queued on the proxy link. The bug only affected Linux kernels of the 2.0/2.2 series. - Fixed a problem in the proxy class that could let the select time- out to become zero. nxcomp-1.4.0-28 - Prevented the main proxy process from quitting when the fork() of any of the children fails. This can actually happen on Windows due to conflicts in reallocating any of the Cygwin DLLs. - Reworked handling of the priority flag in channels when dealing with the X_InternAtoms and X_AllocColor requests to reduce the time of session startup. Priority is never set in case of X_InternAtom requests and replies as we assume that most clients use the appro- priate Xlib function to pipeline multiple requests in a single net- work operation. - The timeout after which proxy will abort the peer connection is still 120 seconds. An alert dialog will be shown earlier, after 30 seconds instead of 60. nxcomp-1.4.0-27 - Removed the code setting priority on channels on ButtonPress and Button release events. It seems preferrable to delay the flush and pack more events in a single frame. This makes possible to get the ButtonRelease together with the ButtonPress. - Fixed error detection in SetNoDelay() where a positive result could produce a 'not supported' message in debug mode. nxcomp-1.4.0-26 - Reworked the check aimed at detecting the clock drifts. The previ- ous code, introduced in 1.3.2-2, had the undesired side-effect of resetting the proxy timeout. This could cause the proxy to never detect that the other end had been killed. - The default behaviour of proxy is now to terminate the session at the time an error is encountered. This includes network failures. - Added a new dialog to be shown to the user whenever the session is terminated abnormally. nxcomp-1.4.0-25 - Fixed a bug in handling of suppression of errors generated by committing the image splits. Other errors, unrelated to the commit of split, could be suppressed. This might cause the hangup of the session when the suppressed error was matching a reply. nxcomp-1.4.0-24 - When killing the proxy process -9 the watchdog could remain alive. This caused the SSH link to keep the stderr open, with the effect that former proxy message were not flushed to the session log. Now the watchdog checks every second if the parent is dead, so that file descriptors can be closed as soon as possible. - SetupDisplaySocket() tried to force the connections to the X server on the UNIX port whenever the display was set to localhost. This patch was intended to fix a bug in nxclient, using the TCP port even when the display was set to a UNIX socket. This beha- viour caused problems when running nxclient on a remote host by means of a ssh -X. Proxy will now adhere to the display setting. nxcomp-1.4.0-23 - Added the possibility to respawn nxclient at the end of session. This is an useful feature when powering thin-clients where NX is the only application made available. - This configuration applies system-wide to the local client ins- tallation. By default it is disabled. It can be enabled by the by creating a 'noexit' file in the directory '/usr/NX/share', or at compile time, by setting ENABLE_RESTART_ON_SHUTDOWN to 1. - Note that the solution is not perfect yet, as there are cases where session could die without going through HandleShutdown(), for example if a decoding error is raised in the communication with the remote peer and the subsequent restore of the proxy link fails to succeed. nxcomp-1.4.0-22 - Corrected typos in Timestamp.h and Loop.cpp. nxcomp-1.4.0-21 - Tuned the handling of RenderCompositeGlyphs. Now compression of RENDER shows an average ratio of 8:1. This is a steady increase compared to the 5:1 of the 1.3.2 version. The overall advantage when running clients displaying a large amount of text is 30%. nxcomp-1.4.0-20 - Added a new message store for the CreatePixmap request. - Fixed handleCopy() to only send the data part past the offset. This fix can save a significant amount of traffic, especially when sending many small images whose size doesn't reach the threshold set for compressing them. nxcomp-1.4.0-19 - Added tuning of all the RENDER requests. RenderCompositeGlyphs will require further work. - Completed porting of the RENDER requests to the new templates. nxcomp-1.4.0-18 - Tuned the handling of RenderCreatePicture and RenderFreePicture. - Created a new template system for writing new message encoders. - Renamed getBits() in EncodeBuffer with diffBits(). The getBits() method now returns the bits actually allocated in the buffer and can be called multiple times. nxcomp-1.4.0-17 - Provided specific encoding of the remaining RENDER requests and added compression of RenderCreateGlyphSet. nxcomp-1.4.0-16 - Code underwent through 7 different patch versions but seems to be stable now. It still needs to provide specific encoding for RenderCreatePicture RenderChangePicture and RenderFreePicture. Code will likely include incompatible changes at any new version until the new encoding will stabilize, so be sure that you use the same nxcomp version at both sides. - Created the infrastructure for compressing extension messages based on the minor opcode. A new class, MinorMessageStore, has to be inherited by stores requiring specific compression, so that all the encoding methods of the parent can be redirected to it. The RENDER extension is now compressed using this new class. - Managed to maintain the compatibility with caches created by the 1.3.X version. Caches will be saved using the old format if the remote version is not a 1.4.x. When restoring a cache created by the 1.3.x with both proxies being the newest version, RENDER mesages will be discarded without having to discard the whole cache. - Added a member to the message class to store the identity size. This field is now used instead of the default data offset to manage the message data, that is the part of message after the identity. - Removed the warnings printed in the session log when passing the 'kbtype' and the 'geometry' options to the proxy. These options are actually used by agents. nxcomp-1.4.0-15 - Started implementation of the new framework for handling enco- ding of X extensions. - Renamed the overloaded methods handleEncode() and handleDecode() to handleEncodeCached(), handleEncodeIdentity() and handleDecode- Cached(), handleDecodeIdentity(). - Updated the copyright notice to year 2004. nxcomp-1.4.0-14 - Added the new channel for HTTP connections. This channel can be used to let applications running on the NX server get access to data and applications made available by a HTTP daemon running at the client side. This channel is not used at the moment by nxclient. It can be activated by passing http=1 to the NX server side proxy and the value http=80 to the NX client side. nxcomp-1.4.0-13 - Quick patch to run on MacOS/X where inet_addr("127.0.0.1") alone seems to fail to return a valid interface. nxcomp-1.4.0-12 - Few cosmetic changes to logging. - Removed the debug output. nxcomp-1.4.0-11 - Removed code used for simulating the new forwarding function- ality without client and server support. nxcomp-1.4.0-10 - Implemented authentication of the forwarder to the listening proxy. If a session cookie is required, the forwarder must provide the cookie upon connection. An authentication phase at the time the forwarder connects to the NX client side is strongly suggested because it is usu- ally this side that sends the authorization cookie. Without such a forwarder authentication, the local peer would reveal the cookie to the first process connecting to the port. - If no cookie is specified in the switch command, the forward- er is required to skip the authentication phase. This can be useful when running the programs on the command line. - The implementation required appropriate changes to session negotiation in nxssh. This version requires nxssh-1.4.0-8. nxcomp-1.4.0-9 - Fixed the startup procedure to correctly negotiate the cookie when the X server side proxy is listening for a forwarder. - When listening for a local forwarder (that is when the listen option is enabled at X server side), proxy will listen for the forwarder on the localhost interface. - This version has ports and and other parameters hardcoded for testing purposes and is not intended for normal use. nxcomp-1.4.0-8 - Slightly modified the text of FAILED_PROXY_CONNECTION alert. nxcomp-1.4.0-7 - Added provision for leaving a running dialog showing an OK box if proxy is exiting abnormally. This seems to fail to yield the expected results as, at the present moment, client checks if parent has exited. - Added a Binder class invoked when calling proxy with -B option. It would serve as a replacement of the modifications I'm doing in nxssh. The class is just a framework and the implementation is unfinished. nxcomp-1.4.0-6 - Added a line in the session log if the connection procedure is aborted due to a signal. - Fixed ParseHostOption() to let it take in account the proxy port offset when passing 'host:port' as the last parameter at X server side. nxcomp-1.4.0-5 - Lowered the default port offsets used for CUPS and SMB services to 2000 and 3000. Arbitrary ports can be used by passing the ser- vice's port at proxy startup. By default, anyway, the port is at the same offset of the proxied display. Considering that the NX server uses to start the first session at display 1000, we must lower some of the defaults to avoid interference with the normal X sessions run on the server. Session at display ":1000" will have: . Forwarding of CUPS connections at 3000. . Forwarding of SMB connections at 4000. . The listening proxy (at NX server side) at 5000. This port is not used when enabling SSH tunneling . The proxied X display (NX agent or proxy) at 7000. . Forwarding of multimedia channels at 8000. . Forwarding of embedded keyboard connections at 9000. - Turned off the log output. nxcomp-1.4.0-4 - Managed to get the X client side proxy connect to the remote peer. This means that some parameters that were implicitly assumed based on the proxied port simulating the X display must be now specified in the NX display string. - The 'port' parameter now specifies the remote TCP port where the local proxy will connect to the remote peer. In previous versions this parameter also affected the proxied port. This was mainly a side effect, as the remote peer was always con- tacted at port 4000 plus the proxied display offset. - Added the 'listen' parameter to tell to the proxy that is go- ing to accept the peer connection on which port it will be contacted. By default, similarly to the previous version, the proxy will listen at the proxied display offset. - A check has been added in order to disallow passing both the 'accept' and the 'connect' parameter at the same time. A simi- lar check affects the 'listen' parameter. - Renamed the previously unused 'sync' channel as 'cups'. nxcomp-1.4.0-3 - Preserved the ability of version 1.3.2 to load caches generated by this version. nxcomp-1.4.0-2 - Small fixes to compile under Solaris 8. nxcomp-1.4.0-1 - Opened the 1.4.0 branch. nxcomp-1.3.2-4 - Fixed a problem with shmget(). Code in ServerChannel checked the return value for being greater then 0, while 0 can actual- ly be a valid segment. - When memory cache is disabled, nxcomp will explicitly set the appropriate control variables dealing with loading and saving of the persistent cache. This can be considered a cosmetic change as nxcomp will disable NX delta compression if memory cache is not available and this has an implicit effect on the ability to load and save such a cache. nxcomp-1.3.2-3 - Removed inclusion of zlib.h in Png.cpp. Conflicting symbols with zlib.h from nx-X11 could cause compilation to fail. nxcomp-1.3.2-2 - Added a function to take into account the clock drifts at the time we check the ping from the remote proxy. This can be caused by the user changing the system time or by small adjust- ments introduced by the operating system making the clock go backward. Such events could cause the proxy link to be shut down and reconnected. - Reduced the length of lines printed in statistics when showing the details of X protocol's opcodes. This is intended to help nxclient to keep the whole statistics in the 'details' window, so that users don't have to use the scrollbars. nxcomp-1.3.2-1 - Opened the 1.3.2 branch. nxcomp-1.3.1-5 - It seems that Solaris can return an EINVAL error selecting a shutdown descriptor even before we actually close the socket. We now ignore the condition on Solaris. This is definitely a Solaris bug. nxcomp-1.3.1-4 - Increased the timeout after which proxy will abort the peer connection to 120 seconds. An alert dialog will now be shown after 60 seconds, instead of the 30 seconds being the default in the previous version. Some users reported the timeout to be too short in the case of temporary network failures. nxcomp-1.3.1-3 - Some optimizations in the cache house-keeping process. Now it runs at lower system priority in respect to the parent. Any 2 iterations through directories and images, it also sleeps for a tiny amount of time. This further decreases the system load. - Removed the underline characters and added a colon in the title of this ChangeLog to fully comply with format used in the release notices. nxcomp-1.3.1-2 - Fixed a crash when running both client and server on the SPARC Solaris. An optimization used to avoid byte-swapping when both hosts have the same endianess doesn't work on SPARC if buffer is not aligned to word boundary. A better version of the code should check the CPU, not the OS, as this probably applies to other processors. nxcomp-1.3.1-1 - Opened the 1.3.1 branch. nxcomp-1.3.0-50 - Disabled the processor limit in X client side proxy. In previous versions the processor load limit was set to an idle time ratio of 2. This was likely to cause an unwanted slowdown on very old hardware or when running the server as guest OS inside a VMWare virtual machine. nxcomp-1.3.0-49 - Last minute update on proxy shutdown bug on Cygwin. A stack trace reveals that faillure happens in the static destructor of the BlockCacheSet class. It seems that problem appeared just after having upgraded to the latest version of Cygwin DLL. Now the destructor is skipped at shutdown. nxcomp-1.3.0-48 - Further fix to overcome the shutdown problem on Windows. nxcomp-1.3.0-47 - Used T_files::value_type() in Keeper.cpp. The form without the explicit constructor fails to compile with GCC 2.91 on RH 6.2. - Fixed '==' -> '=' in configure.in to build under RH 6.2 and probably other platforms. nxcomp-1.3.0-46 - The cleanup procedure now skips deletion of the IO streams under Windows. This is intended to overcome a strange segfault occurring at random time, at the time proxy is being shutdown. nxcomp-1.3.0-45 - Newer versions of the stdlibc++ do not seem to be able to deal with NULL strings or non printable characters. This caused the standard error stream to get sometimes corrupted in the case TEST and DEBUG logs were enabled in Loop.cpp. nxcomp-1.3.0-44 - Disabled TEST and DEBUG logs in ServerChannel. nxcomp-1.3.0-43 - Fixed a crash when unpacking an image in server proxy in the case the unpack state for the given channel had not been previously created. nxcomp-1.3.0-42 - Small modification to setSchedule() to not account previous data accumulated in the encode buffer. The new code seems to allow better use of the available bandwidth. nxcomp-1.3.0-41 - Further fix in Tight decompression. Content of the main write buffer was not removed before flushing the unpacked data to the X server link. nxcomp-1.3.0-40 - Removed the code marked as FIXME in Loop and ServerChannel. Code was used in previous version to override any pack method other than NO_PACK and to disable initialization of the shared memory. nxcomp-1.3.0-39 - Solved the problem with unpacking of RDP text on big-endian architectures. The width field in PutPackedImage was put as 16 bits and extracted as 32 bits at decoding side. - Moved the code checking if any of the children has exited in a specific function. This function is now called any time signals are newly enabled. This works even on Solaris. - Enabled use of shared memory with Tight image decompression. - Added event NXCollectPropertyNotify to NXproto.h. nxcomp-1.3.0-38 - Message locks are now checked before splitting a message. nxcomp-1.3.0-37 - Added a further counter to yield encoding data if the amount of output bytes to be written to channels exceeds a threshold. - Modified proxy to write data immediately if the encoding loop was interrupted. nxcomp-1.3.0-36 - Further fix required by Tight decompression. By handling mul- tiple writes in the Tight class we may flush the main write buffer in the wrong order. If Tight decompression is enabled, the buffer is now flushed before decoding the packed image. - A SIGCHLD is raised any time signals are newly enabled. This allows the main loop to wait() the pid of children that had exited while signals were disabled. nxcomp-1.3.0-35 - Fixed Tight decompression by handling multiple writes to the channel's transport inside the decompression function. - Made Tight decompressor in ServerChannel a pointer instead of an instance of the class. Class is allocated the first time it is referenced. - Rewritten handleUnpack() in ServerChannel to use a switch() on the pack method instead of multiple if(). nxcomp-1.3.0-34 - Disabled RENDER extension when running X agent sessions on the Solaris client. Problems seem to be caused by incompati- bilities in the implementation of RENDER on the Sun X server shipped by default. We did not test NX with the XFree86 X server, but it seems that, when using the Sun X server, even plain X clients don't make full use of the extension. nxcomp-1.3.0-33 - Added 4 new fields to the X_NXGetControlParameters reply. A dialog should be shown by agent if proxy was not able to negotiate a persistent cache or if a cache was selected but not loaded because incompatible or corrupted. - Fixed a bug on Solaris where socket() must be recreated if the first connect() fails. Not a problem. The strange thing is that if you reuse the socket(), the subsequent connect() is successful and the program fails as soon as you try to write to it. nxcomp-1.3.0-32 - Added request X_NXFreeUnpack to free the resources allocated by the remote proxy to unpack images for the given agent's client - Added an alert at X server side if proxies were not able to negotiate a persistent cache or if a cache was selected but not loaded at X server side (that means that probably cache was not compatible or corrupted). The alert is not enabled at the moment. Before enabling it, we need to find a way to deal with full-screen mode. - Tuning of MIT-SHM parameters. nxcomp-1.3.0-31 - Modified the following requests to carry the id of the agent's client: - X_NXSetUnpackGeometry - X_NXSetUnpackColormap - X_NXSetUnpackAlpha - X_NXPutPackedImage Now each agent's client has its own record pointing to the geometry, colormap and alpha channel that will be used to unpack the image. Code is not finished yet so it is advisable that agents' writers keep using client id 0. Note also that the final solution will require some changes to the way split notifications are sent to agent that I plan to implement in the next versions. - The X_NXSetUnpackGeometry request has now a message store. This should reduce the overhead to the minimum even in the case of dumb agents not checking if the current geometry matches the image to be unpacked. nxcomp-1.3.0-30 - Improved handling of short-circuited replies at X client side. Request opcodes are now pushed in the sequence queue to determine if a reply is expected. In this case tainting of reply is skipped to preserve the sequence ordering of any event or error that could have generated by the reply. This seems to solve all the problems reported by people running a NX session in virtual desktop mode nested in an existing session being run in single application mode. - Modified the cleanup procedure to print the 'Waiting for' message in the session log only at the time all X channels have been actually shut down. This message can be used by the NX server to find out the appropriate time to close the session. - Added the selected session type to the 'Using pack method' message printed at startup. - Added the option to close down the proxy link and perform a clean shutdown of the session, re-read the whole session configuration or restart the proxy link when a SIGHUP is delivered to the proxy process. The default behaviour is to close down the session. The new default (the old one was to restart the link) is a required feature to let users run full sessions using single application mode f.e. in a LTSP environment. I noted that when the controlling terminal of the X session is closed, a SIGHUP is delivered to the X server side proxy (probably it is delivered to all the con- nected clients, and, thus for NX, only to the proxy). This signal can be used to terminate the whole session. nxcomp-1.3.0-29 - Changed defaults to disable image masks when running proxy in single application mode. The new configuration requires changes in NX server and in NX X11 library. Users should experience much better image quality when using NX on slow links. This comes at the cost of slightly worse compression ratios. nxcomp-1.3.0-28 - Opcode of request was not rewritten by server channel when using link LAN. This caused problems with taint of replies. nxcomp-1.3.0-27 - Modified the split store and the split procedures in client channel to always return the client id in the notification events. The new field is added to event at byte offset 28 to preserve compatibility with previous agent releases. The modification permits to agent to match the commit of alpha channel with the original image, if requests are split by proxy. - Temporarily set the timeout used to poll MIT-SHM completion events coming from X server to 0. More testing is required. - Fixed an inconsistent message generated in statistics due to a division by 0. nxcomp-1.3.0-26 - Modified pending timeout from 1 to 0 milliseconds. This means that both channels and proxy are now immediately restarted to let them consume all data left in their read buffer. - When querying the X server for MIT-SHM support, size of name in X_QueryExtension request was sent as 32 bits instead of 16. This caused request to fail on big endian architectures. - Modified the MIT-SHM initialization procedure to always send all the 3 protocol requests also in the case of early failures. nxcomp-1.3.0-25 - Implemented handling of the new X_NXSetUnpackAlpha message. Performances are very satisfactory. Using most of the current GNOME and KDE applications, caching reaches 90% of the total messages. - Modified the persistent cache management routines to handle backward compatibility with proxy versions prior of 1.3.0. - It has been made possible to send both X_NXSetUnpackColormap and X_NXSetUnpackAlpha messages with 0 entries to temporarily disable use of the colormap or the alpha channel and free the resources allocated by the server channel class. - Added function UnpackAlpha() to Unpack.cpp. nxcomp-1.3.0-24 - Using WriteBuffer::registerPointer() to track growing of the write buffer in handleFastWrite() functions. This problem existed even in 1.2.2 but never shown up because we didn't have to use the pointer after data had been written to the write buffer. Now, instead, we have to post-process the write buffer to copy data to the shared segment. - Optimized memory allocations running with link LAN to save a memcpy() any time new data is allocated in the scratch buffer. - When running with link LAN the scratch buffer is now used only when: - A further allocation would case growing of the write buffer (and, thus, a memcpy() of the previous content). - When data to added is bigger than the write threshold. In previous versions the scratch buffer was used any time the total amount of data to be written (write buffer + scratch buffer) exceeded the scheduled write threshold. This caused small writes to be appended even when a single write could be obtained without reallocating the buffer. - Preliminary support for transporting the alpha channel in a separate message in case of 32 bits displays using the RENDER extension. nxcomp-1.3.0-23 - Modified the mask used to open the shared memory segment on OS/X to 0777. We have to better investigate why the previous 0600 mask doesn't work even if the user running the proxy is the same user running the X server. nxcomp-1.3.0-22 - Implemented MIT-SHM support on LAN connections. - More MIT-SHM bug fixes. nxcomp-1.3.0-21 - Better use of the shared segment through an improved algorithm leveraging the offset field of the X_ShmPutImage request. The new algorithm greatly reduces the amount of polls the proxy needs to perform to find if the completion event has arrived. - Implemented MIT-SHM support for X_PutImage requests. - Implemented option shmem=value. Use of this option is anyway discouraged. Proxy will allocate the shared memory segment based on the size of the in-memory cache set by the user. Use of MIT-SHM is disabled when user did set a memory cache smaller than 2MB (for example on the embedded client). - Rewritten post-processing of images in server channel loop. - Improved error handling to ensure we intercept all MIT-SHM X errors before they reach the NX agent. - Solved a problem that was causing channels to not reflect shared memory support flags set in control. nxcomp-1.3.0-20 - Solved a compatibility problem when mixing proxy versions 1.2.2 and 1.3.0. nxcomp-1.3.0-18 - Rewritten interfaces to shared memory initialization in client and server channel. - Server channel checks for the completion event until a timeout before reusing the shared memory segment. - Flush flag was not cleared after the write buffer had been flushed in handleWrite() of both client and server channels. This could lead to multiple fragmented writes, affecting the performances. - Added -lcygipc to linking on Windows platform. - Added a check on GCC version to see if -Wnested-externs -Wmissing-declarations are valid options. nxcomp-1.3.0-17 - Implemented initial support for MIT-SHM extension in the network path between the X server proxy and the real X server. Presently it works only for X_NXPutPackedImages. - Modified configure.in to compile under FreeBSD. - Small changes to sources due to FreeBSD support. nxcomp-1.3.0-16 - Fixed caching of RENDER extension on MacOS/X and Solaris. - Under Solaris an explicit call to EnableSignal() is needed at the end of the signal handler as raising a signal seems to reset the previous settings. - Can't find a way to get bytes queued for write on Solaris as both FIONWRITE and TIOCOUTQ don't seem to be available. This means that NX server on Solaris is only able to detect con- gestions on proxy link at the time a write fails with error EAGAIN. - Starting from this version, render extension messages are not automatically discarded from cache when running agent based X sessions. This is in preparation of render support introduced in this release. nxcomp-1.3.0-15 - Changed default to force writes if X channels exceed buffer limits. This change was suggested by benchmarks performed on Win32. - Wrapped IO on cache files in functions performing better error checking. - General cleanup in handling of socket options for MacOS/X and Solaris. nxcomp-1.3.0-14 - Corrupted persistent caches were not deleted in case loading of any of the message stores failed. To run further sessions on the same host, user had to delete the cache file manually. - Improved error handling in JPEG decompression. Now connection is reset in case of failure. - Before performing JPEG or PNG decompression, image is better checked to verify if loading from disk failed. - Improved error handling in case of failure loading persistent cache from disk. On MacOS/X istream -> fail() doesn't seem to work properly. This needs further investigation. - The default installation path of nxclient is searched under MacOS/X at the time nxclient is invoked in dialog mode. nxcomp-1.3.0-13 - Fixed a (further) compilation problem under Solaris. Now static libraries are first searched under /usr/sfw/lib (in case Sun would decide to include them in future releases). nxcomp-1.3.0-12 - Fixed parsing of command line when passing option -V. - Correctly detected ENOPROTOOPT when setting TCP_NODELAY socket option on MacOS/X and Solaris. nxcomp-1.3.0-11 - Given option in configure to specify what needs to be built statically: --with-static-png enable static linking of PNG library --with-static-jpeg enable static linking of JPEG library --with-static-z enable static linking of Z library nxcomp-1.3.0-10 - Fixed a problem in saving of persistent cache on big-endian machines. nxcomp-1.3.0-9 - Testing with different settings to check if it's possible to increase the performances under Windows. - Solved a problem in parsing of options that prevented proxy to connect to a remote session running at port offset 0. - Fixed two warnings compiling on Solaris. - Changed configure.in to first check for nx-X11 includes and libraries. Added "/usr/openwin/bin/makedepend" to path searched for the executable. nxcomp-1.3.0-8 - Small cleanup in configure.in and files modified by Gregorz Kryza to add support for Solaris. - A new configure script has been generated using autoconf-2.57-3. nxcomp-1.3.0-7 - Added support for detection of Solaris in configure script. Now Makefile.in uses ranlib instead of ar. - Small changes in source and header files to support Solaris. nxcomp-1.3.0-4 - Corrected a bug that could cause priority on proxy and channels to be not taken in account at the time proxy tries to determine if it's time to flush the proxy link. - Better implementation of abort split notification by X server proxy to its remote peer. The new implementation doesn't need to set a timeout and permits notifications to be received earlier. - Improved support for 'tainting' XSync() messages coming from X clients in single application mode. Now a X_GetInputFocus is sent to the real X server any n such messages received by proxy. - Included support for 15 bpp displays. It seems that handling them as 16 bpp it's OK. nxcomp/GetPropertyReply.h0000644000076400007640000001112711323113030015770 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef GetPropertyReply_H #define GetPropertyReply_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define GETPROPERTYREPLY_ENABLE_CACHE 1 #define GETPROPERTYREPLY_ENABLE_DATA 1 #define GETPROPERTYREPLY_ENABLE_SPLIT 0 #define GETPROPERTYREPLY_ENABLE_COMPRESS 1 #define GETPROPERTYREPLY_DATA_LIMIT 1048576 - 32 #define GETPROPERTYREPLY_DATA_OFFSET 32 #define GETPROPERTYREPLY_CACHE_SLOTS 400 #define GETPROPERTYREPLY_CACHE_THRESHOLD 5 #define GETPROPERTYREPLY_CACHE_LOWER_THRESHOLD 1 #define GETPROPERTYREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 // // The message class. // class GetPropertyReplyMessage : public Message { friend class GetPropertyReplyStore; public: GetPropertyReplyMessage() { } ~GetPropertyReplyMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char format; unsigned int type; unsigned int after; unsigned int items; }; class GetPropertyReplyStore : public MessageStore { public: GetPropertyReplyStore(StaticCompressor *compressor); virtual ~GetPropertyReplyStore(); virtual const char *name() const { return "GetPropertyReply"; } virtual unsigned char opcode() const { return X_GetProperty; } virtual unsigned int storage() const { return sizeof(GetPropertyReplyMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new GetPropertyReplyMessage(); } virtual Message *create(const Message &message) const { return new GetPropertyReplyMessage((const GetPropertyReplyMessage &) message); } virtual void destroy(Message *message) const { delete (GetPropertyReplyMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* GetPropertyReply_H */ nxcomp/CreateGC.cpp0000644000076400007640000001543211323113027014451 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "CreateGC.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int CreateGCStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { CreateGCMessage *createGC = (CreateGCMessage *) message; // // Here is the fingerprint. // createGC -> gcontext = GetULONG(buffer + 4, bigEndian); createGC -> drawable = GetULONG(buffer + 8, bigEndian); createGC -> value_mask = GetULONG(buffer + 12, bigEndian); // // Clear the unused bytes carried in the // payload to increase the effectiveness // of the caching algorithm. // if ((int) size > dataOffset) { #ifdef DEBUG *logofs << name() << ": Removing unused bytes from the " << "data payload.\n" << logofs_flush; #endif createGC -> value_mask &= (1 << 23) - 1; unsigned int mask = 0x1; unsigned char *source = (unsigned char *) buffer + CREATEGC_DATA_OFFSET; unsigned long value = 0; for (unsigned int i = 0; i < 23; i++) { if (createGC -> value_mask & mask) { value = GetULONG(source, bigEndian); value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i])); PutULONG(value, source, bigEndian); source += 4; } mask <<= 1; } } #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int CreateGCStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { CreateGCMessage *createGC = (CreateGCMessage *) message; // // Fill all the message's fields. // PutULONG(createGC -> gcontext, buffer + 4, bigEndian); PutULONG(createGC -> drawable, buffer + 8, bigEndian); PutULONG(createGC -> value_mask, buffer + 12, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void CreateGCStore::dumpIdentity(const Message *message) const { #ifdef DUMP CreateGCMessage *createGC = (CreateGCMessage *) message; *logofs << name() << ": Identity gcontext " << createGC -> gcontext << ", drawable " << createGC -> drawable << ", value_mask " << createGC -> value_mask << ", size " << createGC -> size_ << ".\n" << logofs_flush; #endif } void CreateGCStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // This didn't include the drawable // in previous versions. // md5_append(md5_state_, buffer + 8, 8); } void CreateGCStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { CreateGCMessage *createGC = (CreateGCMessage *) message; CreateGCMessage *cachedCreateGC = (CreateGCMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; if (control -> isProtoStep7() == 1) { #ifdef TEST *logofs << name() << ": Encoding value " << createGC -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeNewXidValue(createGC -> gcontext, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> gcCache, clientCache -> freeGCCache); cachedCreateGC -> gcontext = createGC -> gcontext; } else { #ifdef TEST *logofs << name() << ": Encoding value " << createGC -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(createGC -> drawable, clientCache -> drawableCache); cachedCreateGC -> drawable = createGC -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << createGC -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(createGC -> gcontext, clientCache -> gcCache); cachedCreateGC -> gcontext = createGC -> gcontext; } } void CreateGCStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { CreateGCMessage *createGC = (CreateGCMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeNewXidValue(value, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> gcCache, clientCache -> freeGCCache); createGC -> gcontext = value; #ifdef TEST *logofs << name() << ": Decoded value " << createGC -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } else { decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); createGC -> drawable = value; #ifdef TEST *logofs << name() << ": Decoded value " << createGC -> drawable << " as drawable field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); createGC -> gcontext = value; #ifdef TEST *logofs << name() << ": Decoded value " << createGC -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } } nxcomp/ServerProxy.h0000644000076400007640000000732111323113027015007 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ServerProxy_H #define ServerProxy_H #include #include #include "Proxy.h" #include "Misc.h" // // Set the verbosity level. // #undef TEST #undef DEBUG class ServerProxy : public Proxy { public: ServerProxy(int proxyFd); virtual ~ServerProxy(); virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily, sockaddr *xServerAddr, unsigned int xServerAddrLength); virtual void handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort, int httpServerPort, const char *fontServerPort); protected: // // Create a new channel. // virtual int handleNewConnection(T_channel_type type, int clientFd); virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId); virtual int handleNewAgentConnection(Agent *agent); virtual int handleNewXConnection(int clientFd); virtual int handleNewXConnectionFromProxy(int channelId); // // Implement persistence according // to our proxy mode. // virtual int handleLoad(T_load_type type) { return 0; } virtual int handleSave() { return 0; } virtual int handleAsyncEvents() { return 0; } virtual int handleLoadFromProxy(); virtual int handleSaveFromProxy(); virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient) const; virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const; int handleCheckDrop(); int handleCheckLoad(); // // Utility function used to realize // a new connection. // protected: virtual int checkLocalChannelMap(int channelId) { if (control -> isProtoStep7() == 1) { return ((channelId & control -> ChannelMask) == 0); } else { return 0; } } private: int xServerAddrFamily_; sockaddr *xServerAddr_; unsigned int xServerAddrLength_; // // This is the name of the X display where // we are going to forward connections. // char *xServerDisplay_; // // Ports where to forward extended services' // TCP connections. // int cupsServerPort_; int smbServerPort_; int mediaServerPort_; int httpServerPort_; // // It will have to be passed to the channel // so that it can set the port where the // font server connections are tunneled. // char *fontServerPort_; }; #endif /* ServerProxy_H */ nxcomp/NXvars.h0000644000076400007640000001201311323113031013705 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef NXvars_H #define NXvars_H /* * This can be included by the proxy or another * layer that doesn't use Xlib. */ #if !defined(_XLIB_H_) && !defined(_XKBSRV_H_) #define NeedFunctionPrototypes 1 #define Display void #endif #ifdef __cplusplus extern "C" { #endif /* * Display flush policies. */ #define NXPolicyImmediate 1 #define NXPolicyDeferred 2 /* * Type of flush. */ #define NXFlushBuffer 0 #define NXFlushLink 1 /* * Type of statistics. */ #define NXStatisticsPartial 0 #define NXStatisticsTotal 1 /* * Reason why the display is blocking. */ #define NXBlockRead 1 #define NXBlockWrite 2 /* * Set if the client is interested in ignoring * the display error and continue with the exe- * cution of the program. By default the usual * Xlib behaviour is gotten, and the library * will call an exit(). */ extern int _NXHandleDisplayError; /* * The function below is called whenever Xlib is * going to perform an I/O operation. The funct- * ion can be redefined to include additional * checks aimed at detecting if the display needs * to be closed, for example because of an event * or a signal mandating the end of the session. * In this way the client program can regain the * control before Xlib blocks waiting for input * from the network. */ typedef int (*NXDisplayErrorPredicate)( #if NeedFunctionPrototypes Display* /* display */, int /* reason */ #endif ); extern NXDisplayErrorPredicate _NXDisplayErrorFunction; /* * This is called when Xlib is going to block * waiting for the display to become readable or * writable. The client can use the hook to run * any arbitrary operation that may require some * time to complete. The user should not try to * read or write to the display inside the call- * back routine. */ typedef void (*NXDisplayBlockHandler)( #if NeedFunctionPrototypes Display* /* display */, int /* reason */ #endif ); extern NXDisplayBlockHandler _NXDisplayBlockFunction; /* * Used to notify the program when more data * is written to the socket. */ typedef void (*NXDisplayWriteHandler)( #if NeedFunctionPrototypes Display* /* display */, int /* length */ #endif ); extern NXDisplayWriteHandler _NXDisplayWriteFunction; /* * This callback is used to notify the agent * that the proxy link has been flushed. */ typedef void (*NXDisplayFlushHandler)( #if NeedFunctionPrototypes Display* /* display */, int /* length */ #endif ); extern NXDisplayFlushHandler _NXDisplayFlushFunction; /* * Used by the NX transport to get an arbitrary * string to add to its protocol statistics. */ typedef void (*NXDisplayStatisticsHandler)( #if NeedFunctionPrototypes Display* /* display */, char* /* buffer */, int /* size */ #endif ); extern NXDisplayStatisticsHandler _NXDisplayStatisticsFunction; /* * Let users redefine the function printing an * error message in the case of a out-of-order * sequence number. */ typedef void (*NXLostSequenceHandler)( #if NeedFunctionPrototypes Display* /* display */, unsigned long /* newseq */, unsigned long /* lastseq */, unsigned int /* type */ #endif ); extern NXLostSequenceHandler _NXLostSequenceFunction; /* * Let the X server run the children processes * (as for example the keyboard initialization * utilities) by using the native system libra- * ries, instead of the libraries shipped with * the NX environment. If set, the Popen() in * the X server will remove the LD_LIBRARY_PATH * setting from the environment before calling * the execl() function in the child process. */ extern int _NXUnsetLibraryPath; #ifdef __cplusplus } #endif #endif /* NXvars_H */ nxcomp/EncodeBuffer.cpp0000644000076400007640000003557111323113030015363 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Misc.h" #include "Control.h" #include "EncodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP #define ADVANCE_DEST \ \ if (destShift_ == 0) \ { \ destShift_ = 7; nextDest_++; *nextDest_ = 0; \ } \ else \ { \ destShift_--; \ } EncodeBuffer::EncodeBuffer() { size_ = ENCODE_BUFFER_DEFAULT_SIZE; buffer_ = new unsigned char[size_ + ENCODE_BUFFER_PREFIX_SIZE + ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE; end_ = buffer_ + size_; nextDest_ = buffer_; *nextDest_ = 0; destShift_ = 7; lastBits_ = 0; initialSize_ = ENCODE_BUFFER_DEFAULT_SIZE; thresholdSize_ = ENCODE_BUFFER_DEFAULT_SIZE << 1; maximumSize_ = ENCODE_BUFFER_DEFAULT_SIZE << 4; } EncodeBuffer::~EncodeBuffer() { delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE); } void EncodeBuffer::setSize(unsigned int initialSize, unsigned int thresholdSize, unsigned int maximumSize) { initialSize_ = initialSize; thresholdSize_ = thresholdSize; maximumSize_ = maximumSize; #ifdef TEST *logofs << "EncodeBuffer: Set buffer sizes to " << initialSize_ << "/" << thresholdSize_ << "/" << maximumSize_ << ".\n" << logofs_flush; #endif } void EncodeBuffer::fullReset() { if (size_ > initialSize_) { delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE); size_ = initialSize_; buffer_ = new unsigned char[size_ + ENCODE_BUFFER_PREFIX_SIZE + ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE; } end_ = buffer_ + size_; nextDest_ = buffer_; *nextDest_ = 0; destShift_ = 7; lastBits_ = 0; } void EncodeBuffer::encodeValue(unsigned int value, unsigned int numBits, unsigned int blockSize) { #ifdef DUMP *logofs << "EncodeBuffer: Encoding " << numBits << " bits value with block " << blockSize << " and " << (nextDest_ - buffer_) << " bytes in buffer.\n" << logofs_flush; #endif value &= IntMask[numBits]; unsigned int srcMask = 0x1; unsigned int bitsWritten = 0; if (blockSize == 0) blockSize = numBits; if (end_ - nextDest_ < 8) { growBuffer(); } unsigned int numBlocks = 1; do { if (numBlocks == 4) blockSize = numBits; unsigned int bitsToWrite = (blockSize > numBits - bitsWritten ? numBits - bitsWritten : blockSize); unsigned int count = 0; unsigned int lastBit; do { lastBit = (value & srcMask); if (lastBit) *nextDest_ |= (1 << destShift_); ADVANCE_DEST; srcMask <<= 1; } while (bitsToWrite > ++count); bitsWritten += bitsToWrite; if (bitsWritten < numBits) { unsigned int tmpMask = srcMask; unsigned int i = bitsWritten; if (lastBit) { do { unsigned int nextBit = (value & tmpMask); if (!nextBit) break; tmpMask <<= 1; } while (numBits > ++i); } else { do { unsigned int nextBit = (value & tmpMask); if (nextBit) break; tmpMask <<= 1; } while (numBits > ++i); } if (i < numBits) *nextDest_ |= (1 << destShift_); else bitsWritten = numBits; ADVANCE_DEST; } blockSize >>= 1; if (blockSize < 2) blockSize = 2; numBlocks++; } while (numBits > bitsWritten); } void EncodeBuffer::encodeCachedValue(unsigned int value, unsigned int numBits, IntCache &cache, unsigned int blockSize) { #ifdef DUMP *logofs << "EncodeBuffer: Encoding " << numBits << " bits cached value with block " << blockSize << " and " << (nextDest_ - buffer_) << " bytes in buffer.\n" << logofs_flush; #endif value &= IntMask[numBits]; if (end_ - nextDest_ < 8) { growBuffer(); } blockSize = cache.getBlockSize(blockSize); unsigned int index; unsigned int sameDiff; #ifdef DUMP diffBits(); #endif if (cache.lookup(value, index, IntMask[numBits], sameDiff)) { if (index > 1) index++; while (destShift_ < index) { index -= destShift_; index--; destShift_ = 7; nextDest_++; *nextDest_ = 0; } destShift_ -= index; *nextDest_ |= (1 << destShift_); ADVANCE_DEST; #ifdef DUMP *logofs << "EncodeBuffer: Encoded cached int using " << diffBits() << " bits out of " << numBits << ".\n" << logofs_flush; #endif } else { ADVANCE_DEST; ADVANCE_DEST; *nextDest_ |= (1 << destShift_); ADVANCE_DEST; // // The attempt is very seldom successful. // Avoid to encode the additional bool. // if (control -> isProtoStep8() == 1) { #ifdef DUMP *logofs << "EncodeBuffer: Encoded missed int using " << diffBits() << " bits out of " << numBits << ".\n" << logofs_flush; #endif encodeValue(value, numBits, blockSize); } else { if (sameDiff) { #ifdef DUMP *logofs << "EncodeBuffer: Matched difference with block size " << cache.getBlockSize(blockSize) << ".\n" << logofs_flush; #endif encodeBoolValue(1); } else { #ifdef DUMP *logofs << "EncodeBuffer: Missed difference with block size " << cache.getBlockSize(blockSize) << ".\n" << logofs_flush; #endif encodeBoolValue(0); encodeValue(value, numBits, blockSize); } #ifdef DUMP *logofs << "EncodeBuffer: Encoded missed int using " << diffBits() << " bits out of " << numBits << ".\n" << logofs_flush; #endif } } } void EncodeBuffer::encodeCachedValue(unsigned char value, unsigned int numBits, CharCache &cache, unsigned int blockSize) { #ifdef DUMP *logofs << "EncodeBuffer: Encoding " << numBits << " bits char cached value with block " << blockSize << " and " << (nextDest_ - buffer_) << " bytes in buffer.\n" << logofs_flush; #endif value &= IntMask[numBits]; if (end_ - nextDest_ < 8) { growBuffer(); } unsigned int index; #ifdef DUMP diffBits(); #endif if (cache.lookup(value, index)) { if (index > 1) index++; while (destShift_ < index) { index -= destShift_; index--; destShift_ = 7; nextDest_++; *nextDest_ = 0; } destShift_ -= index; *nextDest_ |= (1 << destShift_); ADVANCE_DEST; #ifdef DUMP *logofs << "EncodeBuffer: Encoded cached char using " << diffBits() << " bits out of " << numBits << ".\n" << logofs_flush; #endif } else { ADVANCE_DEST; ADVANCE_DEST; *nextDest_ |= (1 << destShift_); ADVANCE_DEST; encodeValue(value, numBits, blockSize); #ifdef DUMP *logofs << "EncodeBuffer: Encoded missed char using " << diffBits() << " bits out of " << numBits << ".\n" << logofs_flush; #endif } } void EncodeBuffer::encodeMemory(const unsigned char *buffer, unsigned int numBytes) { #ifdef DUMP *logofs << "EncodeBuffer: Encoding " << numBytes << " bytes of memory with " << (nextDest_ - buffer_) << " bytes in buffer.\n" << logofs_flush; #endif if (numBytes > ENCODE_BUFFER_OVERFLOW_SIZE) { #ifdef PANIC *logofs << "EncodeBuffer: PANIC! Should never encode buffer " << "of size greater than " << ENCODE_BUFFER_OVERFLOW_SIZE << " bytes.\n" << logofs_flush; *logofs << "EncodeBuffer: PANIC! Assuming failure encoding data " << "in context [A].\n" << logofs_flush; #endif // // Label "context" is just used to identify // the routine which detected the problem in // present source file. // cerr << "Error" << ": Should never encode buffer of size " << "greater than " << ENCODE_BUFFER_OVERFLOW_SIZE << " bytes.\n"; cerr << "Error" << ": Assuming failure encoding data " << "in context [A].\n" ; HandleAbort(); } alignBuffer(); if (end_ - nextDest_ < (int) numBytes) { growBuffer(numBytes); } memcpy(nextDest_, buffer, numBytes); nextDest_ += numBytes; if (nextDest_ == end_) { growBuffer(); } else if (nextDest_ > end_) { #ifdef PANIC *logofs << "EncodeBuffer: PANIC! Assertion failed. Error [B] " << "in encodeMemory() nextDest_ " << (nextDest_ - buffer) << " end_ " << (end_ - buffer) << ".\n" << logofs_flush; #endif // // Label "context" is just used to identify // the routine which detected the problem in // present source file. // cerr << "Error" << ": Failure encoding raw data " << "in context [B].\n" ; HandleAbort(); } *nextDest_ = 0; } unsigned int EncodeBuffer::getLength() const { unsigned int length = nextDest_ - buffer_; if (destShift_ != 7) { length++; } if (length > 0 && control -> isProtoStep7() == 1) { return length + ENCODE_BUFFER_POSTFIX_SIZE; } return length; } unsigned int EncodeBuffer::diffBits() { unsigned int bits = ((nextDest_ - buffer_) << 3); bits += (7 - destShift_); unsigned int diff = bits - lastBits_; lastBits_ = bits; return diff; } void EncodeBuffer::growBuffer(unsigned int numBytes) { if (numBytes == 0) { numBytes = initialSize_; } unsigned int bytesInBuffer = nextDest_ - buffer_; unsigned int newSize = thresholdSize_; while (newSize < bytesInBuffer + numBytes) { newSize <<= 1; if (newSize > maximumSize_) { newSize = bytesInBuffer + numBytes + initialSize_; } } unsigned char *newBuffer; newBuffer = new unsigned char[newSize + ENCODE_BUFFER_PREFIX_SIZE + ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE; if (newBuffer == NULL) { #ifdef PANIC *logofs << "EncodeBuffer: PANIC! Error in context [C] " << "growing buffer to accomodate " << numBytes << " bytes .\n" << logofs_flush; #endif cerr << "Error" << ": Error in context [C] " << "growing encode buffer to accomodate " << numBytes << " bytes.\n"; HandleAbort(); } #ifdef TEST if (newSize >= maximumSize_) { *logofs << "EncodeBuffer: WARNING! Buffer grown to reach " << "size of " << newSize << " bytes.\n" << logofs_flush; } #endif // // Prefix should not contain any valid data. // It is proxy that will fill it with control // messages and data length at the time a new // frame is written to socket. // memcpy(newBuffer, buffer_, bytesInBuffer + 1); newBuffer[bytesInBuffer + 1] = 0; delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE); buffer_ = newBuffer; size_ = newSize; end_ = buffer_ + size_; nextDest_ = buffer_ + bytesInBuffer; } void EncodeBuffer::alignBuffer() { if (destShift_ != 7) { destShift_ = 7; nextDest_++; if (nextDest_ >= end_) { growBuffer(); } *nextDest_ = 0; } } void EncodeBuffer::encodeActionValue(unsigned char value, unsigned short position, ActionCache &cache) { unsigned int v = (value << 13) | position; unsigned int t = (v - cache.last_); encodeCachedValue(t, 15, *(cache.base_[cache.slot_])); cache.last_ = v; #ifdef DEBUG *logofs << "EncodeBuffer: Encoded value " << (unsigned) value << " and position " << position << " with base " << cache.slot_ << ".\n" << logofs_flush; #endif cache.slot_ = (cache.last_ & 0xff); } void EncodeBuffer::encodeNewXidValue(unsigned int value, unsigned int &lastId, IntCache &lastIdCache, IntCache &cache, FreeCache &freeCache) { encodeCachedValue((value - 1) - lastId, 29, lastIdCache); lastId = value; cache.push(value, 0x1fffffff); freeCache.push(value, 0x1fffffff); } void EncodeBuffer::encodeNewXidValue(unsigned int value, unsigned int &lastId, IntCache &lastIdCache, XidCache &cache, FreeCache &freeCache) { encodeCachedValue((value - 1) - lastId, 29, lastIdCache); lastId = value; unsigned int t = (value - cache.last_); cache.last_ = value; #ifdef DEBUG *logofs << "EncodeBuffer: Encoded new Xid " << value << " with base " << cache.slot_ << ".\n" << logofs_flush; #endif cache.slot_ = (value & 0xff); cache.base_[cache.slot_] -> push(t, 0x1fffffff); freeCache.push(value, IntMask[29]); } void EncodeBuffer::encodeXidValue(unsigned int value, XidCache &cache) { unsigned int t = (value - cache.last_); encodeCachedValue(t, 29, *(cache.base_[cache.slot_])); cache.last_ = value; #ifdef DEBUG *logofs << "EncodeBuffer: Encoded Xid " << value << " with base " << cache.slot_ << ".\n" << logofs_flush; #endif cache.slot_ = (value & 0xff); } void EncodeBuffer::encodeFreeXidValue(unsigned int value, FreeCache &cache) { encodeCachedValue(value, 29, cache); } void EncodeBuffer::encodePositionValueCompat(short int value, PositionCacheCompat &cache) { unsigned int t = (value - cache.last_); encodeCachedValue(t, 13, *(cache.base_[cache.slot_])); cache.last_ = value; #ifdef DEBUG *logofs << "EncodeBuffer: Encoded position " << value << " with base " << cache.slot_ << ".\n" << logofs_flush; #endif cache.slot_ = (value & 0x1f); } nxcomp/NXpack.h0000644000076400007640000001164611323113031013663 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef NXpack_H #define NXpack_H #ifdef __cplusplus extern "C" { #endif #define MASK_METHOD_LIMIT 10 #define NO_MASK 0 #define MASK_8_COLORS 1 #define MASK_64_COLORS 2 #define MASK_256_COLORS 3 #define MASK_512_COLORS 4 #define MASK_4K_COLORS 5 #define MASK_32K_COLORS 6 #define MASK_64K_COLORS 7 #define MASK_256K_COLORS 8 #define MASK_2M_COLORS 9 #define MASK_16M_COLORS 10 #define PACK_METHOD_LIMIT 128 #define NO_PACK 0 #define PACK_MASKED_8_COLORS 1 #define PACK_MASKED_64_COLORS 2 #define PACK_MASKED_256_COLORS 3 #define PACK_MASKED_512_COLORS 4 #define PACK_MASKED_4K_COLORS 5 #define PACK_MASKED_32K_COLORS 6 #define PACK_MASKED_64K_COLORS 7 #define PACK_MASKED_256K_COLORS 8 #define PACK_MASKED_2M_COLORS 9 #define PACK_MASKED_16M_COLORS 10 #define PACK_RAW_8_BITS 3 #define PACK_RAW_16_BITS 7 #define PACK_RAW_24_BITS 10 #define PACK_COLORMAP_256_COLORS 11 #define PACK_JPEG_8_COLORS 26 #define PACK_JPEG_64_COLORS 27 #define PACK_JPEG_256_COLORS 28 #define PACK_JPEG_512_COLORS 29 #define PACK_JPEG_4K_COLORS 30 #define PACK_JPEG_32K_COLORS 31 #define PACK_JPEG_64K_COLORS 32 #define PACK_JPEG_256K_COLORS 33 #define PACK_JPEG_2M_COLORS 34 #define PACK_JPEG_16M_COLORS 35 #define PACK_PNG_8_COLORS 37 #define PACK_PNG_64_COLORS 38 #define PACK_PNG_256_COLORS 39 #define PACK_PNG_512_COLORS 40 #define PACK_PNG_4K_COLORS 41 #define PACK_PNG_32K_COLORS 42 #define PACK_PNG_64K_COLORS 43 #define PACK_PNG_256K_COLORS 44 #define PACK_PNG_2M_COLORS 45 #define PACK_PNG_16M_COLORS 46 #define PACK_RGB_16M_COLORS 63 #define PACK_RLE_16M_COLORS 64 #define PACK_ALPHA 65 #define PACK_COLORMAP 66 #define PACK_BITMAP_16M_COLORS 67 /* * Not really pack methods. These values * allow dynamic selection of the pack * method by the agent. */ #define PACK_NONE 0 #define PACK_LOSSY 253 #define PACK_LOSSLESS 254 #define PACK_ADAPTIVE 255 /* * Reduce the number of colors in the * image by applying a mask. */ typedef struct { unsigned int color_mask; unsigned int correction_mask; unsigned int white_threshold; unsigned int black_threshold; } ColorMask; extern const ColorMask Mask8TrueColor; extern const ColorMask Mask64TrueColor; extern const ColorMask Mask512TrueColor; extern const ColorMask Mask4KTrueColor; extern const ColorMask Mask32KTrueColor; extern const ColorMask Mask256KTrueColor; extern const ColorMask Mask2MTrueColor; extern const ColorMask Mask16MTrueColor; const ColorMask *MethodColorMask(unsigned int method); int MethodBitsPerPixel(unsigned int method); #ifdef __cplusplus } #endif #endif /* NXpack_H */ nxcomp/PutPackedImage.h0000644000076400007640000001330511323113030015313 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PutPackedImage_H #define PutPackedImage_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define PUTPACKEDIMAGE_ENABLE_CACHE 1 #define PUTPACKEDIMAGE_ENABLE_DATA 1 #define PUTPACKEDIMAGE_ENABLE_SPLIT 1 #define PUTPACKEDIMAGE_ENABLE_COMPRESS 0 // // We can't exceed lenght of 262144 bytes. // #define PUTPACKEDIMAGE_DATA_LIMIT 262144 - 40 #define PUTPACKEDIMAGE_DATA_OFFSET 40 #define PUTPACKEDIMAGE_CACHE_SLOTS 6000 #define PUTPACKEDIMAGE_CACHE_THRESHOLD 70 #define PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD 50 #define PUTPACKEDIMAGE_CACHE_THRESHOLD_IF_PACKED_SHADOW 97 #define PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED_SHADOW 90 #define PUTPACKEDIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8 0 // // The message class. // class PutPackedImageMessage : public Message { friend class PutPackedImageStore; public: PutPackedImageMessage() { } ~PutPackedImageMessage() { } // // Here are the fields which constitute // the 'identity' part of the message. // private: unsigned char client; unsigned int drawable; unsigned int gcontext; unsigned char format; unsigned char method; unsigned char src_depth; unsigned char dst_depth; unsigned int src_length; unsigned int dst_length; short int src_x; short int src_y; unsigned short src_width; unsigned short src_height; short int dst_x; short int dst_y; unsigned short dst_width; unsigned short dst_height; }; class PutPackedImageStore : public MessageStore { // // Constructors and destructors. // public: PutPackedImageStore(StaticCompressor *compressor); virtual ~PutPackedImageStore(); virtual const char *name() const { return "PutPackedImage"; } virtual unsigned char opcode() const { return X_NXPutPackedImage; } virtual unsigned int storage() const { return sizeof(PutPackedImageMessage); } // // Very special of this class. // int getPackedSize(const int position) const { PutPackedImageMessage *message = (PutPackedImageMessage *) (*messages_)[position]; if (message == NULL) { return 0; } return dataOffset + message -> src_length; } int getUnpackedSize(const int position) const { PutPackedImageMessage *message = (PutPackedImageMessage *) (*messages_)[position]; if (message == NULL) { return 0; } return dataOffset + message -> dst_length; } // // Message handling methods. // protected: virtual Message *create() const { return new PutPackedImageMessage(); } virtual Message *create(const Message &message) const { return new PutPackedImageMessage((const PutPackedImageMessage &) message); } virtual void destroy(Message *message) const { delete (PutPackedImageMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* PutPackedImage_H */ nxcomp/Timestamp.h0000644000076400007640000001406111323113027014441 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Timestamp_H #define Timestamp_H #include #include #include #include #include #include "Misc.h" // // Log level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // If not defined, always query the system time. // #undef CACHE_TIMESTAMP // // Log a warning if the time difference since // the last update exceeds the given number // of milliseconds. // #define DRIFT_TIMESTAMP 1 // // Type used for timeout manipulation. // typedef struct timeval T_timestamp; // // Last timestamp taken from the system. If the // timestamp is cached, we need to explicitly // get a new timestamp after any operation that // may have required a relevant amount of time. // extern T_timestamp timestamp; // // Get a timestamp instance with values set // at the given amount of milliseconds. // inline T_timestamp getTimestamp(long ms) { struct timeval ts; ts.tv_sec = ms / 1000; ts.tv_usec = (ms % 1000) * 1000; return ts; } // // Return the difference in milliseconds // between the two timestamps. // inline long diffTimestamp(const T_timestamp &ts1, const T_timestamp &ts2) { // // Add 500 microseconds to round up // to the nearest millisecond. // return ((ts2.tv_sec * 1000 + (ts2.tv_usec + 500) / 1000) - (ts1.tv_sec * 1000 + (ts1.tv_usec + 500) / 1000)); } // // The same in microseconds. It doesn't // round the value. // inline long diffUsTimestamp(const T_timestamp &ts1, const T_timestamp &ts2) { return ((ts2.tv_sec * 1000000 + ts2.tv_usec) - (ts1.tv_sec * 1000000 + ts1.tv_usec)); } // // Return the last timestamp taken from the // system. It doesn't update the timestamp. // inline T_timestamp getTimestamp() { #ifdef CACHE_TIMESTAMP #ifdef TEST T_timestamp ts; gettimeofday(&ts, NULL); long diffTs = diffTimestamp(timestamp, ts); if (diffTs > DRIFT_TIMESTAMP) { *logofs << "Timestamp: WARNING! Time difference since the " << "current timestamp is " << diffTs << " Ms.\n" << logofs_flush; } #endif return timestamp; #else gettimeofday(×tamp, NULL); return timestamp; #endif } inline T_timestamp &setTimestamp(T_timestamp &ts, long ms) { ts.tv_sec = ms / 1000; ts.tv_usec = (ms % 1000) * 1000; return ts; } // // Return the smaller between two timestamps. // inline T_timestamp &setMinTimestamp(T_timestamp &ts, long ms) { if ((ts.tv_sec * 1000 + ts.tv_usec / 1000) > ms) { ts.tv_sec = ms / 1000; ts.tv_usec = (ms % 1000) * 1000; } return ts; } inline T_timestamp &setMinTimestamp(T_timestamp &ts1, T_timestamp &ts2) { if ((ts1.tv_sec * 1000000 + ts1.tv_usec) > (ts2.tv_sec * 1000000 + ts2.tv_usec)) { ts1.tv_sec = ts2.tv_sec; ts1.tv_usec = ts2.tv_usec; } return ts1; } // // Convert a timestamp in the total number // of milliseconds. // inline long getMsTimestamp(const T_timestamp &ts) { return ts.tv_sec * 1000 + ts.tv_usec / 1000; } // // A 0 value on both seconds and microseconds // fields means that timestamp is invalid or // not set. // inline T_timestamp nullTimestamp() { struct timeval ts; ts.tv_sec = 0; ts.tv_usec = 0; return ts; } inline bool isTimestamp(const T_timestamp &ts) { if (ts.tv_sec == 0 && ts.tv_usec == 0) { return 0; } return 1; } inline void subMsTimestamp(T_timestamp &ts, long ms) { ts.tv_sec -= ms / 1000; ts.tv_usec -= (ms % 1000) * 1000; } inline void addMsTimestamp(T_timestamp &ts, long ms) { ts.tv_sec += ms / 1000; ts.tv_usec += (ms % 1000) * 1000; } // // Check the difference between timestamps. // Return 0 if the system time went backward // compared to the second timestamp, or the // difference between the timestamps exceeds // the given number of milliseconds. // inline int checkDiffTimestamp(const T_timestamp &ts1, const T_timestamp &ts2, long ms = 30000) { long diffTs = diffTimestamp(ts1, ts2); if (diffTs < 0 || diffTs > ms) { return 0; } return 1; } // // Return a string representing the timestamp. // char *strTimestamp(const T_timestamp &ts); char *strMsTimestamp(const T_timestamp &ts); inline char *strTimestamp() { return strTimestamp(getTimestamp()); } inline char *strMsTimestamp() { return strMsTimestamp(getTimestamp()); } // // Update the current timestamp. // inline T_timestamp getNewTimestamp() { #ifdef TEST T_timestamp ts; gettimeofday(&ts, NULL); *logofs << "Timestamp: Updating the current timestamp at " << strMsTimestamp(ts) << ".\n" << logofs_flush; long diffTs = diffTimestamp(timestamp, ts); if (diffTs > DRIFT_TIMESTAMP) { *logofs << "Timestamp: WARNING! Time difference since the " << "old timestamp is " << diffTs << " Ms.\n" << logofs_flush; } #endif gettimeofday(×tamp, NULL); return timestamp; } #endif /* Timestamp_H */ nxcomp/Agent.cpp0000644000076400007640000000423111323113026014064 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Misc.h" #include "Agent.h" #include "Proxy.h" extern Proxy *proxy; // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG Agent::Agent(int fd[2]) { remoteFd_ = fd[0]; localFd_ = fd[1]; transport_ = new AgentTransport(localFd_); if (transport_ == NULL) { #ifdef PANIC *logofs << "Agent: PANIC! Can't create the memory-to-memory transport " << "for FD#" << localFd_ << ".\n" << logofs_flush; #endif cerr << "Error" << ": Can't create the memory-to-memory transport " << "for FD#" << localFd_ << ".\n"; HandleCleanup(); } FD_ZERO(&saveRead_); FD_ZERO(&saveWrite_); canRead_ = 0; #ifdef DEBUG *logofs << "Agent: Created agent object at " << this << ".\n" << logofs_flush; #endif } Agent::~Agent() { delete transport_; #ifdef DEBUG *logofs << "Agent: Deleted agent object at " << this << ".\n" << logofs_flush; #endif } nxcomp/RenderTrapezoids.cpp0000644000076400007640000002624511323113026016323 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderTrapezoids.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { // // The trapezoid data is made up of a structure // containing a top and bottom coordinate in 4 // bytes format, plus two lines, each represent- // ed as four points in 4 bytes format. Thus // each trapezoid is 4 * 2 + (4 * 4) * 2 = 40 // bytes. Bytes are all padded to an long int, // so we don't need to clean the message. // ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding value " << ((size - MESSAGE_OFFSET) >> 2) << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); #ifdef DEBUG *logofs << name() << ": Decoded value " << size << ".\n" << logofs_flush; #endif size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> renderSrcPictureCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), clientCache -> renderDstPictureCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, clientCache -> renderFormatCache); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian), clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian), clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> renderDstPictureCache); PutULONG(value, buffer + 12, bigEndian); decodeBuffer.decodeCachedValue(value, 32, clientCache -> renderFormatCache); PutULONG(value, buffer + 16, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastX, 16, clientCache -> renderXCache, 11); PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian); decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastY, 16, clientCache -> renderYCache, 11); PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { if (size > MESSAGE_OFFSET) { encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); } #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of text data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { if (size > MESSAGE_OFFSET) { decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); } #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.trapezoids.type = *(buffer + 1); renderExtension -> data.trapezoids.op = *(buffer + 4); renderExtension -> data.trapezoids.src_id = GetULONG(buffer + 8, bigEndian); renderExtension -> data.trapezoids.dst_id = GetULONG(buffer + 12, bigEndian); renderExtension -> data.trapezoids.format = GetULONG(buffer + 16, bigEndian); renderExtension -> data.trapezoids.src_x = GetUINT(buffer + 20, bigEndian); renderExtension -> data.trapezoids.src_y = GetUINT(buffer + 22, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.trapezoids.type << " size is " << renderExtension -> size_ << " identity size " << renderExtension -> i_size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.trapezoids.type; *(buffer + 4) = renderExtension -> data.trapezoids.op; PutULONG(renderExtension -> data.trapezoids.src_id, buffer + 8, bigEndian); PutULONG(renderExtension -> data.trapezoids.dst_id, buffer + 12, bigEndian); PutULONG(renderExtension -> data.trapezoids.format, buffer + 16, bigEndian); PutUINT(renderExtension -> data.trapezoids.src_x, buffer + 20, bigEndian); PutUINT(renderExtension -> data.trapezoids.src_y, buffer + 22, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.trapezoids.type << " size is " << renderExtension -> size_ << " identity size " << renderExtension -> i_size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { // // Include minor opcode, size and the // operator in the identity. // md5_append(md5_state, buffer + 1, 4); // // Also include the format but not the // x and y source. // md5_append(md5_state, buffer + 16, 4); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(renderExtension -> data.trapezoids.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.trapezoids.src_id = renderExtension -> data.trapezoids.src_id; encodeBuffer.encodeXidValue(renderExtension -> data.trapezoids.dst_id, clientCache -> renderDstPictureCache); cachedRenderExtension -> data.trapezoids.dst_id = renderExtension -> data.trapezoids.dst_id; // // The source x and y coordinates are // encoded as differerences in respect // to the previous cached value. // unsigned int value; unsigned int previous; value = renderExtension -> data.trapezoids.src_x; previous = cachedRenderExtension -> data.trapezoids.src_x; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderXCache, 11); cachedRenderExtension -> data.trapezoids.src_x = value; value = renderExtension -> data.trapezoids.src_y; previous = cachedRenderExtension -> data.trapezoids.src_y; encodeBuffer.encodeDiffCachedValue(value, previous, 16, clientCache -> renderYCache, 11); cachedRenderExtension -> data.trapezoids.src_y = value; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.trapezoids.type << " size is " << renderExtension -> size_ << " source x " << renderExtension -> data.trapezoids.src_x << " y " << renderExtension -> data.trapezoids.src_y << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.trapezoids.src_id, clientCache -> renderSrcPictureCache); decodeBuffer.decodeXidValue(renderExtension -> data.trapezoids.dst_id, clientCache -> renderDstPictureCache); unsigned int value; unsigned int previous; previous = renderExtension -> data.trapezoids.src_x; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderXCache, 11); renderExtension -> data.trapezoids.src_x = value; previous = renderExtension -> data.trapezoids.src_y; decodeBuffer.decodeDiffCachedValue(value, previous, 16, clientCache -> renderYCache, 11); renderExtension -> data.trapezoids.src_y = value; #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.trapezoids.type << " size is " << renderExtension -> size_ << " source x " << renderExtension -> data.trapezoids.src_x << " y " << renderExtension -> data.trapezoids.src_y << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/RenderAddGlyphs.cpp0000644000076400007640000001512411323113027016051 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderAddGlyphs.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(GetULONG(buffer + 4, bigEndian), 29, clientCache -> renderGlyphSetCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 32, clientCache -> renderNumGlyphsCache, 8); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeCachedValue(value, 29, clientCache -> renderGlyphSetCache); PutULONG(value, buffer + 4, bigEndian); decodeBuffer.decodeCachedValue(value, 32, clientCache -> renderNumGlyphsCache, 8); PutULONG(value, buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.add_glyphs.type = *(buffer + 1); renderExtension -> data.add_glyphs.set_id = GetULONG(buffer + 4, bigEndian); renderExtension -> data.add_glyphs.num_elm = GetULONG(buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.add_glyphs.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.add_glyphs.type; PutULONG(renderExtension -> data.add_glyphs.set_id, buffer + 4, bigEndian); PutULONG(renderExtension -> data.add_glyphs.num_elm, buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.add_glyphs.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); md5_append(md5_state, buffer + 8, 4); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(renderExtension -> data.add_glyphs.set_id, 29, clientCache -> renderGlyphSetCache); cachedRenderExtension -> data.add_glyphs.set_id = renderExtension -> data.add_glyphs.set_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.add_glyphs.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(renderExtension -> data.add_glyphs.set_id, 29, clientCache -> renderGlyphSetCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.add_glyphs.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/Statistics.cpp0000644000076400007640000015166311323113030015167 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include "Statistics.h" #include "Control.h" #include "Proxy.h" #include "ClientStore.h" #include "ServerStore.h" // // Length of temporary buffer // used to format output. // #define FORMAT_LENGTH 1024 // // Log level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Note that when presenting statistics we invert the // correct semantics of X client and server entities. // This is questionable, but matches the user's pers- // pective of running remote X applications on its // local client. // Statistics::Statistics(Proxy *proxy) : proxy_(proxy) { transportPartial_.idleTime_ = 0; transportPartial_.readTime_ = 0; transportPartial_.writeTime_ = 0; transportPartial_.proxyBytesIn_ = 0; transportPartial_.proxyBytesOut_ = 0; transportPartial_.proxyFramesIn_ = 0; transportPartial_.proxyFramesOut_ = 0; transportPartial_.proxyWritesOut_ = 0; transportPartial_.compressedBytesIn_ = 0; transportPartial_.compressedBytesOut_ = 0; transportPartial_.decompressedBytesIn_ = 0; transportPartial_.decompressedBytesOut_ = 0; transportPartial_.framingBitsOut_ = 0; transportTotal_.idleTime_ = 0; transportTotal_.readTime_ = 0; transportTotal_.writeTime_ = 0; transportTotal_.proxyBytesIn_ = 0; transportTotal_.proxyBytesOut_ = 0; transportTotal_.proxyFramesIn_ = 0; transportTotal_.proxyFramesOut_ = 0; transportTotal_.proxyWritesOut_ = 0; transportTotal_.compressedBytesIn_ = 0; transportTotal_.compressedBytesOut_ = 0; transportTotal_.decompressedBytesIn_ = 0; transportTotal_.decompressedBytesOut_ = 0; transportTotal_.framingBitsOut_ = 0; for (int i = 0; i < STATISTICS_OPCODE_MAX; i++) { protocolPartial_.requestCached_[i] = 0; protocolPartial_.requestReplied_[i] = 0; protocolPartial_.requestCount_[i] = 0; protocolPartial_.requestBitsIn_[i] = 0; protocolPartial_.requestBitsOut_[i] = 0; protocolPartial_.renderRequestCached_[i] = 0; protocolPartial_.renderRequestCount_[i] = 0; protocolPartial_.renderRequestBitsIn_[i] = 0; protocolPartial_.renderRequestBitsOut_[i] = 0; protocolPartial_.replyCached_[i] = 0; protocolPartial_.replyCount_[i] = 0; protocolPartial_.replyBitsIn_[i] = 0; protocolPartial_.replyBitsOut_[i] = 0; protocolPartial_.eventCached_[i] = 0; protocolPartial_.eventCount_[i] = 0; protocolPartial_.eventBitsIn_[i] = 0; protocolPartial_.eventBitsOut_[i] = 0; protocolTotal_.requestCached_[i] = 0; protocolTotal_.requestReplied_[i] = 0; protocolTotal_.requestCount_[i] = 0; protocolTotal_.requestBitsIn_[i] = 0; protocolTotal_.requestBitsOut_[i] = 0; protocolTotal_.renderRequestCached_[i] = 0; protocolTotal_.renderRequestCount_[i] = 0; protocolTotal_.renderRequestBitsIn_[i] = 0; protocolTotal_.renderRequestBitsOut_[i] = 0; protocolTotal_.replyCached_[i] = 0; protocolTotal_.replyCount_[i] = 0; protocolTotal_.replyBitsIn_[i] = 0; protocolTotal_.replyBitsOut_[i] = 0; protocolTotal_.eventCached_[i] = 0; protocolTotal_.eventCount_[i] = 0; protocolTotal_.eventBitsIn_[i] = 0; protocolTotal_.eventBitsOut_[i] = 0; } protocolPartial_.cupsCount_ = 0; protocolPartial_.cupsBitsIn_ = 0; protocolPartial_.cupsBitsOut_ = 0; protocolPartial_.smbCount_ = 0; protocolPartial_.smbBitsIn_ = 0; protocolPartial_.smbBitsOut_ = 0; protocolPartial_.mediaCount_ = 0; protocolPartial_.mediaBitsIn_ = 0; protocolPartial_.mediaBitsOut_ = 0; protocolPartial_.httpCount_ = 0; protocolPartial_.httpBitsIn_ = 0; protocolPartial_.httpBitsOut_ = 0; protocolPartial_.fontCount_ = 0; protocolPartial_.fontBitsIn_ = 0; protocolPartial_.fontBitsOut_ = 0; protocolPartial_.slaveCount_ = 0; protocolPartial_.slaveBitsIn_ = 0; protocolPartial_.slaveBitsOut_ = 0; protocolTotal_.cupsCount_ = 0; protocolTotal_.cupsBitsIn_ = 0; protocolTotal_.cupsBitsOut_ = 0; protocolTotal_.smbCount_ = 0; protocolTotal_.smbBitsIn_ = 0; protocolTotal_.smbBitsOut_ = 0; protocolTotal_.mediaCount_ = 0; protocolTotal_.mediaBitsIn_ = 0; protocolTotal_.mediaBitsOut_ = 0; protocolTotal_.httpCount_ = 0; protocolTotal_.httpBitsIn_ = 0; protocolTotal_.httpBitsOut_ = 0; protocolTotal_.fontCount_ = 0; protocolTotal_.fontBitsIn_ = 0; protocolTotal_.fontBitsOut_ = 0; protocolTotal_.slaveCount_ = 0; protocolTotal_.slaveBitsIn_ = 0; protocolTotal_.slaveBitsOut_ = 0; packedPartial_.packedBytesIn_ = 0; packedPartial_.packedBytesOut_ = 0; packedTotal_.packedBytesIn_ = 0; packedTotal_.packedBytesOut_ = 0; splitPartial_.splitCount_ = 0; splitPartial_.splitAborted_ = 0; splitPartial_.splitAbortedBytesOut_ = 0; splitTotal_.splitCount_ = 0; splitTotal_.splitAborted_ = 0; splitTotal_.splitAbortedBytesOut_ = 0; overallPartial_.overallBytesIn_ = 0; overallPartial_.overallBytesOut_ = 0; overallTotal_.overallBytesIn_ = 0; overallTotal_.overallBytesOut_ = 0; proxyData_.protocolCount_ = 0; proxyData_.controlCount_ = 0; proxyData_.splitCount_ = 0; proxyData_.dataCount_ = 0; proxyData_.streamRatio_ = 1; startShortFrameTs_ = getTimestamp(); startLongFrameTs_ = getTimestamp(); startFrameTs_ = getTimestamp(); bytesInShortFrame_ = 0; bytesInLongFrame_ = 0; bitrateInShortFrame_ = 0; bitrateInLongFrame_ = 0; topBitrate_ = 0; congestionInFrame_ = 0; } Statistics::~Statistics() { } int Statistics::resetPartialStats() { transportPartial_.idleTime_ = 0; transportPartial_.readTime_ = 0; transportPartial_.writeTime_ = 0; transportPartial_.proxyBytesIn_ = 0; transportPartial_.proxyBytesOut_ = 0; transportPartial_.proxyFramesIn_ = 0; transportPartial_.proxyFramesOut_ = 0; transportPartial_.proxyWritesOut_ = 0; transportPartial_.compressedBytesIn_ = 0; transportPartial_.compressedBytesOut_ = 0; transportPartial_.decompressedBytesIn_ = 0; transportPartial_.decompressedBytesOut_ = 0; transportPartial_.framingBitsOut_ = 0; for (int i = 0; i < STATISTICS_OPCODE_MAX; i++) { protocolPartial_.requestCached_[i] = 0; protocolPartial_.requestReplied_[i] = 0; protocolPartial_.requestCount_[i] = 0; protocolPartial_.requestBitsIn_[i] = 0; protocolPartial_.requestBitsOut_[i] = 0; protocolPartial_.renderRequestCached_[i] = 0; protocolPartial_.renderRequestCount_[i] = 0; protocolPartial_.renderRequestBitsIn_[i] = 0; protocolPartial_.renderRequestBitsOut_[i] = 0; protocolPartial_.replyCached_[i] = 0; protocolPartial_.replyCount_[i] = 0; protocolPartial_.replyBitsIn_[i] = 0; protocolPartial_.replyBitsOut_[i] = 0; protocolPartial_.eventCached_[i] = 0; protocolPartial_.eventCount_[i] = 0; protocolPartial_.eventBitsIn_[i] = 0; protocolPartial_.eventBitsOut_[i] = 0; } protocolPartial_.cupsCount_ = 0; protocolPartial_.cupsBitsIn_ = 0; protocolPartial_.cupsBitsOut_ = 0; protocolPartial_.smbCount_ = 0; protocolPartial_.smbBitsIn_ = 0; protocolPartial_.smbBitsOut_ = 0; protocolPartial_.mediaCount_ = 0; protocolPartial_.mediaBitsIn_ = 0; protocolPartial_.mediaBitsOut_ = 0; protocolPartial_.httpCount_ = 0; protocolPartial_.httpBitsIn_ = 0; protocolPartial_.httpBitsOut_ = 0; protocolPartial_.fontCount_ = 0; protocolPartial_.fontBitsIn_ = 0; protocolPartial_.fontBitsOut_ = 0; protocolPartial_.slaveCount_ = 0; protocolPartial_.slaveBitsIn_ = 0; protocolPartial_.slaveBitsOut_ = 0; packedPartial_.packedBytesIn_ = 0; packedPartial_.packedBytesOut_ = 0; splitPartial_.splitCount_ = 0; splitPartial_.splitAborted_ = 0; splitPartial_.splitAbortedBytesOut_ = 0; overallPartial_.overallBytesIn_ = 0; overallPartial_.overallBytesOut_ = 0; return 1; } void Statistics::addCompressedBytes(unsigned int bytesIn, unsigned int bytesOut) { transportPartial_.compressedBytesIn_ += bytesIn; transportTotal_.compressedBytesIn_ += bytesIn; transportPartial_.compressedBytesOut_ += bytesOut; transportTotal_.compressedBytesOut_ += bytesOut; double ratio = bytesIn / bytesOut; if (ratio < 1) { ratio = 1; } #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! Old ratio was " << proxyData_.streamRatio_ << " current is " << (double) ratio << " new ratio is " << (double) ((proxyData_.streamRatio_ * 2) + ratio) / 3 << ".\n" << logofs_flush; #endif proxyData_.streamRatio_ = ((proxyData_.streamRatio_ * 2) + ratio) / 3; #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! Updated compressed bytes " << "with " << bytesIn << " in " << bytesOut << " out " << "and ratio " << (double) proxyData_.streamRatio_ << ".\n" << logofs_flush; #endif } // // Recalculate the current bitrate. The bytes written // are accounted at the time the transport actually // writes the data to the network, not at the time it // receives the data from the upper layers. The reason // is that data can be compressed by the stream com- // pressor, so we can become aware of the new bitrate // only afer having flushed the ZLIB stream. This also // means that, to have a reliable estimate, we need to // flush the link often. // void Statistics::updateBitrate(int bytes) { T_timestamp thisFrameTs = getNewTimestamp(); int diffFramesInMs = diffTimestamp(startFrameTs_, thisFrameTs); #ifdef DEBUG *logofs << "Statistics: Difference since previous timestamp is " << diffFramesInMs << " Ms.\n" << logofs_flush; #endif if (diffFramesInMs > 0) { #ifdef DEBUG *logofs << "Statistics: Removing " << diffFramesInMs << " Ms in short and long time frame.\n" << logofs_flush; #endif int shortBytesToRemove = (int) (((double) bytesInShortFrame_ * (double) diffFramesInMs) / (double) control -> ShortBitrateTimeFrame); int longBytesToRemove = (int) (((double) bytesInLongFrame_ * (double) diffFramesInMs) / (double) control -> LongBitrateTimeFrame); #ifdef DEBUG *logofs << "Statistics: Removing " << shortBytesToRemove << " bytes from " << bytesInShortFrame_ << " in the short frame.\n" << logofs_flush; #endif bytesInShortFrame_ -= shortBytesToRemove; if (bytesInShortFrame_ < 0) { #ifdef TEST *logofs << "Statistics: Bytes in short frame are " << bytesInShortFrame_ << ". Set to 0.\n" << logofs_flush; #endif bytesInShortFrame_ = 0; } #ifdef DEBUG *logofs << "Statistics: Removing " << longBytesToRemove << " bytes from " << bytesInLongFrame_ << " in the long frame.\n" << logofs_flush; #endif bytesInLongFrame_ -= longBytesToRemove; if (bytesInLongFrame_ < 0) { #ifdef TEST *logofs << "Statistics: Bytes in long frame are " << bytesInLongFrame_ << ". Set to 0.\n" << logofs_flush; #endif bytesInLongFrame_ = 0; } int diffStartInMs; diffStartInMs = diffTimestamp(thisFrameTs, startShortFrameTs_); if (diffStartInMs > control -> ShortBitrateTimeFrame) { addMsTimestamp(startShortFrameTs_, diffStartInMs); } diffStartInMs = diffTimestamp(thisFrameTs, startLongFrameTs_); if (diffStartInMs > control -> LongBitrateTimeFrame) { addMsTimestamp(startLongFrameTs_, diffStartInMs); } startFrameTs_ = thisFrameTs; } #ifdef DEBUG *logofs << "Statistics: Adding " << bytes << " bytes to " << bytesInShortFrame_ << " in the short frame.\n" << logofs_flush; #endif bytesInShortFrame_ = bytesInShortFrame_ + bytes; #ifdef DEBUG *logofs << "Statistics: Adding " << bytes << " bytes to " << bytesInLongFrame_ << " in the long frame.\n" << logofs_flush; #endif bytesInLongFrame_ = bytesInLongFrame_ + bytes; bitrateInShortFrame_ = (int) ((double) bytesInShortFrame_ / ((double) control -> ShortBitrateTimeFrame / 1000)); bitrateInLongFrame_ = (int) ((double) bytesInLongFrame_ / ((double) control -> LongBitrateTimeFrame / 1000)); if (bitrateInShortFrame_ > topBitrate_) { topBitrate_ = bitrateInShortFrame_; } #ifdef TEST *logofs << "Statistics: Current bitrate is short " << bitrateInShortFrame_ << " long " << bitrateInLongFrame_ << " top " << topBitrate_ << ".\n" << logofs_flush; #endif } void Statistics::updateCongestion(int remaining, int limit) { #ifdef TEST *logofs << "Statistics: Updating the congestion " << "counters at " << strMsTimestamp() << ".\n" << logofs_flush; #endif double current = remaining; if (current < 0) { current = 0; } current = 9 * (limit - current) / limit; #ifdef TEST *logofs << "Statistics: Current congestion is " << current << " with " << limit << " tokens " << "and " << remaining << " remaining.\n" << logofs_flush; #endif // // If the current congestion counter is greater // than the previous, take the current value, // otherwise ramp down the value by calculating // the average of the last 8 updates. // #ifdef TEST *logofs << "Statistics: Old congestion was " << congestionInFrame_; #endif if (current >= congestionInFrame_) { congestionInFrame_ = current; } else { congestionInFrame_ = ((congestionInFrame_ * 7) + current) / 8; } #ifdef TEST *logofs << " new congestion is " << ((congestionInFrame_ * 7) + current) / 8 << ".\n" << logofs_flush; #endif // // Call the function with 0 bytes flushed // so the agent can update its congestion // counter. // FlushCallback(0); } int Statistics::getClientCacheStats(int type, char *&buffer) { if (type != PARTIAL_STATS && type != TOTAL_STATS) { #ifdef PANIC *logofs << "Statistics: PANIC! Cannot produce statistics " << "with qualifier '" << type << "'.\n" << logofs_flush; #endif return -1; } // // Print message cache data according // to local and remote view. // MessageStore *currentStore = NULL; MessageStore *anyStore = NULL; char format[FORMAT_LENGTH]; strcat(buffer, "\nNX Cache Statistics\n"); strcat(buffer, "-------------------\n\n"); for (int m = proxy_client; m <= proxy_server; m++) { if (m == proxy_client) { strcat(buffer, "Request\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n"); strcat(buffer, "-------\t------\t--------------\t\t--------------\t\t-----------\n"); } else { strcat(buffer, "\nReply\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n"); strcat(buffer, "-----\t------\t--------------\t\t--------------\t\t-----------\n"); } for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { if (m == proxy_client) { currentStore = proxy_ -> getClientStore() -> getRequestStore(i); } else { currentStore = proxy_ -> getServerStore() -> getReplyStore(i); } if (currentStore != NULL && (currentStore -> getLocalStorageSize() || currentStore -> getRemoteStorageSize())) { anyStore = currentStore; sprintf(format, "#%d\t%d\t", i, currentStore -> getSize()); strcat(buffer, format); sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getLocalStorageSize(), ((double) currentStore -> getLocalStorageSize()) / 1024); strcat(buffer, format); sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getRemoteStorageSize(), ((double) currentStore -> getRemoteStorageSize()) / 1024); strcat(buffer, format); sprintf(format, "%d/%.0f KB\n", currentStore -> cacheSlots, ((double) control -> getUpperStorageSize() / 100 * currentStore -> cacheThreshold) / 1024); strcat(buffer, format); } } if (anyStore == NULL) { strcat(buffer, "N/A\n"); } } if (anyStore != NULL) { sprintf(format, "\ncache: %d bytes (%d KB) available at server.\n", control -> ClientTotalStorageSize, control -> ClientTotalStorageSize / 1024); strcat(buffer, format); sprintf(format, " %d bytes (%d KB) available at client.\n\n", control -> ServerTotalStorageSize, control -> ServerTotalStorageSize / 1024); strcat(buffer, format); sprintf(format, " %d bytes (%d KB) allocated at server.\n", anyStore -> getLocalTotalStorageSize(), anyStore -> getLocalTotalStorageSize() / 1024); strcat(buffer, format); sprintf(format, " %d bytes (%d KB) allocated at client.\n\n\n", anyStore -> getRemoteTotalStorageSize(), anyStore -> getRemoteTotalStorageSize() / 1024); strcat(buffer, format); } else { strcat(buffer, "\ncache: N/A\n\n"); } return 1; } int Statistics::getClientProtocolStats(int type, char *&buffer) { if (type != PARTIAL_STATS && type != TOTAL_STATS) { #ifdef PANIC *logofs << "Statistics: PANIC! Cannot produce statistics " << "with qualifier '" << type << "'.\n" << logofs_flush; #endif return -1; } struct T_transportData *transportData; struct T_protocolData *protocolData; struct T_overallData *overallData; if (type == PARTIAL_STATS) { transportData = &transportPartial_; protocolData = &protocolPartial_; overallData = &overallPartial_; } else { transportData = &transportTotal_; protocolData = &protocolTotal_; overallData = &overallTotal_; } char format[FORMAT_LENGTH]; double countRequestIn = 0; double countCachedRequestIn = 0; double countRepliedRequestIn = 0; double countRequestBitsIn = 0; double countRequestBitsOut = 0; double countAnyIn = 0; double countBitsIn = 0; double countBitsOut = 0; // // Print request data. // strcat(buffer, "NX Server Side Protocol Statistics\n"); strcat(buffer, "----------------------------------\n\n"); // // Print render data. // strcat(buffer, "Render Total\tCached\tBits In\t\tBits Out\tBits/Request\t\tRatio\n"); strcat(buffer, "------- -----\t------\t-------\t\t--------\t------------\t\t-----\n"); for (int i = 0; i < STATISTICS_OPCODE_MAX; i++) { if (protocolData -> renderRequestCount_[i]) { sprintf(format, "#%d ", i); while (strlen(format) < 8) { strcat(format, " "); } strcat(buffer, format); if (protocolData -> renderRequestCached_[i] > 0) { sprintf(format, "%.0f\t%.0f", protocolData -> renderRequestCount_[i], protocolData -> renderRequestCached_[i]); } else { sprintf(format, "%.0f\t", protocolData -> renderRequestCount_[i]); } strcat(buffer, format); sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t", protocolData -> renderRequestBitsIn_[i], protocolData -> renderRequestBitsIn_[i] / 8192, protocolData -> renderRequestBitsOut_[i], protocolData -> renderRequestBitsOut_[i] / 8192, protocolData -> renderRequestBitsIn_[i] / protocolData -> renderRequestCount_[i], protocolData -> renderRequestBitsOut_[i] / protocolData -> renderRequestCount_[i]); strcat(buffer, format); if (protocolData -> renderRequestBitsOut_[i] > 0) { sprintf(format, "%5.3f:1\n", protocolData -> renderRequestBitsIn_[i] / protocolData -> renderRequestBitsOut_[i]); strcat(buffer, format); } else { strcat(buffer, "1:1\n"); } } countRequestIn += protocolData -> renderRequestCount_[i]; countCachedRequestIn += protocolData -> renderRequestCached_[i]; countRequestBitsIn += protocolData -> renderRequestBitsIn_[i]; countRequestBitsOut += protocolData -> renderRequestBitsOut_[i]; countAnyIn += protocolData -> renderRequestCount_[i]; countBitsIn += protocolData -> renderRequestBitsIn_[i]; countBitsOut += protocolData -> renderRequestBitsOut_[i]; } if (countRequestIn > 0) { if (countCachedRequestIn > 0) { sprintf(format, "\ntotal: %.0f\t%.0f", countRequestIn, countCachedRequestIn); } else { sprintf(format, "\ntotal: %.0f\t", countRequestIn); } strcat(buffer, format); sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t", countRequestBitsIn, countRequestBitsIn / 8192, countRequestBitsOut, countRequestBitsOut / 8192, countRequestBitsIn / countRequestIn, countRequestBitsOut / countRequestIn); strcat(buffer, format); if (countRequestBitsOut > 0) { sprintf(format, "%5.3f:1\n", countRequestBitsIn / countRequestBitsOut); } else { sprintf(format, "1:1\n"); } strcat(buffer, format); } else { strcat(buffer, "N/A\n\n"); } countRequestIn = 0; countCachedRequestIn = 0; countRequestBitsIn = 0; countRequestBitsOut = 0; countAnyIn = 0; countBitsIn = 0; countBitsOut = 0; // // Print other requests' data. // strcat(buffer, "\nRequest Total\tCached\tBits In\t\tBits Out\tBits/Request\t\tRatio\n"); strcat(buffer, "------- -----\t------\t-------\t\t--------\t------------\t\t-----\n"); for (int i = 0; i < STATISTICS_OPCODE_MAX; i++) { if (protocolData -> requestCount_[i]) { sprintf(format, "#%d ", i); while (strlen(format) < 5) { strcat(format, " "); } // // Mark NX agent-related requests, those // having a reply and finally those that // have been probably tainted by client // side. // if (i >= X_NXFirstOpcode && i <= X_NXLastOpcode) { strcat(format, "A"); } if (i != X_NXInternalGenericReply && protocolData -> requestReplied_[i] > 0) { strcat(format, "R"); } if (i == X_NoOperation && control -> TaintReplies) { strcat(format, "T"); } while (strlen(format) < 8) { strcat(format, " "); } strcat(buffer, format); if (protocolData -> requestCached_[i] > 0) { sprintf(format, "%.0f\t%.0f", protocolData -> requestCount_[i], protocolData -> requestCached_[i]); } else { sprintf(format, "%.0f\t", protocolData -> requestCount_[i]); } strcat(buffer, format); sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t", protocolData -> requestBitsIn_[i], protocolData -> requestBitsIn_[i] / 8192, protocolData -> requestBitsOut_[i], protocolData -> requestBitsOut_[i] / 8192, protocolData -> requestBitsIn_[i] / protocolData -> requestCount_[i], protocolData -> requestBitsOut_[i] / protocolData -> requestCount_[i]); strcat(buffer, format); if (protocolData -> requestBitsOut_[i] > 0) { sprintf(format, "%5.3f:1\n", protocolData -> requestBitsIn_[i] / protocolData -> requestBitsOut_[i]); strcat(buffer, format); } else { strcat(buffer, "1:1\n"); } } countRequestIn += protocolData -> requestCount_[i]; countCachedRequestIn += protocolData -> requestCached_[i]; countRepliedRequestIn += protocolData -> requestReplied_[i]; countRequestBitsIn += protocolData -> requestBitsIn_[i]; countRequestBitsOut += protocolData -> requestBitsOut_[i]; countAnyIn += protocolData -> requestCount_[i]; countBitsIn += protocolData -> requestBitsIn_[i]; countBitsOut += protocolData -> requestBitsOut_[i]; } if (countRequestIn > 0) { if (countCachedRequestIn > 0) { sprintf(format, "\ntotal: %.0f\t%.0f", countRequestIn, countCachedRequestIn); } else { sprintf(format, "\ntotal: %.0f\t", countRequestIn); } strcat(buffer, format); sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t", countRequestBitsIn, countRequestBitsIn / 8192, countRequestBitsOut, countRequestBitsOut / 8192, countRequestBitsIn / countRequestIn, countRequestBitsOut / countRequestIn); strcat(buffer, format); if (countRequestBitsOut > 0) { sprintf(format, "%5.3f:1\n", countRequestBitsIn / countRequestBitsOut); } else { sprintf(format, "1:1\n"); } strcat(buffer, format); } else { strcat(buffer, "N/A\n\n"); } // // Print transport data. // getTimeStats(type, buffer); countAnyIn += protocolData -> cupsCount_; countBitsIn += protocolData -> cupsBitsIn_; countBitsOut += protocolData -> cupsBitsOut_; countAnyIn += protocolData -> smbCount_; countBitsIn += protocolData -> smbBitsIn_; countBitsOut += protocolData -> smbBitsOut_; countAnyIn += protocolData -> mediaCount_; countBitsIn += protocolData -> mediaBitsIn_; countBitsOut += protocolData -> mediaBitsOut_; countAnyIn += protocolData -> httpCount_; countBitsIn += protocolData -> httpBitsIn_; countBitsOut += protocolData -> httpBitsOut_; countAnyIn += protocolData -> fontCount_; countBitsIn += protocolData -> fontBitsIn_; countBitsOut += protocolData -> fontBitsOut_; countAnyIn += protocolData -> slaveCount_; countBitsIn += protocolData -> slaveBitsIn_; countBitsOut += protocolData -> slaveBitsOut_; // // Save the overall amount of bytes // coming from X clients. // overallData -> overallBytesIn_ = countBitsIn / 8; // // Print performance data. // if (transportData -> readTime_ > 0) { sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n", countAnyIn / (transportData -> readTime_ / 1000), (countBitsIn + transportData -> framingBitsOut_) / 8192 / (transportData -> readTime_ / 1000)); } else { sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n", countAnyIn, (countBitsIn + transportData -> framingBitsOut_) / 8192); } strcat(buffer, format); strcat(buffer, "link: "); // // ZLIB compression stats. // getStreamStats(type, buffer); // // Save the overall amount of bytes // sent on NX proxy link. // if (transportData -> compressedBytesOut_ > 0) { overallData -> overallBytesOut_ = transportData -> compressedBytesOut_; } else { overallData -> overallBytesOut_ = countBitsOut / 8; } // // Print info on multiplexing overhead. // getFramingStats(type, buffer); // // Print stats about additional channels. // getServicesStats(type, buffer); // // Compression summary. // double ratio = 1; if (transportData -> compressedBytesOut_ / 1024 > 0) { ratio = ((countBitsIn + transportData -> framingBitsOut_) / 8192) / (transportData -> compressedBytesOut_ / 1024); } else if (countBitsOut > 0) { ratio = (countBitsIn + transportData -> framingBitsOut_) / countBitsOut; } sprintf(format, " Protocol compression ratio is %5.3f:1.\n\n", ratio); strcat(buffer, format); getBitrateStats(type, buffer); getSplitStats(type, buffer); sprintf(format, " %.0f total handled replies (%.0f unmatched).\n\n\n", countRepliedRequestIn, protocolData -> requestReplied_[X_NXInternalGenericReply]); strcat(buffer, format); return 1; } int Statistics::getClientOverallStats(int type, char *&buffer) { if (type != PARTIAL_STATS && type != TOTAL_STATS) { #ifdef PANIC *logofs << "Statistics: PANIC! Cannot produce statistics " << "with qualifier '" << type << "'.\n" << logofs_flush; #endif return -1; } struct T_overallData *overallData; struct T_packedData *packedData; if (type == PARTIAL_STATS) { overallData = &overallPartial_; packedData = &packedPartial_; } else { overallData = &overallTotal_; packedData = &packedTotal_; } char format[FORMAT_LENGTH]; // // Print header including link type, // followed by info on packed images. // strcat(buffer, "NX Compression Summary\n"); strcat(buffer, "----------------------\n\n"); char label[FORMAT_LENGTH]; switch (control -> LinkMode) { case LINK_TYPE_NONE: { strcpy(label, "NONE"); break; } case LINK_TYPE_MODEM: { strcpy(label, "MODEM"); break; } case LINK_TYPE_ISDN: { strcpy(label, "ISDN"); break; } case LINK_TYPE_ADSL: { strcpy(label, "ADSL"); break; } case LINK_TYPE_WAN: { strcpy(label, "WAN"); break; } case LINK_TYPE_LAN: { strcpy(label, "LAN"); break; } default: { strcpy(label, "Unknown"); break; } } sprintf(format, "link: %s", label); if (control -> LocalDeltaCompression == 1) { strcat(format, " with protocol compression enabled."); } else { strcat(format, " with protocol compression disabled."); } strcat(format, "\n\n"); strcat(buffer, format); if (packedData -> packedBytesIn_ > 0) { sprintf(format, "images: %.0f bytes (%.0f KB) packed to %.0f (%.0f KB).\n\n", packedData -> packedBytesOut_, packedData -> packedBytesOut_ / 1024, packedData -> packedBytesIn_, packedData -> packedBytesIn_ / 1024); strcat(buffer, format); sprintf(format, " Images compression ratio is %5.3f:1.\n\n", packedData -> packedBytesOut_ / packedData -> packedBytesIn_); strcat(buffer, format); } double overallIn = overallData -> overallBytesIn_ - packedData -> packedBytesIn_ + packedData -> packedBytesOut_; double overallOut = overallData -> overallBytesOut_; sprintf(format, "overall: %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n", overallIn, overallIn / 1024, overallOut, overallOut / 1024); strcat(buffer, format); if (overallData -> overallBytesOut_ > 0) { sprintf(format, " Overall NX server compression ratio is %5.3f:1.\n\n\n", overallIn / overallOut); } else { sprintf(format, " Overall NX server compression ratio is 1:1.\n\n\n"); } strcat(buffer, format); return 1; } int Statistics::getServerCacheStats(int type, char *&buffer) { if (type != PARTIAL_STATS && type != TOTAL_STATS) { #ifdef PANIC *logofs << "Statistics: PANIC! Cannot produce statistics " << "with qualifier '" << type << "'.\n" << logofs_flush; #endif return -1; } // // Print message cache data according // to local and remote view. // MessageStore *currentStore = NULL; MessageStore *anyStore = NULL; char format[FORMAT_LENGTH]; strcat(buffer, "\nNX Cache Statistics\n"); strcat(buffer, "-------------------\n\n"); for (int m = proxy_client; m <= proxy_server; m++) { if (m == proxy_client) { strcat(buffer, "Request\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n"); strcat(buffer, "-------\t------\t--------------\t\t--------------\t\t-----------\n"); } else { strcat(buffer, "\nReply\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n"); strcat(buffer, "-----\t------\t--------------\t\t--------------\t\t-----------\n"); } for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) { if (m == proxy_client) { currentStore = proxy_ -> getClientStore() -> getRequestStore(i); } else { currentStore = proxy_ -> getServerStore() -> getReplyStore(i); } if (currentStore != NULL && (currentStore -> getLocalStorageSize() || currentStore -> getRemoteStorageSize())) { anyStore = currentStore; sprintf(format, "#%d\t%d\t", i, currentStore -> getSize()); strcat(buffer, format); sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getRemoteStorageSize(), ((double) currentStore -> getRemoteStorageSize()) / 1024); strcat(buffer, format); sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getLocalStorageSize(), ((double) currentStore -> getLocalStorageSize()) / 1024); strcat(buffer, format); sprintf(format, "%d/%.0f KB\n", currentStore -> cacheSlots, ((double) control -> getUpperStorageSize() / 100 * currentStore -> cacheThreshold) / 1024); strcat(buffer, format); } } if (anyStore == NULL) { strcat(buffer, "N/A\n"); } } if (anyStore != NULL) { sprintf(format, "\ncache: %d bytes (%d KB) available at server.\n", control -> ClientTotalStorageSize, control -> ClientTotalStorageSize / 1024); strcat(buffer, format); sprintf(format, " %d bytes (%d KB) available at client.\n\n", control -> ServerTotalStorageSize, control -> ServerTotalStorageSize / 1024); strcat(buffer, format); sprintf(format, " %d bytes (%d KB) allocated at server.\n", anyStore -> getRemoteTotalStorageSize(), anyStore -> getRemoteTotalStorageSize() / 1024); strcat(buffer, format); sprintf(format, " %d bytes (%d KB) allocated at client.\n\n\n", anyStore -> getLocalTotalStorageSize(), anyStore -> getLocalTotalStorageSize() / 1024); strcat(buffer, format); } else { strcat(buffer, "\ncache: N/A\n\n"); } return 1; } int Statistics::getServerProtocolStats(int type, char *&buffer) { if (type != PARTIAL_STATS && type != TOTAL_STATS) { #ifdef PANIC *logofs << "Statistics: PANIC! Cannot produce statistics " << "with qualifier '" << type << "'.\n" << logofs_flush; #endif return -1; } struct T_transportData *transportData; struct T_protocolData *protocolData; struct T_overallData *overallData; if (type == PARTIAL_STATS) { transportData = &transportPartial_; protocolData = &protocolPartial_; overallData = &overallPartial_; } else { transportData = &transportTotal_; protocolData = &protocolTotal_; overallData = &overallTotal_; } char format[FORMAT_LENGTH]; double countReplyBitsIn = 0; double countReplyBitsOut = 0; double countReplyIn = 0; double countCachedReplyIn = 0; double countEventBitsIn = 0; double countEventBitsOut = 0; double countEventIn = 0; double countCachedEventIn = 0; double countAnyIn = 0; double countBitsIn = 0; double countBitsOut = 0; // // Print reply data. // strcat(buffer, "NX Client Side Protocol Statistics\n"); strcat(buffer, "----------------------------------\n\n"); strcat(buffer, "Reply Total\tCached\tBits In\t\tBits Out\tBits/Reply\t\tRatio\n"); strcat(buffer, "------- -----\t------\t-------\t\t--------\t----------\t\t-----\n"); for (int i = 0; i < STATISTICS_OPCODE_MAX; i++) { if (protocolData -> replyCount_[i]) { sprintf(format, "#%d ", i); while (strlen(format) < 5) { strcat(format, " "); } // // Mark replies originated // by NX agent requests. // if (i >= X_NXFirstOpcode && i <= X_NXLastOpcode) { strcat(format, "A"); } // // Mark replies that we didn't // match against a request. // if (i == 1) { strcat(format, "U"); } while (strlen(format) < 8) { strcat(format, " "); } strcat(buffer, format); if (protocolData -> replyCached_[i] > 0) { sprintf(format, "%.0f\t%.0f", protocolData -> replyCount_[i], protocolData -> replyCached_[i]); } else { sprintf(format, "%.0f\t", protocolData -> replyCount_[i]); } strcat(buffer, format); sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t", protocolData -> replyBitsIn_[i], protocolData -> replyBitsIn_[i] / 8192, protocolData -> replyBitsOut_[i], protocolData -> replyBitsOut_[i] / 8192, protocolData -> replyBitsIn_[i] / protocolData -> replyCount_[i], protocolData -> replyBitsOut_[i] / protocolData -> replyCount_[i]); strcat(buffer, format); if (protocolData -> replyBitsOut_[i] > 0) { sprintf(format, "%5.3f:1\n", protocolData -> replyBitsIn_[i] / protocolData -> replyBitsOut_[i]); } else { sprintf(format, "1:1\n"); } strcat(buffer, format); } countReplyIn += protocolData -> replyCount_[i]; countCachedReplyIn += protocolData -> replyCached_[i]; countReplyBitsIn += protocolData -> replyBitsIn_[i]; countReplyBitsOut += protocolData -> replyBitsOut_[i]; countAnyIn += protocolData -> replyCount_[i]; countBitsIn += protocolData -> replyBitsIn_[i]; countBitsOut += protocolData -> replyBitsOut_[i]; } if (countReplyIn > 0) { if (countCachedReplyIn > 0) { sprintf(format, "\ntotal: %.0f\t%.0f", countReplyIn, countCachedReplyIn); } else { sprintf(format, "\ntotal: %.0f\t", countReplyIn); } strcat(buffer, format); sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t", countReplyBitsIn, countReplyBitsIn / 8192, countReplyBitsOut, countReplyBitsOut / 8192, countReplyBitsIn / countReplyIn, countReplyBitsOut / countReplyIn); strcat(buffer, format); if (countReplyBitsOut > 0) { sprintf(format, "%5.3f:1\n", countReplyBitsIn / countReplyBitsOut); } else { sprintf(format, "1:1\n"); } strcat(buffer, format); } else { strcat(buffer, "N/A\n"); } strcat(buffer, "\n"); // // Print event and error data. // strcat(buffer, "Event Total\tCached\tBits In\t\tBits Out\tBits/Event\t\tRatio\n"); strcat(buffer, "------- -----\t------\t-------\t\t--------\t----------\t\t-----\n"); for (int i = 0; i < STATISTICS_OPCODE_MAX; i++) { if (protocolData -> eventCount_[i]) { sprintf(format, "#%d ", i); while (strlen(format) < 8) { strcat(format, " "); } strcat(buffer, format); if (protocolData -> eventCached_[i] > 0) { sprintf(format, "%.0f\t%.0f", protocolData -> eventCount_[i], protocolData -> eventCached_[i]); } else { sprintf(format, "%.0f\t", protocolData -> eventCount_[i]); } strcat(buffer, format); sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t", protocolData -> eventBitsIn_[i], protocolData -> eventBitsIn_[i] / 8192, protocolData -> eventBitsOut_[i], protocolData -> eventBitsOut_[i] / 8192, protocolData -> eventBitsIn_[i] / protocolData -> eventCount_[i], protocolData -> eventBitsOut_[i] / protocolData -> eventCount_[i]); strcat(buffer, format); if (protocolData -> eventBitsOut_[i] > 0) { sprintf(format, "%5.3f:1\n", protocolData -> eventBitsIn_[i] / protocolData -> eventBitsOut_[i]); } else { sprintf(format, "1:1\n"); } strcat(buffer, format); } countEventIn += protocolData -> eventCount_[i]; countCachedEventIn += protocolData -> eventCached_[i]; countEventBitsIn += protocolData -> eventBitsIn_[i]; countEventBitsOut += protocolData -> eventBitsOut_[i]; countAnyIn += protocolData -> eventCount_[i]; countBitsIn += protocolData -> eventBitsIn_[i]; countBitsOut += protocolData -> eventBitsOut_[i]; } if (countEventIn > 0) { if (countCachedEventIn > 0) { sprintf(format, "\ntotal: %.0f\t%.0f", countEventIn, countCachedEventIn); } else { sprintf(format, "\ntotal: %.0f\t", countEventIn); } strcat(buffer, format); sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t", countEventBitsIn, countEventBitsIn / 8192, countEventBitsOut, countEventBitsOut / 8192, countEventBitsIn / countEventIn, countEventBitsOut / countEventIn); strcat(buffer, format); if (countEventBitsOut > 0) { sprintf(format, "%5.3f:1\n", countEventBitsIn / countEventBitsOut); } else { sprintf(format, "1:1\n"); } strcat(buffer, format); } else { strcat(buffer, "N/A\n\n"); } // // Print transport data. // getTimeStats(type, buffer); countAnyIn += protocolData -> cupsCount_; countBitsIn += protocolData -> cupsBitsIn_; countBitsOut += protocolData -> cupsBitsOut_; countAnyIn += protocolData -> smbCount_; countBitsIn += protocolData -> smbBitsIn_; countBitsOut += protocolData -> smbBitsOut_; countAnyIn += protocolData -> mediaCount_; countBitsIn += protocolData -> mediaBitsIn_; countBitsOut += protocolData -> mediaBitsOut_; countAnyIn += protocolData -> httpCount_; countBitsIn += protocolData -> httpBitsIn_; countBitsOut += protocolData -> httpBitsOut_; countAnyIn += protocolData -> fontCount_; countBitsIn += protocolData -> fontBitsIn_; countBitsOut += protocolData -> fontBitsOut_; countAnyIn += protocolData -> slaveCount_; countBitsIn += protocolData -> slaveBitsIn_; countBitsOut += protocolData -> slaveBitsOut_; // // Save the overall amount of bytes // coming from X clients. // overallData -> overallBytesIn_ = countBitsIn / 8; // // Print performance data. // if (transportData -> readTime_ > 0) { sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n", countAnyIn / (transportData -> readTime_ / 1000), (countBitsIn + transportData -> framingBitsOut_) / 8192 / (transportData -> readTime_ / 1000)); } else { sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n", countAnyIn, (countBitsIn + transportData -> framingBitsOut_) / 8192); } strcat(buffer, format); strcat(buffer, "link: "); // // ZLIB compression stats. // getStreamStats(type, buffer); // // Save the overall amount of bytes // sent on NX proxy link. // if (transportData -> compressedBytesOut_ > 0) { overallData -> overallBytesOut_ = transportData -> compressedBytesOut_; } else { overallData -> overallBytesOut_ = countBitsOut / 8; } // // Print info on multiplexing overhead. // getFramingStats(type, buffer); // // Print stats about additional channels. // getServicesStats(type, buffer); // // Compression summary. // double ratio = 1; if (transportData -> compressedBytesOut_ / 1024 > 0) { ratio = ((countBitsIn + transportData -> framingBitsOut_) / 8192) / (transportData -> compressedBytesOut_ / 1024); } else if (countBitsOut > 0) { ratio = (countBitsIn + transportData -> framingBitsOut_) / countBitsOut; } sprintf(format, " Protocol compression ratio is %5.3f:1.\n\n", ratio); strcat(buffer, format); getBitrateStats(type, buffer); // // These are not included in output. // // getSplitStats(type, buffer); // strcat(buffer, "\n"); // // These statistics are not included in output. // You can check it anyway to get the effective // amount of bytes produced by unpack procedure. // // getClientOverallStats(type, buffer); // return 1; } int Statistics::getServerOverallStats(int type, char *&buffer) { return 1; } int Statistics::getTimeStats(int type, char *&buffer) { struct T_transportData *transportData; if (type == PARTIAL_STATS) { transportData = &transportPartial_; } else { transportData = &transportTotal_; } char format[FORMAT_LENGTH]; sprintf(format, "\ntime: %.0f Ms idle, %.0f Ms (%.0f Ms in read, %.0f Ms in write) running.\n\n", transportData -> idleTime_, transportData -> readTime_, transportData -> readTime_ - transportData -> writeTime_, transportData -> writeTime_); strcat(buffer, format); return 1; } int Statistics::getStreamStats(int type, char *&buffer) { struct T_transportData *transportData; if (type == PARTIAL_STATS) { transportData = &transportPartial_; } else { transportData = &transportTotal_; } char format[FORMAT_LENGTH]; if (transportData -> compressedBytesOut_ > 0) { sprintf(format, "%.0f bytes (%.0f KB) compressed to %.0f (%.0f KB).\n", transportData -> compressedBytesIn_, transportData -> compressedBytesIn_ / 1024, transportData -> compressedBytesOut_, transportData -> compressedBytesOut_ / 1024); strcat(buffer, format); sprintf(format, " %5.3f:1 stream compression ratio.\n\n", transportData -> compressedBytesIn_ / transportData -> compressedBytesOut_); strcat(buffer, format); } if (transportData -> decompressedBytesOut_ > 0) { if (transportData -> compressedBytesOut_ > 0) { strcat(buffer, " "); } sprintf(format, "%.0f bytes (%.0f KB) decompressed to %.0f (%.0f KB).\n", transportData -> decompressedBytesIn_, transportData -> decompressedBytesIn_ / 1024, transportData -> decompressedBytesOut_, transportData -> decompressedBytesOut_ / 1024); strcat(buffer, format); sprintf(format, " %5.3f:1 stream compression ratio.\n\n", transportData -> decompressedBytesOut_ / transportData -> decompressedBytesIn_); strcat(buffer, format); } if (transportData -> compressedBytesOut_ > 0 || transportData -> decompressedBytesOut_ > 0) { strcat(buffer, " "); } return 1; } int Statistics::getServicesStats(int type, char *&buffer) { struct T_protocolData *protocolData; if (type == PARTIAL_STATS) { protocolData = &protocolPartial_; } else { protocolData = &protocolTotal_; } char format[FORMAT_LENGTH]; if (protocolData -> cupsBitsOut_ > 0) { sprintf(format, " %.0f CUPS messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n", protocolData -> cupsCount_ , protocolData -> cupsBitsIn_ / 8, protocolData -> cupsBitsIn_ / 8192, protocolData -> cupsBitsOut_ / 8, protocolData -> cupsBitsOut_ / 8192); strcat(buffer, format); } if (protocolData -> smbBitsOut_ > 0) { sprintf(format, " %.0f SMB messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n", protocolData -> smbCount_ , protocolData -> smbBitsIn_ / 8, protocolData -> smbBitsIn_ / 8192, protocolData -> smbBitsOut_ / 8, protocolData -> smbBitsOut_ / 8192); strcat(buffer, format); } if (protocolData -> mediaBitsOut_ > 0) { sprintf(format, " %.0f multimedia messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n", protocolData -> mediaCount_ , protocolData -> mediaBitsIn_ / 8, protocolData -> mediaBitsIn_ / 8192, protocolData -> mediaBitsOut_ / 8, protocolData -> mediaBitsOut_ / 8192); strcat(buffer, format); } if (protocolData -> httpBitsOut_ > 0) { sprintf(format, " %.0f HTTP messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n", protocolData -> httpCount_ , protocolData -> httpBitsIn_ / 8, protocolData -> httpBitsIn_ / 8192, protocolData -> httpBitsOut_ / 8, protocolData -> httpBitsOut_ / 8192); strcat(buffer, format); } if (protocolData -> fontBitsOut_ > 0) { sprintf(format, " %.0f font server messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n", protocolData -> fontCount_ , protocolData -> fontBitsIn_ / 8, protocolData -> fontBitsIn_ / 8192, protocolData -> fontBitsOut_ / 8, protocolData -> fontBitsOut_ / 8192); strcat(buffer, format); } if (protocolData -> slaveBitsOut_ > 0) { sprintf(format, " %.0f slave messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n", protocolData -> slaveCount_ , protocolData -> slaveBitsIn_ / 8, protocolData -> slaveBitsIn_ / 8192, protocolData -> slaveBitsOut_ / 8, protocolData -> slaveBitsOut_ / 8192); strcat(buffer, format); } return 1; } int Statistics::getFramingStats(int type, char *&buffer) { struct T_transportData *transportData; if (type == PARTIAL_STATS) { transportData = &transportPartial_; } else { transportData = &transportTotal_; } char format[FORMAT_LENGTH]; // // Print info on multiplexing overhead. // sprintf(format, "%.0f frames in, %.0f frames out, %.0f writes out.\n\n", transportData -> proxyFramesIn_, transportData -> proxyFramesOut_, transportData -> proxyWritesOut_); strcat(buffer, format); sprintf(format, " %.0f bytes (%.0f KB) used for framing and multiplexing.\n\n", transportData -> framingBitsOut_ / 8, transportData -> framingBitsOut_ / 8192); strcat(buffer, format); return 1; } int Statistics::getBitrateStats(int type, char *&buffer) { struct T_transportData *transportData; struct T_overallData *overallData; if (type == PARTIAL_STATS) { transportData = &transportPartial_; overallData = &overallPartial_; } else { transportData = &transportTotal_; overallData = &overallTotal_; } double total = 0; if (transportData -> idleTime_ + transportData -> readTime_ > 0) { total = overallData -> overallBytesOut_ / ((transportData -> idleTime_ + transportData -> readTime_) / 1000); } char format[FORMAT_LENGTH]; sprintf(format, " %.0f B/s average, %d B/s %ds, %d B/s %ds, %d B/s maximum.\n\n", total, getBitrateInShortFrame(), control -> ShortBitrateTimeFrame / 1000, getBitrateInLongFrame(), control -> LongBitrateTimeFrame / 1000, getTopBitrate()); strcat(buffer, format); resetTopBitrate(); return 1; } int Statistics::getSplitStats(int type, char *&buffer) { // // Don't print these statistics if persistent // cache of images is disabled. // if (control -> ImageCacheEnableLoad == 0 && control -> ImageCacheEnableSave == 0) { return 0; } struct T_splitData *splitData; if (type == PARTIAL_STATS) { splitData = &splitPartial_; } else { splitData = &splitTotal_; } char format[FORMAT_LENGTH]; // // Print info on split messages restored from disk. // sprintf(format, " %.0f images streamed, %.0f restored, %.0f bytes (%.0f KB) cached.\n\n", splitData -> splitCount_, splitData -> splitAborted_, splitData -> splitAbortedBytesOut_, splitData -> splitAbortedBytesOut_ / 1024); strcat(buffer, format); return 1; } nxcomp/RenderCreateGlyphSetCompat.cpp0000644000076400007640000001515211323113030020214 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderCreateGlyphSetCompat.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { // // Strictly speaking this request doesn't have // a data part. We encode the fields past the // offset as they were data. An improvement // would be to encode the format field using // the cache. // #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { size = MESSAGE_OFFSET + 4; buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian), clientCache -> renderLastId, 29, clientCache -> renderIdCache); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeDiffCachedValue(value, clientCache -> renderLastId, 29, clientCache -> renderIdCache); PutULONG(value, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { #ifdef DEBUG *logofs << name() << ": Glyphset is " << GetULONG(buffer + 4, bigEndian) << ".\n" << logofs_flush; if (size > MESSAGE_OFFSET) { *logofs << name() << ": Format is " << GetULONG(buffer + 8, bigEndian) << ".\n" << logofs_flush; } if (size > MESSAGE_OFFSET + 4) { *logofs << name() << ": WARNING! Unexpected size " << size << ".\n" << logofs_flush; } #endif encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.create_set.type = *(buffer + 1); renderExtension -> data.create_set.set_id = GetULONG(buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.create_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.create_set.type; PutULONG(renderExtension -> data.create_set.set_id, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.create_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeDiffCachedValue(renderExtension -> data.create_set.set_id, clientCache -> renderLastId, 29, clientCache -> renderIdCache); cachedRenderExtension -> data.create_set.set_id = renderExtension -> data.create_set.set_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.create_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeDiffCachedValue(renderExtension -> data.create_set.set_id, clientCache -> renderLastId, 29, clientCache -> renderIdCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.create_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/RenderTrapezoids.h0000644000076400007640000000470011323113027015761 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderTrapezoids_H #define RenderTrapezoids_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderTrapezoids" #undef MESSAGE_STORE #define MESSAGE_STORE RenderTrapezoidsStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 24 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size); } #include MESSAGE_METHODS }; #endif /* RenderTrapezoids_H */ nxcomp/PolyFillArc.cpp0000644000076400007640000001162211323113031015204 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "PolyFillArc.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int PolyFillArcStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message; // // Here is the fingerprint. // polyFillArc -> drawable = GetULONG(buffer + 4, bigEndian); polyFillArc -> gcontext = GetULONG(buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int PolyFillArcStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message; // // Fill all the message's fields. // PutULONG(polyFillArc -> drawable, buffer + 4, bigEndian); PutULONG(polyFillArc -> gcontext, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void PolyFillArcStore::dumpIdentity(const Message *message) const { #ifdef DUMP PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message; *logofs << name() << ": Identity drawable " << polyFillArc -> drawable << ", gcontext " << polyFillArc -> gcontext << ", size " << polyFillArc -> size_ << ".\n" << logofs_flush; #endif } void PolyFillArcStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { } void PolyFillArcStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message; PolyFillArcMessage *cachedPolyFillArc = (PolyFillArcMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << polyFillArc -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyFillArc -> drawable, clientCache -> drawableCache); cachedPolyFillArc -> drawable = polyFillArc -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << polyFillArc -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyFillArc -> gcontext, clientCache -> gcCache); cachedPolyFillArc -> gcontext = polyFillArc -> gcontext; } void PolyFillArcStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); polyFillArc -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyFillArc -> drawable << " as drawable field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); polyFillArc -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyFillArc -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } nxcomp/Fork.cpp0000644000076400007640000000473011342773476013761 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include "Fork.h" #include "Misc.h" #include "Timestamp.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Only on Cygwin, retry n times waiting a // given amount of milliseconds after each // attempt. // #define RETRY_LIMIT 30 #define RETRY_TIMEOUT 1000 int Fork() { #ifdef __CYGWIN32__ int limit = RETRY_LIMIT; int timeout = RETRY_TIMEOUT; #else int limit = 1; int timeout = 0; #endif int pid = 0; for (int i = 0; i < limit; i++) { #ifdef TEST *logofs << "Fork: Trying at " << strMsTimestamp() << ".\n" << logofs_flush; #endif // // It could optionally try again only if the // error code is 11, 'Resource temporarily // unavailable'. // if ((pid = fork()) >= 0) { break; } else if (i < limit - 1) { #ifdef WARNING *logofs << "Fork: WARNING! Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'. Retrying...\n" << logofs_flush; #endif usleep(timeout * 1000); } } #ifdef TEST if (pid <= 0) { *logofs << "Fork: Returning at " << strMsTimestamp() << ".\n" << logofs_flush; } #endif return pid; } nxcomp/InternAtom.h0000644000076400007640000001036611323113027014562 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef InternAtom_H #define InternAtom_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define INTERNATOM_ENABLE_CACHE 1 #define INTERNATOM_ENABLE_DATA 0 #define INTERNATOM_ENABLE_SPLIT 0 #define INTERNATOM_ENABLE_COMPRESS 0 #define INTERNATOM_DATA_LIMIT 80 #define INTERNATOM_DATA_OFFSET 8 #define INTERNATOM_CACHE_SLOTS 2000 #define INTERNATOM_CACHE_THRESHOLD 2 #define INTERNATOM_CACHE_LOWER_THRESHOLD 1 // // The message class. // class InternAtomMessage : public Message { friend class InternAtomStore; public: InternAtomMessage() { } ~InternAtomMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char only_if_exists; unsigned short name_length; }; class InternAtomStore : public MessageStore { // // Constructors and destructors. // public: InternAtomStore() : MessageStore() { enableCache = INTERNATOM_ENABLE_CACHE; enableData = INTERNATOM_ENABLE_DATA; enableSplit = INTERNATOM_ENABLE_SPLIT; enableCompress = INTERNATOM_ENABLE_COMPRESS; dataLimit = INTERNATOM_DATA_LIMIT; dataOffset = INTERNATOM_DATA_OFFSET; cacheSlots = INTERNATOM_CACHE_SLOTS; cacheThreshold = INTERNATOM_CACHE_THRESHOLD; cacheLowerThreshold = INTERNATOM_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~InternAtomStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "InternAtom"; } virtual unsigned char opcode() const { return X_InternAtom; } virtual unsigned int storage() const { return sizeof(InternAtomMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new InternAtomMessage(); } virtual Message *create(const Message &message) const { return new InternAtomMessage((const InternAtomMessage &) message); } virtual void destroy(Message *message) const { delete (InternAtomMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* InternAtom_H */ nxcomp/RenderPictureTransform.cpp0000644000076400007640000001355411323113031017501 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderPictureTransform.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { // // Size is always 44. The identity size // is set to 8, so we encode the 36 bytes // of the transformation matrix as our // data. // } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { size = MESSAGE_OFFSET + 36; buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> renderSrcPictureCache); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.picture_transform.type = *(buffer + 1); renderExtension -> data.picture_transform.src_id = GetULONG(buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.picture_transform.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.picture_transform.type; PutULONG(renderExtension -> data.picture_transform.src_id, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.picture_transform.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(renderExtension -> data.picture_transform.src_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.picture_transform.src_id = renderExtension -> data.picture_transform.src_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.picture_transform.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.picture_transform.src_id, clientCache -> renderSrcPictureCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.picture_transform.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/SetUnpackGeometry.cpp0000644000076400007640000002352711323113027016451 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "SetUnpackGeometry.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // SetUnpackGeometryStore::SetUnpackGeometryStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = SETUNPACKGEOMETRY_ENABLE_CACHE; enableData = SETUNPACKGEOMETRY_ENABLE_DATA; enableSplit = SETUNPACKGEOMETRY_ENABLE_SPLIT; enableCompress = SETUNPACKGEOMETRY_ENABLE_COMPRESS; dataLimit = SETUNPACKGEOMETRY_DATA_LIMIT; dataOffset = SETUNPACKGEOMETRY_DATA_OFFSET; cacheSlots = SETUNPACKGEOMETRY_CACHE_SLOTS; cacheThreshold = SETUNPACKGEOMETRY_CACHE_THRESHOLD; cacheLowerThreshold = SETUNPACKGEOMETRY_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } SetUnpackGeometryStore::~SetUnpackGeometryStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int SetUnpackGeometryStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(*(buffer + 1), 8, clientCache -> resourceCache); const unsigned char *nextChar = buffer + 4; for (int i = 0; i < 6; i++) { encodeBuffer.encodeCachedValue(*nextChar++, 8, clientCache -> depthCache); } encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32); encodeBuffer.encodeValue(GetULONG(buffer + 16, bigEndian), 32); encodeBuffer.encodeValue(GetULONG(buffer + 20, bigEndian), 32); #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int SetUnpackGeometryStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif size = 24; buffer = writeBuffer -> addMessage(size); unsigned char cValue; decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> resourceCache); *(buffer + 1) = cValue; decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); *(buffer + 4) = cValue; decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); *(buffer + 5) = cValue; decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); *(buffer + 6) = cValue; decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); *(buffer + 7) = cValue; decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); *(buffer + 8) = cValue; decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); *(buffer + 9) = cValue; unsigned int value; decodeBuffer.decodeValue(value, 32); PutULONG(value, buffer + 12, bigEndian); decodeBuffer.decodeValue(value, 32); PutULONG(value, buffer + 16, bigEndian); decodeBuffer.decodeValue(value, 32); PutULONG(value, buffer + 20, bigEndian); #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int SetUnpackGeometryStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message; setUnpackGeometry -> client = *(buffer + 1); setUnpackGeometry -> depth_1_bpp = *(buffer + 4); setUnpackGeometry -> depth_4_bpp = *(buffer + 5); setUnpackGeometry -> depth_8_bpp = *(buffer + 6); setUnpackGeometry -> depth_16_bpp = *(buffer + 7); setUnpackGeometry -> depth_24_bpp = *(buffer + 8); setUnpackGeometry -> depth_32_bpp = *(buffer + 9); setUnpackGeometry -> red_mask = GetULONG(buffer + 12, bigEndian); setUnpackGeometry -> green_mask = GetULONG(buffer + 16, bigEndian); setUnpackGeometry -> blue_mask = GetULONG(buffer + 20, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int SetUnpackGeometryStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message; *(buffer + 1) = setUnpackGeometry -> client; *(buffer + 4) = setUnpackGeometry -> depth_1_bpp; *(buffer + 5) = setUnpackGeometry -> depth_4_bpp; *(buffer + 6) = setUnpackGeometry -> depth_8_bpp; *(buffer + 7) = setUnpackGeometry -> depth_16_bpp; *(buffer + 8) = setUnpackGeometry -> depth_24_bpp; *(buffer + 9) = setUnpackGeometry -> depth_32_bpp; PutULONG(setUnpackGeometry -> red_mask, buffer + 12, bigEndian); PutULONG(setUnpackGeometry -> green_mask, buffer + 16, bigEndian); PutULONG(setUnpackGeometry -> blue_mask, buffer + 20, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void SetUnpackGeometryStore::dumpIdentity(const Message *message) const { #ifdef DUMP SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message; *logofs << name() << ": Identity client " << (unsigned) setUnpackGeometry -> client << " depth_1_bpp " << (unsigned) setUnpackGeometry -> depth_1_bpp << " depth_4_bpp " << (unsigned int) setUnpackGeometry -> depth_4_bpp << " depth_8_bpp " << (unsigned int) setUnpackGeometry -> depth_8_bpp << " depth_16_bpp " << (unsigned int) setUnpackGeometry -> depth_16_bpp << " depth_24_bpp " << (unsigned int) setUnpackGeometry -> depth_24_bpp << " depth_32_bpp " << (unsigned int) setUnpackGeometry -> depth_32_bpp << " red_mask " << setUnpackGeometry -> red_mask << " green_mask " << setUnpackGeometry -> green_mask << " blue_mask " << setUnpackGeometry -> blue_mask << " size " << setUnpackGeometry -> size_ << ".\n"; #endif } void SetUnpackGeometryStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 4, 6); md5_append(md5_state_, buffer + 12, 12); } void SetUnpackGeometryStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message; SetUnpackGeometryMessage *cachedSetUnpackGeometry = (SetUnpackGeometryMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << (unsigned int) setUnpackGeometry -> client << " as client field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(setUnpackGeometry -> client, 8, clientCache -> resourceCache); cachedSetUnpackGeometry -> client = setUnpackGeometry -> client; } void SetUnpackGeometryStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(setUnpackGeometry -> client, 8, clientCache -> resourceCache); #ifdef DEBUG *logofs << name() << ": Decoded value " << (unsigned int) setUnpackGeometry -> client << " as client field.\n" << logofs_flush; #endif } nxcomp/RenderFreePictureCompat.h0000644000076400007640000000470011323113030017210 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderFreePictureCompat_H #define RenderFreePictureCompat_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderFreePictureCompat" #undef MESSAGE_STORE #define MESSAGE_STORE RenderFreePictureCompatStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 8 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 0 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 0 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderFreePictureCompat_H */ nxcomp/Channel.h0000644000076400007640000003735411342773403014073 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Channel_H #define Channel_H #include "Transport.h" #include "WriteBuffer.h" #include "OpcodeStore.h" #include "ClientStore.h" #include "ServerStore.h" #include "ClientCache.h" #include "ServerCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Forward declaration of referenced classes. // class List; class StaticCompressor; // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Define this to log a line when a channel // is created or destroyed. // #undef REFERENCES // // Type of traffic carried by channel. // typedef enum { channel_none = -1, channel_x11, channel_cups, channel_smb, channel_media, channel_http, channel_font, channel_slave, channel_last_tag } T_channel_type; // // Type of notification event to be sent // by proxy to the X channel. // typedef enum { notify_no_split, notify_start_split, notify_commit_split, notify_end_split, notify_empty_split, } T_notification_type; class Channel { public: // // Maximum number of X connections supported. // static const int CONNECTIONS_LIMIT = 256; Channel(Transport *transport, StaticCompressor *compressor); virtual ~Channel(); // // Read any X message available on the X // connection and encode it to the encode // buffer. // virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, unsigned int length) = 0; // // Decode any X message encoded in the // proxy message and write it to the X // connection. // virtual int handleWrite(const unsigned char *message, unsigned int length) = 0; // // Other methods to be implemented in // client, server and generic channel // classes. // virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store, T_store_action action, int position, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) = 0; virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, T_store_action action, int position, unsigned char &opcode, unsigned char *&buffer, unsigned int &size) = 0; virtual int handleSplit(EncodeBuffer &encodeBuffer) = 0; virtual int handleSplit(DecodeBuffer &decodeBuffer) = 0; virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split) = 0; virtual int handleSplitEvent(DecodeBuffer &decodeBuffer) = 0; virtual int handleMotion(EncodeBuffer &encodeBuffer) = 0; virtual int handleCompletion(EncodeBuffer &encodeBuffer) = 0; virtual int handleConfiguration() = 0; virtual int handleFinish() = 0; // // Interleave reads of the available // events while writing data to the // channel socket. // virtual int handleAsyncEvents() = 0; // // Handle the channel tear down. // int handleClosing() { closing_ = 1; return 1; } int handleDrop() { drop_ = 1; return 1; } // // Try to read more data from the socket. In // the meanwhile flush any enqueued data if // the channel is blocked. Return as soon as // more data has been read or the timeout has // been exceeded. // int handleWait(int timeout); // // Drain the output buffer while handling the // data that may become readable. // int handleDrain(int timeout, int limit); // // Flush any remaining data in the transport // buffer. // int handleFlush(); // // Called when the loop has replaced or // closed a previous alert. // void handleResetAlert(); // // Initialize all the static members. // static int setReferences(); // // Set pointer to object mapping opcodes // of NX specific messages. // int setOpcodes(OpcodeStore *opcodeStore); // // Update pointers to message stores in // channels. // int setStores(ClientStore *clientStore, ServerStore *serverStore); // // The same for channels caches. // int setCaches(ClientCache *clientCache, ServerCache *serverCache); // // Set the port used for tunneling of the // font server connections. // void setPorts(int fontPort) { fontPort_ = fontPort; } // // Check if there are pending split // to send to the remote side. // virtual int needSplit() const = 0; // // Check if there are motion events // to flush. // virtual int needMotion() const = 0; // // Return the type of traffic carried // by this channel. // virtual T_channel_type getType() const = 0; // // Check if the channel has been marked // as closing down. // int getFinish() const { return finish_; } int getClosing() { return closing_; } int getDrop() { return drop_; } int getCongestion() { return congestion_; } protected: int handleFlush(T_flush type) { // // We could write the data immediately if there // is already something queued to the low level // TCP buffers. // // if (... || transport_ -> queued() > 0) // { // ... // } // if (writeBuffer_.getScratchLength() > 0 || (type == flush_if_any && writeBuffer_.getLength() > 0) || writeBuffer_.getLength() >= (unsigned int) control -> TransportFlushBufferSize) { return handleFlush(type, writeBuffer_.getLength(), writeBuffer_.getScratchLength()); } return 0; } // // Actually flush the data to the // channel descriptor. // int handleFlush(T_flush type, int bufferLength, int scratchLength); // // Handle the congestion changes. // int handleCongestion(); // // Encode and decode X messages. // int handleEncode(EncodeBuffer &encodeBuffer, ChannelCache *channelCache, MessageStore *store, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); int handleDecode(DecodeBuffer &decodeBuffer, ChannelCache *channelCache, MessageStore *store, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Encode the message based on its // message store. // int handleEncodeCached(EncodeBuffer &encodeBuffer, ChannelCache *channelCache, MessageStore *store, const unsigned char *buffer, const unsigned int size); int handleDecodeCached(DecodeBuffer &decodeBuffer, ChannelCache *channelCache, MessageStore *store, unsigned char *&buffer, unsigned int &size); int handleEncodeIdentity(EncodeBuffer &encodeBuffer, ChannelCache *channelCache, MessageStore *store, const unsigned char *buffer, const unsigned int size, int bigEndian) { return (store -> encodeIdentity(encodeBuffer, buffer, size, bigEndian, channelCache)); } int handleDecodeIdentity(DecodeBuffer &decodeBuffer, ChannelCache *channelCache, MessageStore *store, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer) { return (store -> decodeIdentity(decodeBuffer, buffer, size, bigEndian, writeBuffer, channelCache)); } // // Other utility functions used by // the encoding and decoding methods. // void handleCopy(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned int offset, const unsigned char *buffer, const unsigned int size) { if (size > offset) { encodeBuffer.encodeMemory(buffer + offset, size - offset); } } void handleCopy(DecodeBuffer &decodeBuffer, const unsigned char opcode, const unsigned int offset, unsigned char *buffer, const unsigned int size) { if (size > offset) { memcpy(buffer + offset, decodeBuffer.decodeMemory(size - offset), size - offset); } } void handleUpdate(MessageStore *store, const unsigned int dataSize, const unsigned int compressedDataSize) { if (store -> lastAction == IS_ADDED) { handleUpdateAdded(store, dataSize, compressedDataSize); } } void handleSave(MessageStore *store, unsigned char *buffer, unsigned int size, const unsigned char *compressedData = NULL, const unsigned int compressedDataSize = 0) { if (store -> lastAction == IS_ADDED) { handleSaveAdded(store, 0, buffer, size, compressedData, compressedDataSize); } } void handleSaveSplit(MessageStore *store, unsigned char *buffer, unsigned int size) { if (store -> lastAction == IS_ADDED) { return handleSaveAdded(store, 1, buffer, size, 0, 0); } } void handleUpdateAdded(MessageStore *store, const unsigned int dataSize, const unsigned int compressedDataSize); void handleSaveAdded(MessageStore *store, int split, unsigned char *buffer, unsigned int size, const unsigned char *compressedData, const unsigned int compressedDataSize); // // Compress the data part of a message // using ZLIB or another compressor // and send it over the network. // int handleCompress(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned int offset, const unsigned char *buffer, const unsigned int size, unsigned char *&compressedData, unsigned int &compressedDataSize); int handleDecompress(DecodeBuffer &decodeBuffer, const unsigned char opcode, const unsigned int offset, unsigned char *buffer, const unsigned int size, const unsigned char *&compressedData, unsigned int &compressedDataSize); // // Send an X_NoOperation to the X server. // The second version also removes any // previous data in the write buffer. // int handleNullRequest(unsigned char &opcode, unsigned char *&buffer, unsigned int &size); int handleCleanAndNullRequest(unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // X11 channels are considered to be in // congestion state when there was a // blocking write and, since then, the // local end didn't consume all the data. // virtual int isCongested() { return (transport_ -> getType() != transport_agent && transport_ -> length() > control -> TransportFlushBufferSize); } virtual int isReliable() { return 1; } // // Determine how to handle allocation // of new messages in the message // stores. // int mustCleanStore(MessageStore *store) { return (store -> getRemoteTotalStorageSize() > control -> RemoteTotalStorageSize || store -> getLocalTotalStorageSize() > control -> LocalTotalStorageSize || (store -> getRemoteStorageSize() > (control -> RemoteTotalStorageSize / 100 * store -> cacheThreshold)) || (store -> getLocalStorageSize() > (control -> LocalTotalStorageSize / 100 * store -> cacheThreshold))); } int canCleanStore(MessageStore *store) { return ((store -> getSize() > 0 && (store -> getRemoteStorageSize() > (control -> RemoteTotalStorageSize / 100 * store -> cacheLowerThreshold))) || (store -> getLocalStorageSize() > (control -> LocalTotalStorageSize / 100 * store -> cacheLowerThreshold))); } protected: // // Set up the split stores. // void handleSplitStoreError(int resource); void handleSplitStoreAlloc(List *list, int resource); void handleSplitStoreRemove(List *list, int resource); Split *handleSplitCommitRemove(int request, int resource, int position); void validateSize(const char *name, int input, int output, int offset, int size) { if (size < offset || size > control -> MaximumMessageSize || size != (int) RoundUp4(input) + offset || output > control -> MaximumMessageSize) { *logofs << "Channel: PANIC! Invalid size " << size << " for " << name << " output with data " << input << "/" << output << "/" << offset << "/" << size << ".\n" << logofs_flush; cerr << "Error" << ": Invalid size " << size << " for " << name << " output.\n"; HandleAbort(); } } // // Is the X client big endian? // int bigEndian() const { return bigEndian_; } int bigEndian_; // // Other X server's features // saved at session startup. // unsigned int imageByteOrder_; unsigned int bitmapBitOrder_; unsigned int scanlineUnit_; unsigned int scanlinePad_; int firstRequest_; int firstReply_; // // Use this class for IO operations. // Transport *transport_; // // The static compressor is created by the // proxy and shared among channels. // StaticCompressor *compressor_; // // Map NX operations to opcodes. Propagated // by proxy to all channels on the same X // server. // OpcodeStore *opcodeStore_; // // Also stores are shared between channels. // ClientStore *clientStore_; ServerStore *serverStore_; // // Caches are specific for each channel. // ClientCache *clientCache_; ServerCache *serverCache_; // // Data going to X connection. // WriteBuffer writeBuffer_; // // Other data members. // int fd_; int finish_; int closing_; int drop_; int congestion_; int priority_; int alert_; // // It will be set to the descriptor of the // first X channel that is successfully con- // nected and will print an info message on // standard error. // static int firstClient_; // // Port used for font server connections. // static int fontPort_; // // Track which cache operations have been // enabled by the agent. // int enableCache_; int enableSplit_; int enableSave_; int enableLoad_; // // Keep track of object creation and // deletion. // #ifdef REFERENCES static int references_; #endif }; #endif /* Channel_H */ nxcomp/Pipe.cpp0000644000076400007640000001746311342773403013752 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include #include #include "Pipe.h" #include "Misc.h" #include "Fork.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG extern void RegisterChild(int child); static int Psplit(const char *command, char *parameters[], int limit); // // These are slightly modified versions of popen(3) and pclose(3) // that don't rely on a shell to be available on the system, so // that they can also work on Windows. As an additional benefit, // these functions give up all privileges before running the com- // mand. Code is taken from the X distribution and, in turn, is // based on libc from FreeBSD 2.2. // static struct pid { struct pid *next; FILE *fp; int self; } *pidlist; // // A very unsofisticated attempt to parse the command line and // split each parameter in distinct strings. This is not going // to work when dealing with parameters containing spaces, even // if they are enclosed in quotes. // int Psplit(const char *command, char *parameters[], int limit) { char *line; char *value; int number; // // Preapare the list of parameters. // for (number = 0; number < limit; number++) { parameters[number] = NULL; } // // Copy the command to get rid of the // const qualifier. // line = new char[strlen(command) + 1]; if (line == NULL) { goto PsplitError; } strcpy(line, command); number = 0; value = strtok(line, " "); while (value != NULL && number < limit) { #ifdef DEBUG *logofs << "Psplit: Got parameter '" << value << "'.\n" << logofs_flush; #endif parameters[number] = new char[strlen(value) + 1]; if (parameters[number] == NULL) { goto PsplitError; } strcpy(parameters[number], value); number++; // // If this is the first parameter, then // copy it in the second position and // use it as the name of the command. // if (number == 1) { parameters[number] = new char[strlen(value) + 1]; if (parameters[number] == NULL) { goto PsplitError; } strcpy(parameters[number], value); number++; } value = strtok(NULL, " "); } // // Needs at least to have the command itself and // the first argument, being again the name of // the command. // if (number < 2) { goto PsplitError; } return number; PsplitError: #ifdef PANIC *logofs << "Psplit: PANIC! Can't split command line '" << command << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't split command line '" << command << "'.\n"; delete [] line; return -1; } FILE *Popen(char * const parameters[], const char *type) { FILE *iop; struct pid *cur; int pdes[2], pid; if (parameters == NULL || type == NULL) { return NULL; } if ((*type != 'r' && *type != 'w') || type[1]) { return NULL; } if ((cur = (struct pid *) malloc(sizeof(struct pid))) == NULL) { return NULL; } if (pipe(pdes) < 0) { free(cur); return NULL; } // // Block all signals until command is exited. // We need to gather information about the // child in Pclose(). // DisableSignals(); switch (pid = Fork()) { case -1: { // // Error. // #ifdef PANIC *logofs << "Popen: PANIC! Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; close(pdes[0]); close(pdes[1]); free(cur); return NULL; } case 0: { // // Child. // setgid(getgid()); setuid(getuid()); if (*type == 'r') { if (pdes[1] != 1) { // // Set up stdout. // dup2(pdes[1], 1); close(pdes[1]); } close(pdes[0]); } else { if (pdes[0] != 0) { // // Set up stdin. // dup2(pdes[0], 0); close(pdes[0]); } close(pdes[1]); } execvp(parameters[0], parameters + 1); exit(127); } } // // Parent. Save data about the child. // RegisterChild(pid); if (*type == 'r') { iop = fdopen(pdes[0], type); close(pdes[1]); } else { iop = fdopen(pdes[1], type); close(pdes[0]); } cur -> fp = iop; cur -> self = pid; cur -> next = pidlist; pidlist = cur; #ifdef TEST *logofs << "Popen: Executing "; for (int i = 0; i < 256 && parameters[i] != NULL; i++) { *logofs << "[" << parameters[i] << "]"; } *logofs << " with descriptor " << fileno(iop) << ".\n" << logofs_flush; #endif return iop; } FILE *Popen(const char *command, const char *type) { char *parameters[256]; if (Psplit(command, parameters, 256) > 0) { FILE *file = Popen(parameters, type); for (int i = 0; i < 256; i++) { delete [] parameters[i]; } return file; } else { #ifdef PANIC *logofs << "Popen: PANIC! Failed to parse command '" << command << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to parse command '" << command << "'.\n"; return NULL; } } int Pclose(FILE *iop) { struct pid *cur, *last; int pstat; int pid; #ifdef TEST *logofs << "Pclose: Closing command with output " << "on descriptor " << fileno(iop) << ".\n" << logofs_flush; #endif fclose((FILE *) iop); for (last = NULL, cur = pidlist; cur; last = cur, cur = cur -> next) { if (cur -> fp == iop) { break; } } if (cur == NULL) { #ifdef PANIC *logofs << "Pclose: PANIC! Failed to find the process " << "for descriptor " << fileno(iop) << ".\n" << logofs_flush; #endif cerr << "Error" << ": Failed to find the process " << "for descriptor " << fileno(iop) << ".\n"; return -1; } do { #ifdef TEST *logofs << "Pclose: Going to wait for process " << "with pid '" << cur -> self << "'.\n" << logofs_flush; #endif pid = waitpid(cur -> self, &pstat, 0); } while (pid == -1 && errno == EINTR); if (last == NULL) { pidlist = cur -> next; } else { last -> next = cur -> next; } free(cur); // // Child has finished and we called the // waitpid(). We can enable signals again. // EnableSignals(); return (pid == -1 ? -1 : pstat); } nxcomp/GenericReadBuffer.cpp0000644000076400007640000000440711323113026016335 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "GenericReadBuffer.h" #include "GenericChannel.h" #define PANIC #define WARNING #undef TEST #undef DEBUG unsigned int GenericReadBuffer::suggestedLength(unsigned int pendingLength) { // // Always read the initial read size. // return 0; } int GenericReadBuffer::locateMessage(const unsigned char *start, const unsigned char *end, unsigned int &controlLength, unsigned int &dataLength, unsigned int &trailerLength) { // // We don't care about the endianess // in generic channels. // unsigned int size = end - start; #ifdef TEST *logofs << "GenericReadBuffer: Locating message for FD#" << transport_ -> fd() << " with " << size << " bytes.\n" << logofs_flush; #endif if (size == 0) { remaining_ = 1; return 0; } dataLength = size; controlLength = 0; trailerLength = 0; remaining_ = 0; return 1; } nxcomp/List.cpp0000644000076400007640000000473511323113030013745 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "List.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Define this to know how many instances // are allocated and deallocated. // #undef REFERENCES #ifdef REFERENCES int List::references_ = 0; #endif List::List() { #ifdef REFERENCES references_++; *logofs << "List: Created new List at " << this << " out of " << references_ << " allocated references.\n" << logofs_flush; #endif } List::~List() { #ifdef REFERENCES references_--; *logofs << "List: Deleted List at " << this << " out of " << references_ << " allocated references.\n" << logofs_flush; #endif } void List::remove(int value) { for (T_list::iterator i = list_.begin(); i != list_.end(); i++) { if (*i == value) { list_.erase(i); return; } } #ifdef PANIC *logofs << "List: PANIC! Should not try to remove " << "an element not found in the list.\n" << logofs_flush; #endif cerr << "Error" << ": Should not try to remove " << "an element not found in the list.\n"; HandleAbort(); } void List::rotate() { if (list_.size() > 1) { int value = *(list_.begin()); list_.pop_front(); list_.push_back(value); } } nxcomp/NXproto.h0000644000076400007640000003036111323113027014110 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef NXproto_H #define NXproto_H #ifdef __cplusplus extern "C" { #endif #include #include #include /* * Force the size to match the wire protocol. */ #define Drawable CARD32 #define GContext CARD32 #define sz_xNXGetControlParametersReq 4 #define sz_xNXGetCleanupParametersReq 4 #define sz_xNXGetImageParametersReq 4 #define sz_xNXGetUnpackParametersReq 8 #define sz_xNXGetShmemParametersReq 16 #define sz_xNXGetFontParametersReq 4 #define sz_xNXSetExposeParametersReq 8 #define sz_xNXSetCacheParametersReq 8 #define sz_xNXStartSplitReq 8 #define sz_xNXEndSplitReq 4 #define sz_xNXCommitSplitReq 12 #define sz_xNXSetUnpackGeometryReq 24 #define sz_xNXSetUnpackColormapReq 16 #define sz_xNXSetUnpackAlphaReq 16 #define sz_xNXPutPackedImageReq 40 #define sz_xNXFreeUnpackReq 4 #define sz_xNXFinishSplitReq 4 #define sz_xNXAbortSplitReq 4 #define sz_xNXFreeSplitReq 4 #define sz_xGetControlParametersReply 32 #define sz_xGetCleanupParametersReply 32 #define sz_xGetImageParametersReply 32 #define sz_xGetUnpackParametersReply 32 #define sz_xGetShmemParametersReply 32 #define LINK_TYPE_LIMIT 5 #define LINK_TYPE_NONE 0 #define LINK_TYPE_MODEM 1 #define LINK_TYPE_ISDN 2 #define LINK_TYPE_ADSL 3 #define LINK_TYPE_WAN 4 #define LINK_TYPE_LAN 5 /* * NX Replies. */ /* * The following reply has 4 new boolean * fields in the last protocol version. */ typedef struct _NXGetControlParametersReply { BYTE type; /* Is X_Reply. */ CARD8 linkType; CARD16 sequenceNumber B16; CARD32 length B32; /* Is 0. */ CARD8 localMajor; CARD8 localMinor; CARD8 localPatch; CARD8 remoteMajor; CARD8 remoteMinor; CARD8 remotePatch; CARD16 splitTimeout B16; CARD16 motionTimeout B16; CARD8 splitMode; CARD8 pad1; CARD32 splitSize B32; CARD8 packMethod; CARD8 packQuality; CARD8 dataLevel; CARD8 streamLevel; CARD8 deltaLevel; CARD8 loadCache; CARD8 saveCache; CARD8 startupCache; } xNXGetControlParametersReply; typedef struct _NXGetCleanupParametersReply { BYTE type; /* Is X_Reply. */ BYTE pad; CARD16 sequenceNumber B16; CARD32 length B32; /* Is 0. */ BOOL cleanGet; BOOL cleanAlloc; BOOL cleanFlush; BOOL cleanSend; BOOL cleanImages; BYTE pad1, pad2, pad3; CARD32 pad4 B32; CARD32 pad5 B32; CARD32 pad6 B32; CARD32 pad7 B32; } xNXGetCleanupParametersReply; typedef struct _NXGetImageParametersReply { BYTE type; /* Is X_Reply. */ BYTE pad; CARD16 sequenceNumber B16; CARD32 length B32; /* Is 0. */ BOOL imageSplit; BOOL imageMask; BOOL imageFrame; CARD8 imageMaskMethod; CARD8 imageSplitMethod; BYTE pad1, pad2, pad3; CARD32 pad4 B32; CARD32 pad5 B32; CARD32 pad6 B32; CARD32 pad7 B32; } xNXGetImageParametersReply; /* * Data is made of PACK_METHOD_LIMIT values of * type BOOL telling which unpack capabilities * are implemented in proxy. */ typedef struct _NXGetUnpackParametersReply { BYTE type; /* Is X_Reply. */ BYTE pad; CARD16 sequenceNumber B16; CARD32 length B32; /* Is PACK_METHOD_LIMIT / 4 from NXpack.h. */ CARD8 entries; /* Is PACK_METHOD_LIMIT. */ BYTE pad1, pad2, pad3; CARD32 pad4 B32; CARD32 pad5 B32; CARD32 pad6 B32; CARD32 pad7 B32; CARD32 pad8 B32; } xNXGetUnpackParametersReply; typedef struct _NXGetShmemParametersReply { BYTE type; /* Is X_Reply. */ CARD8 stage; /* As in the corresponding request. */ CARD16 sequenceNumber B16; CARD32 length B32; /* Is 0. */ BOOL clientEnabled; /* SHM on path agent to proxy. */ BOOL serverEnabled; /* SHM on path proxy to X server. */ BYTE pad1, pad2; /* Previous values can be checked */ CARD32 clientSize B32; /* at end of stage 2. */ CARD32 serverSize B32; CARD32 pad3 B32; CARD32 pad4 B32; CARD32 pad5 B32; } xNXGetShmemParametersReply; typedef struct _NXGetFontParametersReply { BYTE type; /* Is X_Reply. */ BYTE pad1; CARD16 sequenceNumber B16; CARD32 length B32; /* Is length of path string + 1 / 4. */ CARD32 pad2 B32; CARD32 pad3 B32; CARD32 pad4 B32; CARD32 pad5 B32; CARD32 pad6 B32; CARD32 pad7 B32; } xNXGetFontParametersReply; /* * NX Requests. */ typedef struct _NXGetControlParametersReq { CARD8 reqType; BYTE pad; CARD16 length B16; } xNXGetControlParametersReq; typedef struct _NXGetCleanupParametersReq { CARD8 reqType; BYTE pad; CARD16 length B16; } xNXGetCleanupParametersReq; typedef struct _NXGetImageParametersReq { CARD8 reqType; BYTE pad; CARD16 length B16; } xNXGetImageParametersReq; typedef struct _NXGetUnpackParametersReq { CARD8 reqType; BYTE pad; CARD16 length B16; CARD8 entries; BYTE pad1, pad2, pad3; } xNXGetUnpackParametersReq; typedef struct _NXGetShmemParametersReq { CARD8 reqType; CARD8 stage; /* It is between 0 and 2. */ CARD16 length B16; BOOL enableClient; /* X client side support is */ BOOL enableServer; /* not implemented yet. */ BYTE pad1, pad2; CARD32 clientSegment; /* XID identifying the shared */ CARD32 serverSegment; /* memory segments. */ } xNXGetShmemParametersReq; typedef struct _NXGetFontParametersReq { CARD8 reqType; CARD8 pad; CARD16 length B16; } xNXGetFontParametersReq; /* * The available split modes. */ #define NXSplitModeDefault 0 #define NXSplitModeAsync 1 #define NXSplitModeSync 2 typedef struct _NXStartSplitReq { CARD8 reqType; CARD8 resource; CARD16 length B16; CARD8 mode; BYTE pad1, pad2, pad3; } xNXStartSplitReq; typedef struct _NXEndSplitReq { CARD8 reqType; CARD8 resource; CARD16 length B16; } xNXEndSplitReq; typedef struct _NXCommitSplitReq { CARD8 reqType; CARD8 resource; CARD16 length B16; CARD8 propagate; CARD8 request; BYTE pad1, pad2; CARD32 position B32; } xNXCommitSplitReq; typedef struct _NXFinishSplitReq { CARD8 reqType; CARD8 resource; CARD16 length B16; } xNXFinishSplitReq; typedef struct _NXAbortSplitReq { CARD8 reqType; CARD8 resource; CARD16 length B16; } xNXAbortSplitReq; typedef struct _NXFreeSplitReq { CARD8 reqType; CARD8 resource; CARD16 length B16; } xNXFreeSplitReq; typedef struct _NXSetExposeParametersReq { CARD8 reqType; BYTE pad; CARD16 length B16; BOOL expose; BOOL graphicsExpose; BOOL noExpose; BYTE pad1; } xNXSetExposeParametersReq; typedef struct _NXSetCacheParametersReq { CARD8 reqType; BYTE pad; CARD16 length B16; BOOL enableCache; BOOL enableSplit; BOOL enableSave; BOOL enableLoad; } xNXSetCacheParametersReq; typedef struct _NXSetUnpackGeometryReq { CARD8 reqType; CARD8 resource; CARD16 length B16; CARD8 depth1Bpp; CARD8 depth4Bpp; CARD8 depth8Bpp; CARD8 depth16Bpp; CARD8 depth24Bpp; CARD8 depth32Bpp; BYTE pad1, pad2; CARD32 redMask B32; CARD32 greenMask B32; CARD32 blueMask B32; } xNXSetUnpackGeometryReq; typedef struct _NXSetUnpackColormapReq { CARD8 reqType; CARD8 resource; CARD16 length B16; CARD8 method; BYTE pad1, pad2, pad3; CARD32 srcLength B32; CARD32 dstLength B32; } xNXSetUnpackColormapReq; typedef struct _NXSetUnpackAlphaReq { CARD8 reqType; CARD8 resource; CARD16 length B16; CARD8 method; BYTE pad1, pad2, pad3; CARD32 srcLength B32; CARD32 dstLength B32; } xNXSetUnpackAlphaReq; typedef struct _NXPutPackedImageReq { CARD8 reqType; CARD8 resource; CARD16 length B16; Drawable drawable B32; GContext gc B32; CARD8 method; CARD8 format; CARD8 srcDepth; CARD8 dstDepth; CARD32 srcLength B32; CARD32 dstLength B32; INT16 srcX B16, srcY B16; CARD16 srcWidth B16, srcHeight B16; INT16 dstX B16, dstY B16; CARD16 dstWidth B16, dstHeight B16; } xNXPutPackedImageReq; typedef struct _NXFreeUnpackReq { CARD8 reqType; CARD8 resource; CARD16 length B16; } xNXFreeUnpackReq; /* * The X_NXSplitData and X_NXSplitEvent opcodes * are used internally and are ignored if coming * from the agent. */ #define X_NXInternalGenericData 0 #define X_NXInternalGenericReply 1 #define X_NXInternalGenericRequest 255 #define X_NXInternalShapeExtension 128 #define X_NXInternalRenderExtension 129 #define X_NXFirstOpcode 230 #define X_NXLastOpcode 252 #define X_NXGetControlParameters 230 #define X_NXGetCleanupParameters 231 #define X_NXGetImageParameters 232 #define X_NXGetUnpackParameters 233 #define X_NXStartSplit 234 #define X_NXEndSplit 235 #define X_NXSplitData 236 #define X_NXCommitSplit 237 #define X_NXSetExposeParameters 240 #define X_NXSetUnpackGeometry 241 #define X_NXSetUnpackColormap 242 #define X_NXPutPackedImage 243 #define X_NXSplitEvent 244 #define X_NXGetShmemParameters 245 #define X_NXSetUnpackAlpha 246 #define X_NXFreeUnpack 247 #define X_NXFinishSplit 248 #define X_NXAbortSplit 249 #define X_NXFreeSplit 250 #define X_NXGetFontParameters 251 #define X_NXSetCacheParameters 252 /* * The following events are received by the agent * in the form of a ClientMessage with the value * 0 in fields atom and window. The format is * always 32. Event specific data starts at byte * offset 12. * * These events are sent by the NX transport to * notify the agent about the result of a split * operation. */ #define NXNoSplitNotify 1 #define NXStartSplitNotify 2 #define NXCommitSplitNotify 3 #define NXEndSplitNotify 4 #define NXEmptySplitNotify 5 /* * Notifications of collect events. These events * don't come from the NX transport but are put * back in client's event queue by NXlib. */ #define NXCollectImageNotify 8 #define NXCollectPropertyNotify 9 #define NXCollectGrabPointerNotify 10 #define NXCollectInputFocusNotify 11 #undef Drawable #undef GContext #ifdef __cplusplus } #endif #endif /* NXproto_H */ nxcomp/ImageText16.cpp0000644000076400007640000001546711323113027015102 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ImageText16.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int ImageText16Store::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { ImageText16Message *imageText16 = (ImageText16Message *) message; // // Here is the fingerprint. // imageText16 -> len = *(buffer + 1); imageText16 -> drawable = GetULONG(buffer + 4, bigEndian); imageText16 -> gcontext = GetULONG(buffer + 8, bigEndian); imageText16 -> x = GetUINT(buffer + 12, bigEndian); imageText16 -> y = GetUINT(buffer + 14, bigEndian); if ((int) size > dataOffset) { int pad = (size - dataOffset) - (imageText16 -> len * 2); if (pad > 0) { CleanData((unsigned char *) buffer + size - pad, pad); } } #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int ImageText16Store::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { ImageText16Message *imageText16 = (ImageText16Message *) message; // // Fill all the message's fields. // *(buffer + 1) = imageText16 -> len; PutULONG(imageText16 -> drawable, buffer + 4, bigEndian); PutULONG(imageText16 -> gcontext, buffer + 8, bigEndian); PutUINT(imageText16 -> x, buffer + 12, bigEndian); PutUINT(imageText16 -> y, buffer + 14, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void ImageText16Store::dumpIdentity(const Message *message) const { #ifdef DUMP ImageText16Message *imageText16 = (ImageText16Message *) message; *logofs << name() << ": Identity len " << (unsigned int) imageText16 -> len << " drawable " << imageText16 -> drawable << ", gcontext " << imageText16 -> gcontext << ", x " << imageText16 -> x << ", y " << imageText16 -> y << ", size " << imageText16 -> size_ << ".\n"; #endif } void ImageText16Store::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 1, 1); } void ImageText16Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { ImageText16Message *imageText16 = (ImageText16Message *) message; ImageText16Message *cachedImageText16 = (ImageText16Message *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << imageText16 -> drawable << " as " << "drawable" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(imageText16 -> drawable, clientCache -> drawableCache); cachedImageText16 -> drawable = imageText16 -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << imageText16 -> gcontext << " as " << "gcontext" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(imageText16 -> gcontext, clientCache -> gcCache); cachedImageText16 -> gcontext = imageText16 -> gcontext; #ifdef TEST *logofs << name() << ": Encoding value " << imageText16 -> x << " as " << "x" << " field.\n" << logofs_flush; #endif unsigned short int diff_x = imageText16 -> x - cachedImageText16 -> x; encodeBuffer.encodeCachedValue(diff_x, 16, clientCache -> imageTextCacheX); cachedImageText16 -> x = imageText16 -> x; #ifdef TEST *logofs << name() << ": Encoding value " << imageText16 -> y << " as " << "y" << " field.\n" << logofs_flush; #endif unsigned short int diff_y = imageText16 -> y - cachedImageText16 -> y; encodeBuffer.encodeCachedValue(diff_y, 16, clientCache -> imageTextCacheY); cachedImageText16 -> y = imageText16 -> y; } void ImageText16Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { ImageText16Message *imageText16 = (ImageText16Message *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); imageText16 -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << imageText16 -> drawable << " as " << "drawable" << " field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); imageText16 -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << imageText16 -> gcontext << " as gcontext field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> imageTextCacheX); imageText16 -> x += value; imageText16 -> x &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << imageText16 -> x << " as x field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> imageTextCacheY); imageText16 -> y += value; imageText16 -> y &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << imageText16 -> y << " as y field.\n" << logofs_flush; #endif } nxcomp/Utils.cpp0000644000076400007640000000263411323113030014126 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "NX.h" #include "Timestamp.h" // // Log level. // #define PANIC #define WARNING #undef TEST #undef DEBUG int NXTransSearch(char *data, int size, int step, int threshold) { return -1; } nxcomp/Split.h0000644000076400007640000002214311323113027013571 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Split_H #define Split_H #include "Types.h" #include "Timestamp.h" #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Define this to know how many splits // are allocated and deallocated. // #undef REFERENCES // // Size of header of messages saved on // disk. // #define SPLIT_HEADER_SIZE 12 // // This class is used to divide big messages // in smaller chunks and send them at idle // time. // class EncodeBuffer; class DecodeBuffer; class SplitStore; class CommitStore; // // Preferred message streaming policy. // typedef enum { split_none = -1, split_async = 1, split_sync } T_split_mode; // // Current state of the split. Used to // implement the state machine. // typedef enum { split_undefined = -1, split_added, split_missed, split_loaded, split_aborted, split_notified } T_split_state; class Split { friend class SplitStore; friend class CommitStore; public: Split(); ~Split(); // // Note that, differently from the message // store, the split store doesn't account // for the data offset when dealing with // the data. This means that both the size_ // and c_size members represent the actual // size of the data part. // void compressedSize(int size) { c_size_ = size; store_ -> validateSize(d_size_, c_size_); } int compressedSize() { return c_size_; } int plainSize() { return i_size_ + d_size_; } T_checksum getChecksum() { return checksum_; } MessageStore *getStore() { return store_; } T_split_state getState() { return state_; } T_store_action getAction() { return action_; } // // We may need to find the resource // associated to the split message // because old protocol version use // a single store for all splits. // int getResource() { return resource_; } int getRequest() { return store_ -> opcode(); } int getPosition() { return position_; } T_split_mode getMode() { return mode_; } void setPolicy(int load, int save) { load_ = load; save_ = save; } void setState(T_split_state state) { state_ = state; } private: // // The agent's resource which is splitting // the message. // int resource_; // // Where to find the message in the message // store or the X sequence number of the // original request, in recent versions. // int position_; // // Which store is involved. // MessageStore *store_; // // Identity size of the message. // int i_size_; // // This is the uncompressed data size of the // original message. // int d_size_; // // This is the size of the compressed data, // if the data is stored in this form. // int c_size_; // // Size of the data buffer, as known by the // encoding side. This field is only used at // the decoding side. The remote size can be // different from the actual data size, if // the encoding side did not confirm that it // received the abort split event. // int r_size_; // // Position in the data buffer that will be // the target of the next send or receive // operation while streaming the message. // int next_; // // Load or save the split to disk. // int load_; int save_; // // Checksum of the original message. // T_checksum checksum_; // // Was this split confirmed or aborted? // T_split_state state_; // // What's the policy for sending this split? // T_split_mode mode_; // // Operation that had been performed on the // store at the time the split was added. // T_store_action action_; // // Container for the identity and data part // of the X message. // T_data identity_; T_data data_; #ifdef REFERENCES static int references_; #endif }; class SplitStore { public: SplitStore(StaticCompressor *compressor, CommitStore *commits, int resource); ~SplitStore(); Split *getFirstSplit() const { if (splits_ -> size() > 0) { return (*(splits_ -> begin())); } return NULL; } Split *getLastSplit() const { if (splits_ -> size() > 0) { return (*(--(splits_ -> end()))); } return NULL; } int getNodeSize(const Split *split) const { // // Take in account 64 bytes of overhead // for each node. // return (sizeof(class Split) + 64 + split -> i_size_ + split -> d_size_); } int getStorageSize() { return splitStorageSize_; } static int getTotalSize() { return totalSplitSize_; } static int getTotalStorageSize() { return totalSplitStorageSize_; } int getResource() { return resource_; } int getSize() { return splits_ -> size(); } T_splits *getSplits() { return splits_; } // // Used, respectively, at the encoding // and decoding side. // Split *add(MessageStore *store, int resource, T_split_mode mode, int position, T_store_action action, T_checksum checksum, const unsigned char *buffer, const int size); Split *add(MessageStore *store, int resource, int position, T_store_action action, T_checksum checksum, unsigned char *buffer, const int size); // // Handle the streaming of the message data. // int send(EncodeBuffer &encodeBuffer, int packetSize); int receive(DecodeBuffer &decodeBuffer); // // Remove the top element of the split store // and update the storage size. // void remove(Split *split); // // Load the message from disk and replace the // message in the store with the new copy. // int load(Split *split); // // Save the data to disk after the message has // been recomposed at the local side. // int save(Split *split); // // Find the message on disk and update the last // modification time. This is currently unused. // int find(Split *split); // // Remove the element on top of the queue and // discard any split data that still needs to // be transferred. // Split *pop(); // // Dump the content of the store. // void dump(); protected: // // Repository where to add the splits. // T_splits *splits_; // // Compress and decompress the data payload. // StaticCompressor *compressor_; private: int start(EncodeBuffer &encodeBuffer); int start(DecodeBuffer &decodeBuffer); void push(Split *split); // // Determine the name of the file object based // on the checksum. // const char *name(const T_checksum checksum); // // The number of elements and data bytes // in the repository. // int splitStorageSize_; static int totalSplitSize_; static int totalSplitStorageSize_; // // Current element being transferred. // T_splits::iterator current_; // // Repository where to move the splits // after they are completely recomposed. // CommitStore *commits_; // // Index in the client store or none, // if this is a commit store. // int resource_; #ifdef REFERENCES static int references_; #endif }; class CommitStore : public SplitStore { // // This is just a split store. // public: CommitStore(StaticCompressor *compressor) : SplitStore(compressor, NULL, nothing) { } // // Move identity and data of the split to the // provided buffer, uncompressing the message, // if needed. // int expand(Split *split, unsigned char *buffer, const int size); // // We recomposed the data part. If the message // was originally added to the message store, // replace the data and/or update the size. // int update(Split *split); // // Remove the split from the commit queue. // Split *pop(); // // This is just used for debug. It checks // if any message in the message store has // an invalid number of locks. // int validate(Split *split); }; #endif /* Split_H */ nxcomp/RenderFreeGlyphSet.cpp0000644000076400007640000001137111323113027016533 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderFreeGlyphSet.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeFreeXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> renderFreeGlyphSetCache); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { unsigned int value; ClientCache *clientCache = (ClientCache *) channelCache; *(buffer + 1) = type; decodeBuffer.decodeFreeXidValue(value, clientCache -> renderFreeGlyphSetCache); PutULONG(value, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.free_set.type = *(buffer + 1); renderExtension -> data.free_set.set_id = GetULONG(buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.free_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.free_set.type; PutULONG(renderExtension -> data.free_set.set_id, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.free_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeFreeXidValue(renderExtension -> data.free_set.set_id, clientCache -> renderFreeGlyphSetCache); cachedRenderExtension -> data.free_set.set_id = renderExtension -> data.free_set.set_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.free_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeFreeXidValue(renderExtension -> data.free_set.set_id, clientCache -> renderFreeGlyphSetCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.free_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/PolyArc.cpp0000644000076400007640000001131211323113030014370 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "PolyArc.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int PolyArcStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { PolyArcMessage *polyArc = (PolyArcMessage *) message; // // Here is the fingerprint. // polyArc -> drawable = GetULONG(buffer + 4, bigEndian); polyArc -> gcontext = GetULONG(buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int PolyArcStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { PolyArcMessage *polyArc = (PolyArcMessage *) message; // // Fill all the message's fields. // PutULONG(polyArc -> drawable, buffer + 4, bigEndian); PutULONG(polyArc -> gcontext, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void PolyArcStore::dumpIdentity(const Message *message) const { #ifdef DUMP PolyArcMessage *polyArc = (PolyArcMessage *) message; *logofs << name() << ": Identity drawable " << polyArc -> drawable << ", gcontext " << polyArc -> gcontext << ", size " << polyArc -> size_ << ".\n" << logofs_flush; #endif } void PolyArcStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { } void PolyArcStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { PolyArcMessage *polyArc = (PolyArcMessage *) message; PolyArcMessage *cachedPolyArc = (PolyArcMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << polyArc -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyArc -> drawable, clientCache -> drawableCache); cachedPolyArc -> drawable = polyArc -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << polyArc -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyArc -> gcontext, clientCache -> gcCache); cachedPolyArc -> gcontext = polyArc -> gcontext; } void PolyArcStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { PolyArcMessage *polyArc = (PolyArcMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); polyArc -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyArc -> drawable << " as drawable field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); polyArc -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyArc -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } nxcomp/Makefile.in0000644000076400007640000001576611323113027014407 0ustar svetonisvetoni############################################################################ # # # Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. # # # # NXCOMP, NX protocol compression and NX extensions to this software # # are copyright of NoMachine. Redistribution and use of the present # # software is allowed according to terms specified in the file LICENSE # # which comes in the source distribution. # # # # Check http://www.nomachine.com/licensing.html for applicability. # # # # NX and NoMachine are trademarks of Medialogic S.p.A. # # # # All rights reserved. # # # ############################################################################ # # Get these values from the configure script. The # version printed by the program should be derived # from the CHANGELOG. For example we may use the # following command: # # head -n 3 CHANGELOG | grep 'nxcomp-' | cut -d '-' -f 2-3 # VERSION=@VERSION@ LIBVERSION=@LIBVERSION@ # # We would really like to enable all warnings, -Wredundant-decls, # though, gives a warning caused by pthread.h and unistd.h and # GCC 3.4 was changed in a way that it now complains about some # of the -W directives we used before (-Wmissing-declarations, # -Wnested-externs, -Wstrict-prototypes and -Wmissing-prototypes). # CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ @X_CFLAGS@ @DEFS@ \ -Wall -Wpointer-arith CXXINCLUDES = CXXDEFINES = # # C programs have their own CFLAGS. # CC = @CC@ CCFLAGS = @CFLAGS@ @X_CFLAGS@ @DEFS@ \ -Wall -Wpointer-arith CCINCLUDES = CCDEFINES = LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ # # Other autoconfigured settings, not used at the moment. # srcdir = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ man1dir = @mandir@/man1 VPATH = @srcdir@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ # # This should be autodetected. # MAKEDEPEND = @MAKEDEPEND@ DEPENDINCLUDES = -I/usr/include/c++ -I/usr/include/g++ -I/usr/include/g++-3 .SUFFIXES: .cpp.c .cpp.o: $(CXX) -c $(CXXFLAGS) $(CXXINCLUDES) $(CXXDEFINES) $< .c.o: $(CC) -c $(CCFLAGS) $(CCINCLUDES) $(CCDEFINES) $< LIBRARY = Xcomp LIBNAME = lib$(LIBRARY) LIBFULL = lib$(LIBRARY).so.$(VERSION) LIBLOAD = lib$(LIBRARY).so.$(LIBVERSION) LIBSHARED = lib$(LIBRARY).so LIBARCHIVE = lib$(LIBRARY).a LIBCYGSHARED = cyg$(LIBRARY).dll LIBCYGARCHIVE = lib$(LIBRARY).dll.a all: depend @ALL@ MSRC = CSRC = MD5.c \ Pack.c \ Vars.c CXXSRC = Loop.cpp \ Children.cpp \ Control.cpp \ Misc.cpp \ Socket.cpp \ Fork.cpp \ Pipe.cpp \ List.cpp \ Keeper.cpp \ Timestamp.cpp \ Transport.cpp \ Statistics.cpp \ Auth.cpp \ Agent.cpp \ Proxy.cpp \ Channel.cpp \ Message.cpp \ Split.cpp \ ClientProxy.cpp \ ServerProxy.cpp \ OpcodeStore.cpp \ ClientStore.cpp \ ServerStore.cpp \ ChannelCache.cpp \ ClientCache.cpp \ ServerCache.cpp \ ClientChannel.cpp \ ServerChannel.cpp \ GenericChannel.cpp \ ReadBuffer.cpp \ ProxyReadBuffer.cpp \ ClientReadBuffer.cpp \ ServerReadBuffer.cpp \ GenericReadBuffer.cpp \ EncodeBuffer.cpp \ DecodeBuffer.cpp \ WriteBuffer.cpp \ SequenceQueue.cpp \ IntCache.cpp \ CharCache.cpp \ XidCache.cpp \ ActionCache.cpp \ BlockCache.cpp \ BlockCacheSet.cpp \ StaticCompressor.cpp \ TextCompressor.cpp \ Unpack.cpp \ Alpha.cpp \ Colormap.cpp \ Jpeg.cpp \ Pgn.cpp \ Bitmap.cpp \ Rgb.cpp \ Rle.cpp \ Z.cpp \ ChangeProperty.cpp \ SendEvent.cpp \ ChangeGC.cpp \ CreateGC.cpp \ CreatePixmap.cpp \ SetClipRectangles.cpp \ CopyArea.cpp \ PolyLine.cpp \ PolySegment.cpp \ PolyFillRectangle.cpp \ PutImage.cpp \ TranslateCoords.cpp \ GetImage.cpp \ ClearArea.cpp \ ConfigureWindow.cpp \ PolyText8.cpp \ PolyText16.cpp \ ImageText8.cpp \ ImageText16.cpp \ PolyPoint.cpp \ PolyFillArc.cpp \ PolyArc.cpp \ FillPoly.cpp \ InternAtom.cpp \ GetProperty.cpp \ SetUnpackGeometry.cpp \ SetUnpackColormap.cpp \ SetUnpackAlpha.cpp \ PutPackedImage.cpp \ ShapeExtension.cpp \ RenderExtension.cpp \ GenericRequest.cpp \ QueryFontReply.cpp \ ListFontsReply.cpp \ GetImageReply.cpp \ GetPropertyReply.cpp \ GenericReply.cpp \ RenderGenericRequest.cpp \ RenderCreatePicture.cpp \ RenderChangePicture.cpp \ RenderFreePicture.cpp \ RenderPictureClip.cpp \ RenderPictureTransform.cpp \ RenderPictureFilter.cpp \ RenderCreateGlyphSet.cpp \ RenderFreeGlyphSet.cpp \ RenderAddGlyphs.cpp \ RenderComposite.cpp \ RenderCompositeGlyphs.cpp \ RenderFillRectangles.cpp \ RenderTrapezoids.cpp \ RenderTriangles.cpp \ PositionCacheCompat.cpp \ ChangeGCCompat.cpp \ CreatePixmapCompat.cpp \ SetUnpackColormapCompat.cpp \ SetUnpackAlphaCompat.cpp \ RenderCreatePictureCompat.cpp \ RenderFreePictureCompat.cpp \ RenderPictureClipCompat.cpp \ RenderCreateGlyphSetCompat.cpp \ RenderCompositeCompat.cpp \ RenderCompositeGlyphsCompat.cpp MOBJ = $(MSRC:.c=.o) COBJ = $(CSRC:.c=.o) CXXOBJ = $(CXXSRC:.cpp=.o) $(LIBFULL): $(CXXOBJ) $(COBJ) $(CXX) -o $@ $(LDFLAGS) $(CXXOBJ) $(COBJ) $(LIBS) $(LIBLOAD): $(LIBFULL) rm -f $(LIBLOAD) ln -s $(LIBFULL) $(LIBLOAD) $(LIBSHARED): $(LIBFULL) rm -f $(LIBSHARED) ln -s $(LIBFULL) $(LIBSHARED) $(LIBARCHIVE): $(CXXOBJ) $(COBJ) rm -f $(LIBARCHIVE) ar clq $(LIBARCHIVE) $(CXXOBJ) $(COBJ) ranlib $(LIBARCHIVE) $(LIBCYGSHARED): $(LIBARCHIVE) $(CC) -shared -o $(LIBCYGSHARED) \ -Wl,--out-implib=$(LIBCYGARCHIVE) \ -Wl,--export-all-symbols \ -Wl,--enable-auto-import \ -Wl,--whole-archive $(LIBARCHIVE) \ -Wl,--no-whole-archive $(LIBS) \ $(LDFLAGS) $(LIBCYGARCHIVE): $(LIBCYGSHARED) depends: depend.status depend: depend.status depend.status: if [ -x $(MAKEDEPEND) ] ; then \ $(MAKEDEPEND) $(CXXINCLUDES) $(CCINCLUDES) \ $(DEPENDINCLUDES) -f Makefile $(MSRC) $(CSRC) \ $(CXXSRC) 2>/dev/null; \ fi touch depend.status install: install.bin install.man install.bin: install.man: clean: -rm -f *~ *.o *.bak *.orig *.rej st?????? core core.* *.out.* \ @ALL@ distclean: clean -rm -rf autom4te.cache config.status config.log \ config.cache depend.status Makefile tags nxcomp/RenderExtension.cpp0000644000076400007640000004530611323113031016146 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "NXrender.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" #include "RenderExtension.h" #include "RenderGenericRequest.h" #include "RenderCreatePicture.h" #include "RenderChangePicture.h" #include "RenderFreePicture.h" #include "RenderPictureClip.h" #include "RenderPictureTransform.h" #include "RenderPictureFilter.h" #include "RenderCreateGlyphSet.h" #include "RenderFreeGlyphSet.h" #include "RenderAddGlyphs.h" #include "RenderComposite.h" #include "RenderCompositeGlyphs.h" #include "RenderFillRectangles.h" #include "RenderTrapezoids.h" #include "RenderTriangles.h" #include "RenderCreatePictureCompat.h" #include "RenderFreePictureCompat.h" #include "RenderPictureClipCompat.h" #include "RenderCreateGlyphSetCompat.h" #include "RenderCompositeCompat.h" #include "RenderCompositeGlyphsCompat.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Constructor and destructor. // RenderExtensionStore::RenderExtensionStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = RENDEREXTENSION_ENABLE_CACHE; enableData = RENDEREXTENSION_ENABLE_DATA; enableSplit = RENDEREXTENSION_ENABLE_SPLIT; enableCompress = RENDEREXTENSION_ENABLE_COMPRESS; generic_ = new RenderGenericRequestStore(); for (int i = 0; i < RENDEREXTENSION_MINOR_OPCODE_LIMIT; i++) { minors_[i] = generic_; } minors_[X_RenderChangePicture] = new RenderChangePictureStore(); minors_[X_RenderFillRectangles] = new RenderFillRectanglesStore(); minors_[X_RenderAddGlyphs] = new RenderAddGlyphsStore(); if (control -> isProtoStep7() == 1) { minors_[X_RenderCreatePicture] = new RenderCreatePictureStore(); minors_[X_RenderFreePicture] = new RenderFreePictureStore(); minors_[X_RenderSetPictureClipRectangles] = new RenderPictureClipStore(); minors_[X_RenderCreateGlyphSet] = new RenderCreateGlyphSetStore(); minors_[X_RenderComposite] = new RenderCompositeStore(); minors_[X_RenderCompositeGlyphs8] = new RenderCompositeGlyphsStore(); minors_[X_RenderCompositeGlyphs16] = new RenderCompositeGlyphsStore(); minors_[X_RenderCompositeGlyphs32] = new RenderCompositeGlyphsStore(); minors_[X_RenderSetPictureTransform] = new RenderPictureTransformStore(); minors_[X_RenderSetPictureFilter] = new RenderPictureFilterStore(); minors_[X_RenderFreeGlyphSet] = new RenderFreeGlyphSetStore(); minors_[X_RenderTrapezoids] = new RenderTrapezoidsStore(); minors_[X_RenderTriangles] = new RenderTrianglesStore(); } else { minors_[X_RenderCreatePicture] = new RenderCreatePictureCompatStore(); minors_[X_RenderFreePicture] = new RenderFreePictureCompatStore(); minors_[X_RenderSetPictureClipRectangles] = new RenderPictureClipCompatStore(); minors_[X_RenderCreateGlyphSet] = new RenderCreateGlyphSetCompatStore(); minors_[X_RenderComposite] = new RenderCompositeCompatStore(); minors_[X_RenderCompositeGlyphs8] = new RenderCompositeGlyphsCompatStore(); minors_[X_RenderCompositeGlyphs16] = new RenderCompositeGlyphsCompatStore(); minors_[X_RenderCompositeGlyphs32] = new RenderCompositeGlyphsCompatStore(); } dataLimit = RENDEREXTENSION_DATA_LIMIT; dataOffset = RENDEREXTENSION_DATA_OFFSET; if (control -> isProtoStep7() == 1) { cacheSlots = RENDEREXTENSION_CACHE_SLOTS_IF_PROTO_STEP_7; } else { cacheSlots = RENDEREXTENSION_CACHE_SLOTS; } cacheThreshold = RENDEREXTENSION_CACHE_THRESHOLD; cacheLowerThreshold = RENDEREXTENSION_CACHE_LOWER_THRESHOLD; opcode_ = X_NXInternalRenderExtension; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } RenderExtensionStore::~RenderExtensionStore() { for (int i = 0; i < RENDEREXTENSION_MINOR_OPCODE_LIMIT; i++) { if (minors_[i] != generic_) { delete minors_[i]; } } delete generic_; for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } int RenderExtensionStore::validateMessage(const unsigned char *buffer, int size) { #ifdef TEST *logofs << name() << ": Encoding message OPCODE#" << (unsigned) *buffer << " MINOR#" << (unsigned) *(buffer + 1) << " with size " << size << ".\n" << logofs_flush; #endif return (size >= control -> MinimumMessageSize && size <= control -> MaximumMessageSize); } // // Here are the methods to handle the messages' content. // int RenderExtensionStore::identitySize(const unsigned char *buffer, unsigned int size) { return minors_[*(buffer + 1)] -> identitySize(buffer, size); } int RenderExtensionStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { encodeBuffer.encodeOpcodeValue(*(buffer + 1), ((ClientCache *) channelCache) -> renderOpcodeCache); minors_[*(buffer + 1)] -> encodeMessage(encodeBuffer, buffer, size, bigEndian, channelCache); return 1; } int RenderExtensionStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { unsigned char type; decodeBuffer.decodeOpcodeValue(type, ((ClientCache *) channelCache) -> renderOpcodeCache); minors_[type] -> decodeMessage(decodeBuffer, buffer, size, type, bigEndian, writeBuffer, channelCache); return 1; } int RenderExtensionStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { return minors_[*(buffer + 1)] -> parseIdentity(message, buffer, size, bigEndian); } int RenderExtensionStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { return minors_[((RenderExtensionMessage *) message) -> data.any.type] -> unparseIdentity(message, buffer, size, bigEndian); } void RenderExtensionStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { minors_[*(buffer + 1)] -> identityChecksum(message, buffer, size, md5_state_, bigEndian); } void RenderExtensionStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { minors_[((RenderExtensionMessage *) message) -> data.any.type] -> updateIdentity(encodeBuffer, message, cachedMessage, channelCache); } void RenderExtensionStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { minors_[((RenderExtensionMessage *) message) -> data.any.type] -> updateIdentity(decodeBuffer, message, channelCache); } void RenderExtensionStore::dumpIdentity(const Message *message) const { #ifdef DUMP #ifdef WARNING *logofs << name() << ": WARNING! Dump of identity not implemented.\n" << logofs_flush; #endif #endif } // // TODO: The following encoding and decoding functions // could be generalized further, for example by passing // the pointer to the data cache, the number of caches // made available by the caller and the first cache to // iterate through. // void RenderMinorExtensionStore::encodeLongData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const { if (control -> isProtoStep7() == 1) { encodeBuffer.encodeLongData(buffer + offset, size - offset); #ifdef TEST *logofs << name() << ": Encoded " << size - offset << " bytes of long data.\n" << logofs_flush; #endif return; } ClientCache *clientCache = (ClientCache *) channelCache; for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 4) { #ifdef DEBUG *logofs << name() << ": Encoding int with i = " << i << " c = " << c << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(GetULONG(buffer + i, bigEndian), 32, *clientCache -> renderDataCache[c]); if (++c == 16) c = 0; } } void RenderMinorExtensionStore::encodeIntData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const { if (control -> isProtoStep7() == 1) { encodeBuffer.encodeIntData(buffer + offset, size - offset); #ifdef TEST *logofs << name() << ": Encoded " << size - offset << " bytes of int data.\n" << logofs_flush; #endif return; } ClientCache *clientCache = (ClientCache *) channelCache; for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 2) { #ifdef DEBUG *logofs << name() << ": Encoding int with i = " << i << " c = " << c << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(GetUINT(buffer + i, bigEndian), 16, *clientCache -> renderDataCache[c]); if (++c == 16) c = 0; } } void RenderMinorExtensionStore::encodeCharData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const { if (control -> isProtoStep7() == 1) { encodeBuffer.encodeTextData(buffer + offset, size - offset); #ifdef TEST *logofs << name() << ": Encoded " << size - offset << " bytes of text data.\n" << logofs_flush; #endif return; } ClientCache *clientCache = (ClientCache *) channelCache; clientCache -> renderTextCompressor.reset(); const unsigned char *next = buffer + offset; for (unsigned int i = offset; i < size; i++) { #ifdef DEBUG *logofs << name() << ": Encoding char with i = " << i << ".\n" << logofs_flush; #endif clientCache -> renderTextCompressor. encodeChar(*next++, encodeBuffer); } } void RenderMinorExtensionStore::decodeLongData(DecodeBuffer &decodeBuffer, unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const { if (control -> isProtoStep7() == 1) { decodeBuffer.decodeLongData(buffer + offset, size - offset); #ifdef TEST *logofs << name() << ": Decoded " << size - offset << " bytes of long data.\n" << logofs_flush; #endif return; } ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 4) { #ifdef DEBUG *logofs << name() << ": Decoding int with i = " << i << " c = " << c << ".\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 32, *clientCache -> renderDataCache[c]); PutULONG(value, buffer + i, bigEndian); if (++c == 16) c = 0; } } void RenderMinorExtensionStore::decodeIntData(DecodeBuffer &decodeBuffer, unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const { if (control -> isProtoStep7() == 1) { decodeBuffer.decodeIntData(buffer + offset, size - offset); #ifdef TEST *logofs << name() << ": Decoded " << size - offset << " bytes of int data.\n" << logofs_flush; #endif return; } ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 2) { #ifdef DEBUG *logofs << name() << ": Decoding int with i = " << i << " c = " << c << ".\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, *clientCache -> renderDataCache[c]); PutUINT(value, buffer + i, bigEndian); if (++c == 16) c = 0; } } void RenderMinorExtensionStore::decodeCharData(DecodeBuffer &decodeBuffer, unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const { if (control -> isProtoStep7() == 1) { decodeBuffer.decodeTextData(buffer + offset, size - offset); #ifdef TEST *logofs << name() << ": Decoded " << size - offset << " bytes of text data.\n" << logofs_flush; #endif return; } ClientCache *clientCache = (ClientCache *) channelCache; clientCache -> renderTextCompressor.reset(); unsigned char *next = buffer + offset; for (unsigned int i = offset; i < size; i++) { #ifdef DEBUG *logofs << name() << ": Decoding char with i = " << i << ".\n" << logofs_flush; #endif *next++ = clientCache -> renderTextCompressor. decodeChar(decodeBuffer); } } void RenderMinorExtensionStore::parseIntData(const Message *message, const unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian) const { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_); for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2) { #ifdef DEBUG *logofs << name() << ": Parsing int with i = " << i << " c = " << c << ".\n" << logofs_flush; #endif renderExtension -> data.any.short_data[c] = GetUINT(buffer + i, bigEndian); if (++c == 16) c = 0; } } void RenderMinorExtensionStore::unparseIntData(const Message *message, unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian) const { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_); for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2) { #ifdef DEBUG *logofs << name() << ": Unparsing int with i = " << i << " c = " << c << ".\n" << logofs_flush; #endif PutUINT(renderExtension -> data.any.short_data[c], buffer + i, bigEndian); if (++c == 16) c = 0; } } void RenderMinorExtensionStore::updateIntData(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, unsigned int offset, unsigned int size, ChannelCache *channelCache) const { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_); for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2) { #ifdef DEBUG *logofs << name() << ": Encoding int update with i = " << i << " c = " << c << ".\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(renderExtension -> data.any.short_data[c], 16, *clientCache -> renderDataCache[c]); cachedRenderExtension -> data.any.short_data[c] = renderExtension -> data.any.short_data[c]; if (++c == 16) c = 0; } } void RenderMinorExtensionStore::updateIntData(DecodeBuffer &decodeBuffer, const Message *message, unsigned int offset, unsigned int size, ChannelCache *channelCache) const { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_); unsigned int value; for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2) { #ifdef DEBUG *logofs << name() << ": Decoding int update with i = " << i << " c = " << c << ".\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, *clientCache -> renderDataCache[c]); renderExtension -> data.any.short_data[c] = value; if (++c == 16) c = 0; } } nxcomp/Rle.cpp0000644000076400007640000000621711323113027013557 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Misc.h" #include "Rle.h" #define PANIC #define WARNING #undef TEST #undef DEBUG int UnpackRle(T_geometry *geometry, unsigned char method, unsigned char *src_data, int src_size, int dst_bpp, int dst_width, int dst_height, unsigned char *dst_data, int dst_size) { if (*src_data == 0) { if (dst_size != src_size - 1) { #ifdef TEST *logofs << "UnpackRle: PANIC! Invalid destination size " << dst_size << " with source " << src_size << ".\n" << logofs_flush; #endif return -1; } #ifdef TEST *logofs << "UnpackRle: Expanding " << src_size - 1 << " bytes of plain RLE data.\n" << logofs_flush; #endif memcpy(dst_data, src_data + 1, src_size - 1); return 1; } unsigned int check_size = dst_size; int result = ZDecompress(&unpackStream, dst_data, &check_size, src_data + 1, src_size - 1); if (result != Z_OK) { #ifdef PANIC *logofs << "UnpackRle: PANIC! Failure decompressing RLE data. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failure decompressing RLE data. " << "Error is '" << zError(result) << "'.\n"; return -1; } else if (check_size != (unsigned int) dst_size) { #ifdef PANIC *logofs << "UnpackRle: PANIC! Size mismatch in RLE data. " << "Resulting size is " << check_size << " with " << "expected size " << dst_size << ".\n" << logofs_flush; #endif cerr << "Error" << ": Size mismatch in RLE data. " << "Resulting size is " << check_size << " with " << "expected size " << dst_size << ".\n"; return -1; } #ifdef TEST *logofs << "UnpackRle: Decompressed " << src_size - 1 << " bytes to " << dst_size << " bytes of RLE data.\n" << logofs_flush; #endif return 1; } nxcomp/Misc.cpp0000644000076400007640000012115511323113030013721 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include #include #include #include "NXproto.h" #include "MD5.h" #include "Misc.h" #include "Proxy.h" // // Set the verbosity level. // #define PANIC #define WARNING #define OPCODES #undef TEST #undef DEBUG // // TCP port offset applied to any NX port specification. // const int DEFAULT_NX_PROXY_PORT_OFFSET = 4000; // // Default TCP port used by client proxy to listen to // X clients and by server proxy to connect to remote. // const int DEFAULT_NX_PROXY_PORT = 8; // // Default X display number that client proxy imitates. // const int DEFAULT_NX_X_PORT = 8; // // Default ports used for listening for cups, samba, http, // multimedia and auxiliary X connections. Arbitrary ports // can be used by passing the service's port at the proxy // startup. By default ports are determined by adding the // offset below to the offset of the proxied display. For // example, if the proxy is impersonating the display :8, // SMB tunnels can be created by connecting to port 3008. // // Considering that the NX server uses to start the first // session at display offset 1000, we must lower the CUPS // and SMB ports to avoid interference with normal X ses- // sions run on the server. // // Font server connections are used to let the X server on // the client connect to a font server on the NX server. // // Slave channels can be originated by both sides so we need // different offsets in the case the user runs both proxies // on the same host. // const int DEFAULT_NX_CUPS_PORT_OFFSET = 2000; const int DEFAULT_NX_SMB_PORT_OFFSET = 3000; const int DEFAULT_NX_MEDIA_PORT_OFFSET = 7000; const int DEFAULT_NX_AUX_PORT_OFFSET = 8000; const int DEFAULT_NX_HTTP_PORT_OFFSET = 9000; const int DEFAULT_NX_FONT_PORT_OFFSET = 10000; const int DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET = 11000; const int DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET = 12000; // // Usage info and copyright. // static const char UsageInfo[] = "\n\ Usage: nxproxy [OPTIONS] host:port\n\ \n\ -C Specify that nxproxy has to run on the 'X client'\n\ side, listening for connections and impersonating\n\ an X server.\n\ \n\ -S Specify that nxproxy has to run in 'X server' mode,\n\ thus forwarding the connections to daemons running\n\ on the client.\n\ \n\ -h Print this message.\n\ \n\ -v Print version information.\n\ \n\ host:port Put at the end, specifies the host and port of the\n\ listening proxy.\n\ \n\ name=value Set the NX option to the provided value.\n\ \n\ Multiple NX options can be specified in the DISPLAY environment\n\ or on the command line, by using the nx/nx,option=value notation.\n\ \n\ Options:\n\ \n\ link=s An indication of the link speed that is going to be\n\ used between the proxies. Usually the compression\n\ and the other link parameters depend on this setting.\n\ The value can be either 'modem', 'isdn', 'adsl',\n\ 'wan', 'lan', 'local' or a bandwidth specification,\n\ like for example '56k', '1m', '100m', etc.\n\ \n\ type=s Type of session, for example 'windows', 'unix-kde'.\n\ 'unix-application', etc.\n\ \n\ display=s Specify the real display where X connections have\n\ to be forwarded by the proxy running on the client.\n\ \n\ listen=n Local port used for accepting the proxy connection.\n\ \n\ accept=s Name or IP of host that can connect to the proxy.\n\ \n\ connect=s Name or IP of host that the proxy will connect to.\n\ \n\ port=n Remote port used for the connection.\n\ \n\ retry=n Number of connection atempts.\n\ \n\ root=s The root directory for the session. Usually is the\n\ C-* or S-* in the .nx directory in the user's home,\n\ with '*' being the virtual display.\n\ \n\ session=s Name of the session file. The default is the name\n\ 'session' in the session directory.\n\ \n\ errors=s Name of the log file used by the proxy. The default\n\ is the name 'errors' in the session directory.\n\ \n\ stats=s Name of the file where are written the proxy stat-\n\ istics. The default is a file 'stats' in the session\n\ directory. The proxy replaces the data in the file\n\ whenever it receives a SIGUSR1 or SIGUSR2 signal:\n\ \n\ SIGUSR1 Gives total statistics, i.e. statistics\n\ collected since the beginning of the\n\ session.\n\ \n\ SIGUSR2 Gives partial statistics, i.e. statist-\n\ ics collected since the last time this\n\ signal was received.\n\ \n\ cookie=s Use the provided cookie for authenticating to the\n\ remote proxy. The same cookie is used as the fake\n\ value used for the X authorization. The fake cookie\n\ is replaced on the X server side with the real cookie\n\ to be used for the display, so that the real cookie\n\ doesn't have to travel over the net. When not using\n\ a proxy cookie, any host will be able to connect to\n\ the proxy. See also the 'accept' parameter.\n\ \n\ nodelay=b A boolean indicating if TCP_NODELAY has to be set\n\ on the proxy link. Old Linux kernels had problems\n\ with handling TCP_NODELAY on PPP links.\n\ \n\ policy=b Let or not the agent decide when it is the best time\n\ to flush the proxy link. If set to 0, the proxy will\n\ flush any encoded data immediately. The option has\n\ only effect on the X client side proxy.\n\ \n\ render=b Enable or disable use of the RENDER extension.\n\ \n\ taint=b Try to suppress trivial sources of X roundtrips by\n\ generating the reply on the X client side.\n\ \n\ delta=b Enable X differential compression.\n\ \n\ data=n Enable or disable the ZLIB data compression. It is\n\ possible to specify a value between 0 and 9. Usual-\n\ ly the value is chosen automatically based on the\n\ requested link setting.\n\ \n\ stream=n Enable or disable the ZLIB stream compression. The\n\ value, between 0 and 9, is usually determined accor-\n\ ding to the requested link setting.\n\ \n\ limit=n Specify a bitrate limit allowed for this session.\n\ \n\ memory=n Trigger memory optimizations used to keep small the\n\ size of X buffers. This is useful on embedded plat-\n\ forms, or where memory is scarce.\n\ \n\ cache=n Size of the in-memory X message cache. Setting the\n\ value to 0 will disable the memory cache as well\n\ as the NX differential compression.\n\ \n\ images=n Size of the persistent image cache.\n\ \n\ shseg=n Enable the use of the MIT-SHM extension between the\n\ NX client proxy and the real X server. A value greater\n\ than 1 is assumed to be the size of requested shared\n\ memory segment. By default, the size of the segment is\n\ determined based on the size of the in-memory cache.\n\ \n\ load=b Enable loading a persistent X message cache at the\n\ proxy startup.\n\ \n\ save=b Enable saving a persistent X message cache at the\n\ end of session.\n\ \n\ cups=n Enable or disable forwarding of CUPS connections,\n\ by listening on the optional port 'n'.\n\ \n\ aux=n Enable or disable forwarding of the auxiliary X chan-\n\ nel used for controlling the keyboard. The 'keybd=n'\n\ form is accepted for backward compatibility.\n\ \n\ smb=n Enable or disable forwarding of SMB connections. The\n\ 'samba=n' form is accepted for backward compatibility.\n\ \n\ media=n Enable forwarding of audio connections.\n\ \n\ http=n Enable forwarding of HTTP connections.\n\ \n\ font=n Enable forwarding of reversed connections to a font\n\ server running on the NX server.\n\ \n\ file=n Enable forwarding of file transfer connections.\n\ \n\ mask=n Determine the distribution of channel ids between the\n\ proxies. By default, channels whose ids are multiple\n\ of 8 (starting from 0) are reserved for the NX client\n\ side. All the other channels can be allocated by the\n\ NX server side.\n\ \n\ timeout=t Specify the keep-alive timeout used by proxies to\n\ determine if there is a network problem preventing\n\ communication with the remote peer. A value of 0\n\ disables the check.\n\ \n\ cleanup=t Specify the number of seconds the proxy has to wait\n\ at session shutdown before closing all channels.\n\ The feature is used by the NX server to ensure that\n\ services are disconnected before shutting down the\n\ link.\n\ \n\ pack=s Determine the method used to compress images.\n\ \n\ product=s The product id of the client or server. The value is\n\ ignored by the proxy, but the client or server can\n\ provide it to facilitate the support.\n\ \n\ core=b Enable production of core dumps when aborting the\n\ proxy connection.\n\ \n\ options=s Specify an additional file containing options that\n\ has to be merged with option read from the command\n\ line or the environment.\n\ \n\ kill=n Add the given process to the list of daemons that\n\ must be terminated at session shutdown. Multiple\n\ 'kill=n' options can be specified. The proxy will\n\ send them a SIGTERM signal just before exiting.\n\ \n\ strict=b Optimize for responsiveness, rather than for the best\n\ use of all the available bandwidth.\n\ \n\ encryption=b Should be set to 1 if the proxy is running as part of\n\ a program providing encryption of the point to point\n\ communication.\n\ \n\ rootless=b\n\ geometry=s\n\ resize=b\n\ fullscreen=b\n\ keyboard=s\n\ clipboard=n\n\ streaming=n\n\ backingstore=n\n\ composite=n\n\ shmem=b\n\ shpix=b\n\ kbtype=s\n\ client=s\n\ shadow=n\n\ shadowuid=n\n\ shadowmode=s\n\ defer=n\n\ tile=s\n\ menu=n These options are interpreted by the NX agent. They\n\ are ignored by the proxy.\n\ \n\ Environment:\n\ \n\ NX_ROOT The root NX directory is the place where the session\n\ directory and the cache files are created. This is\n\ usually overridden by passing the 'root=' option. By\n\ default, the root NX directory is assumed to be the\n\ directory '.nx' in the user's home.\n\ \n\ NX_SYSTEM The directory where NX programs and libraries reside.\n\ If not set, the value is assumed to be '/usr/NX'.\n\ Programs, libraries and data files are respectedly\n\ searched in the 'bin', 'lib' and 'share' subdirecto-\n\ ries.\n\ \n\ NX_HOME The NX user's home directory. If NX_ROOT is not set\n\ or invalid, the user's NX directory is created here.\n\ \n\ NX_TEMP The directory where the X11 Unix Domain Sockets and\n\ all temporary files are to be created.\n\ \n\ NX_CLIENT The full path to the nxclient executable. If the va-\n\ riable is not set, the nxclient executable will be\n\ run assuming that the program is in the system path.\n\ This can be useful on platforms like Windows and the\n\ Mac where nxclient is located in a different direct-\n\ ory compared to the other programs, to make easier\n\ for the user to execute the program from the shell.\n\ \n\ Shell environment:\n\ \n\ HOME The variable is checked in the case NX_HOME is not\n\ set, null or invalid.\n\ \n\ TEMP The variable is checked whenever the NX_TEMP direct-\n\ ory is not set, null or invalid.\n\ \n\ PATH The path where all executables are searched, except\n\ nxclient. If NX_CLIENT is not set, also the client\n\ executable is searched in the system path.\n\ \n\ LD_LIBRARY_PATH\n\ System-wide library search order. This should be set\n\ by the program invoking the proxy.\n\ \n\ DISPLAY On the X server side, the DISPLAY variable indicates\n\ the location of the X11 server. When nxcomp is used\n\ as a transport library, the DISPLAY may represent a\n\ NX transport specification and options can passed in\n\ the form nx/nx,option=value...\n\ \n\ XAUTHORITY This is the file containing the X11 authorization\n\ cookie. If not set, the file is assumed to be in\n\ the user's home (either NX_HOME or HOME).\n\ \n\ "; const char *GetUsageInfo() { return UsageInfo; } static const char CopyrightInfo[] = "\ Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/.\n\ \n\ NXCOMP, NX protocol compression and NX extensions to this software \n\ are copyright of NoMachine. Redistribution and use of the present\n\ software is allowed according to terms specified in the file LICENSE\n\ which comes in the source distribution.\n\ \n\ Check http://www.nomachine.com/licensing.html for applicability.\n\ \n\ NX and NoMachine are trademarks of NoMachine S.r.l.\n\ \n\ All rights reserved.\n\ "; const char *GetCopyrightInfo() { return CopyrightInfo; } static const char OtherCopyrightInfo[] = "\ NX protocol compression is derived from DXPC project.\n\ \n\ Copyright (c) 1995,1996 Brian Pane\n\ Copyright (c) 1996,1997 Zachary Vonler and Brian Pane\n\ Copyright (c) 1999 Kevin Vigor and Brian Pane\n\ Copyright (c) 2000,2003 Gian Filippo Pinzari and Brian Pane\n\ \n\ All rights reserved.\n\ "; const char *GetOtherCopyrightInfo() { return OtherCopyrightInfo; } int _hostBigEndian = 0; int _storeBigEndian = 0; const unsigned int IntMask[33] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff }; unsigned int GetUINT(unsigned const char *buffer, int bigEndian) { // // It doesn't work on SPARCs if the buffer // is not aligned to the word boundary. We // should check the CPU, not the OS as this // surely applies to other architectures. // #ifndef __sun if (_hostBigEndian == bigEndian) { return *((unsigned short *) buffer); } #else if (_hostBigEndian == bigEndian && ((unsigned int) buffer) & 0x1 == 0) { return *((unsigned short *) buffer); } #endif unsigned int result; if (bigEndian) { result = *buffer; result <<= 8; result += buffer[1]; } else { result = buffer[1]; result <<= 8; result += *buffer; } return result; } unsigned int GetULONG(unsigned const char *buffer, int bigEndian) { // // It doesn't work on SPARCs if the buffer // is not aligned to word the boundary. // #ifndef __sun if (_hostBigEndian == bigEndian) { return *((unsigned int *) buffer); } #else if (_hostBigEndian == bigEndian && ((unsigned int) buffer) & 0x3 == 0) { return *((unsigned int *) buffer); } #endif const unsigned char *next = (bigEndian ? buffer : buffer + 3); unsigned int result = 0; for (int i = 0; i < 4; i++) { result <<= 8; result += *next; if (bigEndian) { next++; } else { next--; } } return result; } void PutUINT(unsigned int value, unsigned char *buffer, int bigEndian) { if (_hostBigEndian == bigEndian) { *((unsigned short *) buffer) = value; return; } if (bigEndian) { buffer[1] = (unsigned char) (value & 0xff); value >>= 8; *buffer = (unsigned char) value; } else { *buffer = (unsigned char) (value & 0xff); value >>= 8; buffer[1] = (unsigned char) value; } } void PutULONG(unsigned int value, unsigned char *buffer, int bigEndian) { if (_hostBigEndian == bigEndian) { *((unsigned int *) buffer) = value; return; } if (bigEndian) { buffer += 3; for (int i = 4; i > 0; i--) { *buffer-- = (unsigned char) (value & 0xff); value >>= 8; } } else { for (int i = 4; i > 0; i--) { *buffer++ = (unsigned char) (value & 0xff); value >>= 8; } } } int CheckData(istream *fs) { if (fs == NULL || fs -> fail()) { return -1; } return 1; } int CheckData(ostream *fs) { if (fs == NULL || fs -> fail()) { return -1; } return 1; } int PutData(ostream *fs, const unsigned char *buffer, int size) { fs -> write((char *) buffer, size); #ifdef DEBUG *logofs << "PutData: Written " << size << " bytes with eof " << fs -> eof() << " fail " << fs -> fail() << " and bad " << fs -> bad() << ".\n" << logofs_flush; #endif if (fs -> fail()) { return -1; } return size; } int GetData(istream *fs, unsigned char *buffer, int size) { fs -> read((char *) buffer, size); #ifdef DEBUG *logofs << "GetData: Read " << size << " bytes with eof " << fs -> eof() << " fail " << fs -> fail() << " and bad " << fs -> bad() << ".\n" << logofs_flush; #endif #ifdef __APPLE__ if (fs -> bad()) { return -1; } #else if (fs -> fail()) { return -1; } #endif return size; } int FlushData(ostream *fs) { fs -> flush(); if (fs -> fail()) { return -1; } return 1; } unsigned int RoundUp2(unsigned int x) { unsigned int y = x / 2; y *= 2; if (y != x) { y += 2; } return y; } unsigned int RoundUp4(unsigned int x) { unsigned int y = x / 4; y *= 4; if (y != x) { y += 4; } return y; } unsigned int RoundUp8(unsigned int x) { unsigned int y = x / 8; y *= 8; if (y != x) { y += 8; } return y; } const char *DumpSignal(int signal) { switch (signal) { case SIGCHLD: { return "SIGCHLD"; } case SIGUSR1: { return "SIGUSR1"; } case SIGUSR2: { return "SIGUSR2"; } case SIGHUP: { return "SIGHUP"; } case SIGINT: { return "SIGINT"; } case SIGTERM: { return "SIGTERM"; } case SIGPIPE: { return "SIGPIPE"; } case SIGALRM: { return "SIGALRM"; } case SIGVTALRM: { return "SIGVTALRM"; } case SIGWINCH: { return "SIGWINCH"; } case SIGIO: { return "SIGIO"; } case SIGTSTP: { return "SIGTSTP"; } case SIGTTIN: { return "SIGTTIN"; } case SIGTTOU: { return "SIGTTOU"; } case SIGSEGV: { return "SIGSEGV"; } case SIGABRT: { return "SIGABRT"; } default: { return "Unknown"; } } } const char *DumpPolicy(int type) { switch ((T_flush_policy) type) { case policy_immediate: { return "immediate"; } case policy_deferred: { return "deferred"; } default: { #ifdef PANIC *logofs << "Misc: PANIC! Unknown policy type '" << type << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Unknown policy type '" << type << "'.\n"; HandleCleanup(); } } } const char *DumpAction(int type) { T_store_action action = (T_store_action) type; if (action == IS_HIT) { return "is_hit"; } else if (action == IS_ADDED) { return "is_added"; } else if (action == is_discarded) { return "is_discarded"; } else if (action == is_removed) { return "is_removed"; } else { #ifdef PANIC *logofs << "Misc: PANIC! Unknown store action '" << type << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Unknown store action '" << type << "'.\n"; HandleCleanup(); } } const char *DumpState(int type) { switch ((T_split_state) type) { case split_added: { return "split_added"; } case split_missed: { return "split_missed"; } case split_loaded: { return "split_loaded"; } case split_aborted: { return "split_aborted"; } case split_notified: { return "split_notified"; } default: { #ifdef PANIC *logofs << "Misc: PANIC! Unknown split state '" << type << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Unknown split state '" << type << "'.\n"; HandleCleanup(); } } } const char *DumpControl(int code) { switch ((T_proxy_code) code) { case code_new_x_connection: { return "code_new_x_connection"; } case code_new_cups_connection: { return "code_new_cups_connection"; } case code_new_aux_connection: { return "code_new_aux_connection"; } case code_new_smb_connection: { return "code_new_smb_connection"; } case code_new_media_connection: { return "code_new_media_connection"; } case code_switch_connection: { return "code_switch_connection"; } case code_drop_connection: { return "code_drop_connection"; } case code_finish_connection: { return "code_finish_connection"; } case code_begin_congestion: { return "code_begin_congestion"; } case code_end_congestion: { return "code_end_congestion"; } case code_alert_request: { return "code_alert_request"; } case code_alert_reply: { return "code_alert_reply"; } case code_reset_request: { return "code_reset_request"; } case code_reset_reply: { return "code_reset_reply"; } case code_load_request: { return "code_load_request"; } case code_load_reply: { return "code_load_reply"; } case code_save_request: { return "code_save_request"; } case code_save_reply: { return "code_save_reply"; } case code_shutdown_request: { return "code_shutdown_request"; } case code_shutdown_reply: { return "code_shutdown_reply"; } case code_control_token_request: { return "code_control_token_request"; } case code_control_token_reply: { return "code_control_token_reply"; } case code_configuration_request: { return "code_configuration_request"; } case code_configuration_reply: { return "code_configuration_reply"; } case code_statistics_request: { return "code_statistics_request"; } case code_statistics_reply: { return "code_statistics_reply"; } case code_new_http_connection: { return "code_new_http_connection"; } case code_sync_request: { return "code_sync_request"; } case code_sync_reply: { return "code_sync_reply"; } case code_new_font_connection: { return "code_new_font_connection"; } case code_new_slave_connection: { return "code_new_slave_connection"; } case code_finish_listeners: { return "code_finish_listeners"; } case code_split_token_request: { return "code_split_token_request"; } case code_split_token_reply: { return "code_split_token_reply"; } case code_data_token_request: { return "code_data_token_request"; } case code_data_token_reply: { return "code_data_token_reply"; } default: { #ifdef WARNING *logofs << "Misc: WARNING! Unknown control code '" << code << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Unknown control code '" << code << "'.\n"; return "unknown"; } } } const char *DumpSession(int code) { switch ((T_session_mode) code) { case session_agent: { return "session_agent"; } case session_shadow: { return "session_shadow"; } case session_proxy: { return "session_proxy"; } default: { #ifdef WARNING *logofs << "Misc: WARNING! Unknown session type '" << code << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Unknown session type '" << code << "'.\n"; return "unknown"; } } } const char *DumpToken(int type) { switch ((T_token_type) type) { case token_control: { return "token_control"; } case token_split: { return "token_split"; } case token_data: { return "token_data"; } default: { #ifdef WARNING *logofs << "Misc: WARNING! Unknown token type '" << type << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Unknown token type '" << type << "'.\n"; return "unknown"; } } } // // Always include this in code as it is generally // needed to test channels and split store. // const char *DumpChecksum(const void *checksum) { static char string[MD5_LENGTH * 2 + 1]; if (checksum != NULL) { for (unsigned int i = 0; i < MD5_LENGTH; i++) { sprintf(string + (i * 2), "%02X", ((unsigned char *) checksum)[i]); } } else { strcpy(string, "null"); } return string; } // // Define OPCODES here and in the channel // if you want to log the opcode literal. // #ifdef OPCODES const char *DumpOpcode(const int &opcode) { switch (opcode) { case X_CreateWindow: { return "X_CreateWindow"; } case X_ChangeWindowAttributes: { return "X_ChangeWindowAttributes"; } case X_GetWindowAttributes: { return "X_GetWindowAttributes"; } case X_DestroyWindow: { return "X_DestroyWindow"; } case X_DestroySubwindows: { return "X_DestroySubwindows"; } case X_ChangeSaveSet: { return "X_ChangeSaveSet"; } case X_ReparentWindow: { return "X_ReparentWindow"; } case X_MapWindow: { return "X_MapWindow"; } case X_MapSubwindows: { return "X_MapSubwindows"; } case X_UnmapWindow: { return "X_UnmapWindow"; } case X_UnmapSubwindows: { return "X_UnmapSubwindows"; } case X_ConfigureWindow: { return "X_ConfigureWindow"; } case X_CirculateWindow: { return "X_CirculateWindow"; } case X_GetGeometry: { return "X_GetGeometry"; } case X_QueryTree: { return "X_QueryTree"; } case X_InternAtom: { return "X_InternAtom"; } case X_GetAtomName: { return "X_GetAtomName"; } case X_ChangeProperty: { return "X_ChangeProperty"; } case X_DeleteProperty: { return "X_DeleteProperty"; } case X_GetProperty: { return "X_GetProperty"; } case X_ListProperties: { return "X_ListProperties"; } case X_SetSelectionOwner: { return "X_SetSelectionOwner"; } case X_GetSelectionOwner: { return "X_GetSelectionOwner"; } case X_ConvertSelection: { return "X_ConvertSelection"; } case X_SendEvent: { return "X_SendEvent"; } case X_GrabPointer: { return "X_GrabPointer"; } case X_UngrabPointer: { return "X_UngrabPointer"; } case X_GrabButton: { return "X_GrabButton"; } case X_UngrabButton: { return "X_UngrabButton"; } case X_ChangeActivePointerGrab: { return "X_ChangeActivePointerGrab"; } case X_GrabKeyboard: { return "X_GrabKeyboard"; } case X_UngrabKeyboard: { return "X_UngrabKeyboard"; } case X_GrabKey: { return "X_GrabKey"; } case X_UngrabKey: { return "X_UngrabKey"; } case X_AllowEvents: { return "X_AllowEvents"; } case X_GrabServer: { return "X_GrabServer"; } case X_UngrabServer: { return "X_UngrabServer"; } case X_QueryPointer: { return "X_QueryPointer"; } case X_GetMotionEvents: { return "X_GetMotionEvents"; } case X_TranslateCoords: { return "X_TranslateCoords"; } case X_WarpPointer: { return "X_WarpPointer"; } case X_SetInputFocus: { return "X_SetInputFocus"; } case X_GetInputFocus: { return "X_GetInputFocus"; } case X_QueryKeymap: { return "X_QueryKeymap"; } case X_OpenFont: { return "X_OpenFont"; } case X_CloseFont: { return "X_CloseFont"; } case X_QueryFont: { return "X_QueryFont"; } case X_QueryTextExtents: { return "X_QueryTextExtents"; } case X_ListFonts: { return "X_ListFonts"; } case X_ListFontsWithInfo: { return "X_ListFontsWithInfo"; } case X_SetFontPath: { return "X_SetFontPath"; } case X_GetFontPath: { return "X_GetFontPath"; } case X_CreatePixmap: { return "X_CreatePixmap"; } case X_FreePixmap: { return "X_FreePixmap"; } case X_CreateGC: { return "X_CreateGC"; } case X_ChangeGC: { return "X_ChangeGC"; } case X_CopyGC: { return "X_CopyGC"; } case X_SetDashes: { return "X_SetDashes"; } case X_SetClipRectangles: { return "X_SetClipRectangles"; } case X_FreeGC: { return "X_FreeGC"; } case X_ClearArea: { return "X_ClearArea"; } case X_CopyArea: { return "X_CopyArea"; } case X_CopyPlane: { return "X_CopyPlane"; } case X_PolyPoint: { return "X_PolyPoint"; } case X_PolyLine: { return "X_PolyLine"; } case X_PolySegment: { return "X_PolySegment"; } case X_PolyRectangle: { return "X_PolyRectangle"; } case X_PolyArc: { return "X_PolyArc"; } case X_FillPoly: { return "X_FillPoly"; } case X_PolyFillRectangle: { return "X_PolyFillRectangle"; } case X_PolyFillArc: { return "X_PolyFillArc"; } case X_PutImage: { return "X_PutImage"; } case X_GetImage: { return "X_GetImage"; } case X_PolyText8: { return "X_PolyText8"; } case X_PolyText16: { return "X_PolyText16"; } case X_ImageText8: { return "X_ImageText8"; } case X_ImageText16: { return "X_ImageText16"; } case X_CreateColormap: { return "X_CreateColormap"; } case X_FreeColormap: { return "X_FreeColormap"; } case X_CopyColormapAndFree: { return "X_CopyColormapAndFree"; } case X_InstallColormap: { return "X_InstallColormap"; } case X_UninstallColormap: { return "X_UninstallColormap"; } case X_ListInstalledColormaps: { return "X_ListInstalledColormaps"; } case X_AllocColor: { return "X_AllocColor"; } case X_AllocNamedColor: { return "X_AllocNamedColor"; } case X_AllocColorCells: { return "X_AllocColorCells"; } case X_AllocColorPlanes: { return "X_AllocColorPlanes"; } case X_FreeColors: { return "X_FreeColors"; } case X_StoreColors: { return "X_StoreColors"; } case X_StoreNamedColor: { return "X_StoreNamedColor"; } case X_QueryColors: { return "X_QueryColors"; } case X_LookupColor: { return "X_LookupColor"; } case X_CreateCursor: { return "X_CreateCursor"; } case X_CreateGlyphCursor: { return "X_CreateGlyphCursor"; } case X_FreeCursor: { return "X_FreeCursor"; } case X_RecolorCursor: { return "X_RecolorCursor"; } case X_QueryBestSize: { return "X_QueryBestSize"; } case X_QueryExtension: { return "X_QueryExtension"; } case X_ListExtensions: { return "X_ListExtensions"; } case X_ChangeKeyboardMapping: { return "X_ChangeKeyboardMapping"; } case X_GetKeyboardMapping: { return "X_GetKeyboardMapping"; } case X_ChangeKeyboardControl: { return "X_ChangeKeyboardControl"; } case X_GetKeyboardControl: { return "X_GetKeyboardControl"; } case X_Bell: { return "X_Bell"; } case X_ChangePointerControl: { return "X_ChangePointerControl"; } case X_GetPointerControl: { return "X_GetPointerControl"; } case X_SetScreenSaver: { return "X_SetScreenSaver"; } case X_GetScreenSaver: { return "X_GetScreenSaver"; } case X_ChangeHosts: { return "X_ChangeHosts"; } case X_ListHosts: { return "X_ListHosts"; } case X_SetAccessControl: { return "X_SetAccessControl"; } case X_SetCloseDownMode: { return "X_SetCloseDownMode"; } case X_KillClient: { return "X_KillClient"; } case X_RotateProperties: { return "X_RotateProperties"; } case X_ForceScreenSaver: { return "X_ForceScreenSaver"; } case X_SetPointerMapping: { return "X_SetPointerMapping"; } case X_GetPointerMapping: { return "X_GetPointerMapping"; } case X_SetModifierMapping: { return "X_SetModifierMapping"; } case X_GetModifierMapping: { return "X_GetModifierMapping"; } case X_NoOperation: { return "X_NoOperation"; } case X_NXInternalGenericData: { return "X_NXInternalGenericData"; } // // case X_NXInternalGenericReply: // { // return "X_NXInternalGenericReply"; // } // case X_NXInternalGenericRequest: { return "X_NXInternalGenericRequest"; } case X_NXInternalShapeExtension: { return "X_NXInternalShapeExtension"; } case X_NXGetControlParameters: { return "X_NXGetControlParameters"; } case X_NXGetCleanupParameters: { return "X_NXGetCleanupParameters"; } case X_NXGetImageParameters: { return "X_NXGetImageParameters"; } case X_NXGetUnpackParameters: { return "X_NXGetUnpackParameters"; } case X_NXGetShmemParameters: { return "X_NXGetShmemParameters"; } case X_NXGetFontParameters: { return "X_NXGetFontParameters"; } case X_NXSetExposeParameters: { return "X_NXSetExposeParameters"; } case X_NXSetCacheParameters: { return "X_NXSetCacheParameters"; } case X_NXStartSplit: { return "X_NXStartSplit"; } case X_NXEndSplit: { return "X_NXEndSplit"; } case X_NXSplitData: { return "X_NXSplitData"; } case X_NXSplitEvent: { return "X_NXSplitEvent"; } case X_NXCommitSplit: { return "X_NXCommitSplit"; } case X_NXFinishSplit: { return "X_NXFinishSplit"; } case X_NXAbortSplit: { return "X_NXAbortSplit"; } case X_NXFreeSplit: { return "X_NXFreeSplit"; } case X_NXSetUnpackGeometry: { return "X_NXSetUnpackGeometry"; } case X_NXSetUnpackColormap: { return "X_NXSetUnpackColormap"; } case X_NXSetUnpackAlpha: { return "X_NXSetUnpackAlpha"; } case X_NXPutPackedImage: { return "X_NXPutPackedImage"; } case X_NXFreeUnpack: { return "X_NXFreeUnpack"; } default: { if (opcode > 127) { return "Extension"; } else { return "?"; } } } } #else /* #ifdef OPCODES */ const char *DumpOpcode(const int &opcode) { return "?"; } #endif /* #ifdef OPCODES */ void DumpData(const unsigned char *buffer, unsigned int size) { if (buffer != NULL) { unsigned int i = 0; while (i < size) { *logofs << "[" << i << "]\t"; for (unsigned int ii = 0; i < size && ii < 8; i++, ii++) { *logofs << (unsigned int) (buffer[i]) << "\t"; } *logofs << "\n" << logofs_flush; } } } void DumpChecksum(const unsigned char *buffer, unsigned int size) { if (buffer != NULL) { md5_byte_t md5_digest[MD5_LENGTH]; md5_state_t md5_state; md5_init(&md5_state); md5_append(&md5_state, buffer, size); md5_finish(&md5_state, md5_digest); char md5_string[MD5_LENGTH * 2 + 1]; for (unsigned int i = 0; i < MD5_LENGTH; i++) { sprintf(md5_string + (i * 2), "%02X", md5_digest[i]); } *logofs << "[" << md5_string << "]" << logofs_flush; } } void DumpBlockChecksums(const unsigned char *buffer, unsigned int size, unsigned int block) { for (unsigned int i = 0; i < (size / block); i++) { *logofs << "[" << i * block << "]"; DumpChecksum(buffer + (i * block), block); *logofs << "\n"; } if (size % block > 0) { *logofs << "[" << size / block * block << "]"; DumpChecksum(buffer + (size / block * block), size % block); *logofs << "\n"; } } void DumpHexData(const unsigned char *buffer, unsigned int size) { char message [65536]; char ascii [17]; unsigned int index = 0; unsigned int linescan = 0; unsigned int index_ascii = 0; sprintf (message,"\n#### Start Dump Buffer of [%.5d] Bytes ####\n\n",size); *logofs << message << logofs_flush; // // "Index 0 1 2 3 4 5 6 7 8 9 a b c d e f Ascii " // "----- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----------------" // "00000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................" // sprintf (message,"Index 0 1 2 3 4 5 6 7 8 9 a b c d e f Ascii \n"); *logofs << message << logofs_flush; sprintf (message,"----- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----------------\n"); *logofs << message << logofs_flush; index = 0; while (index < size) { memset (ascii, ' ', sizeof(ascii)); ascii[16] = '\0'; sprintf (message,"%.5d ", index); for (index_ascii = 0, linescan = index; ((index < (linescan + 16)) && (index < size)); index++, index_ascii++) { if (isprint(buffer [index])) { ascii[index_ascii] = buffer [index]; } else { ascii[index_ascii] = '.'; } sprintf (&message [strlen (message)],"%.2x ", (unsigned char) buffer [index]); } for (linescan = index_ascii; linescan < 16; linescan++) { strcat (&message [strlen (message)], " "); } sprintf (&message [strlen (message)]," %s\n", ascii); *logofs << message << logofs_flush; } sprintf (message,"\n#### End Dump Buffer ####\n\n"); *logofs << message << logofs_flush; } nxcomp/ShapeExtension.cpp0000644000076400007640000002121611323113027015766 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ShapeExtension.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // ShapeExtensionStore::ShapeExtensionStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = SHAPEEXTENSION_ENABLE_CACHE; enableData = SHAPEEXTENSION_ENABLE_DATA; enableSplit = SHAPEEXTENSION_ENABLE_SPLIT; enableCompress = SHAPEEXTENSION_ENABLE_COMPRESS; if (control -> isProtoStep7() == 1) { enableCompress = SHAPEEXTENSION_ENABLE_COMPRESS_IF_PROTO_STEP_7; } dataLimit = SHAPEEXTENSION_DATA_LIMIT; dataOffset = SHAPEEXTENSION_DATA_OFFSET; cacheSlots = SHAPEEXTENSION_CACHE_SLOTS; cacheThreshold = SHAPEEXTENSION_CACHE_THRESHOLD; cacheLowerThreshold = SHAPEEXTENSION_CACHE_LOWER_THRESHOLD; opcode_ = X_NXInternalShapeExtension; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } ShapeExtensionStore::~ShapeExtensionStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int ShapeExtensionStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { // // Handle this extension in a way similar to shape. // ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif // // We handle all possible requests of this extension // using the same opcode. We give to message a data // offset of 4 (or 16 if proto is >= 3) and handle // the first 16 bytes through an array of caches. // encodeBuffer.encodeValue(size >> 2, 16, 10); encodeBuffer.encodeCachedValue(*(buffer + 1), 8, clientCache -> shapeOpcodeCache); for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) { encodeBuffer.encodeCachedValue(GetUINT(buffer + (i * 2) + 4, bigEndian), 16, *clientCache -> shapeDataCache[i]); } #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int ShapeExtensionStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif decodeBuffer.decodeValue(size, 16, 10); size <<= 2; buffer = writeBuffer -> addMessage(size); decodeBuffer.decodeCachedValue(*(buffer + 1), 8, clientCache -> shapeOpcodeCache); unsigned int value; for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) { decodeBuffer.decodeCachedValue(value, 16, *clientCache -> shapeDataCache[i]); PutUINT(value, buffer + 4 + (i * 2), bigEndian); } #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int ShapeExtensionStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message; shapeExtension -> opcode = *(buffer + 1); for (unsigned int i = 0; i < 8; i++) { if ((i * 2 + 4) < size) { shapeExtension -> data[i] = GetUINT(buffer + i * 2 + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed data[" << i << "].\n" << logofs_flush; #endif } else { shapeExtension -> data[i] = 0; } } #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int ShapeExtensionStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message; *(buffer + 1) = shapeExtension -> opcode; for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) { PutUINT(shapeExtension -> data[i], buffer + i * 2 + 4, bigEndian); } #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void ShapeExtensionStore::dumpIdentity(const Message *message) const { #ifdef DUMP ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message; *logofs << name() << ": Identity opcode " << (unsigned) shapeExtension -> opcode; for (int i = 0; i < 8; i++) { *logofs << ", data[" << i << "] " << shapeExtension -> data[i]; } *logofs << ", size " << shapeExtension -> size_ << ".\n" << logofs_flush; #endif } void ShapeExtensionStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // Include minor opcode in the checksum. As data // offset can be beyond the real end of message, // we need to include size or we will match any // message of size less or equal to data offset. // md5_append(md5_state_, buffer + 1, 3); } void ShapeExtensionStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { // // Encode the variant part. // ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message; ShapeExtensionMessage *cachedShapeExtension = (ShapeExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; for (int i = 0; i < 8 && (i * 2 + 4) < shapeExtension -> size_; i++) { #ifdef TEST *logofs << name() << ": Encoding value " << shapeExtension -> data[i] << " as data[" << i << "] field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue((unsigned int) shapeExtension -> data[i], 16, *clientCache -> shapeDataCache[i]); cachedShapeExtension -> data[i] = shapeExtension -> data[i]; } } void ShapeExtensionStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; for (int i = 0; i < 8 && (i * 2 + 4) < shapeExtension -> size_; i++) { decodeBuffer.decodeCachedValue(value, 16, *clientCache -> shapeDataCache[i]); shapeExtension -> data[i] = (unsigned short) value; #ifdef TEST *logofs << name() << ": Decoded value " << shapeExtension -> data[i] << " as data[" << i << "] field.\n" << logofs_flush; #endif } } nxcomp/RenderCompositeGlyphsCompat.h0000644000076400007640000000476711323113026020146 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderCompositeGlyphsCompat_H #define RenderCompositeGlyphsCompat_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderCompositeGlyphsCompat" #undef MESSAGE_STORE #define MESSAGE_STORE RenderCompositeGlyphsCompatStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 36 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size); } #include MESSAGE_METHODS }; #endif /* RenderCompositeGlyphsCompat_H */ nxcomp/Alpha.cpp0000644000076400007640000000731511342775572014106 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Misc.h" #include "Unpack.h" #include "Alpha.h" #define PANIC #define WARNING #undef TEST #undef DEBUG int UnpackAlpha(unsigned char method, unsigned char *src_data, int src_size, unsigned char *dst_data, int dst_size) { if (*src_data == 0) { if (dst_size != src_size - 1) { #ifdef TEST *logofs << "UnpackAlpha: PANIC! Invalid destination size " << dst_size << " with source " << src_size << ".\n" << logofs_flush; #endif return -1; } #ifdef TEST *logofs << "UnpackAlpha: Expanding " << src_size - 1 << " bytes of plain alpha data.\n" << logofs_flush; #endif memcpy(dst_data, src_data + 1, src_size - 1); return 1; } unsigned int check_size = dst_size; int result = ZDecompress(&unpackStream, dst_data, &check_size, src_data + 1, src_size - 1); if (result != Z_OK) { #ifdef PANIC *logofs << "UnpackAlpha: PANIC! Failure decompressing alpha data. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failure decompressing alpha data. " << "Error is '" << zError(result) << "'.\n"; return -1; } else if (check_size != (unsigned int) dst_size) { #ifdef PANIC *logofs << "UnpackAlpha: PANIC! Size mismatch in alpha data. " << "Resulting size is " << check_size << " with " << "expected size " << dst_size << ".\n" << logofs_flush; #endif cerr << "Error" << ": Size mismatch in alpha data. " << "Resulting size is " << check_size << " with " << "expected size " << dst_size << ".\n"; return -1; } #ifdef TEST *logofs << "UnpackAlpha: Decompressed " << src_size - 1 << " bytes to " << dst_size << " bytes of alpha data.\n" << logofs_flush; #endif return 1; } int UnpackAlpha(T_alpha *alpha, unsigned char *dst_data, int dst_size, int big_endian) { unsigned int count = dst_size >> 2; unsigned int i; int shift; if (count != alpha -> entries) { #ifdef WARNING *logofs << "UnpackAlpha: WARNING! Not applying the alpha with " << count << " elements needed and " << alpha -> entries << " available.\n" << logofs_flush; #endif return 0; } shift = (big_endian == 1 ? 0 : 3); for (i = 0; i < count; i++) { *(dst_data + shift) = *(alpha -> data + i); dst_data += 4; } return 1; } nxcomp/PositionCacheCompat.h0000644000076400007640000000306111323113030016362 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PositionCacheCompat_H #define PositionCacheCompat_H #include "IntCache.h" class PositionCacheCompat { friend class EncodeBuffer; friend class DecodeBuffer; public: PositionCacheCompat(); ~PositionCacheCompat(); private: IntCache *base_[32]; unsigned int slot_; short int last_; }; #endif /* PositionCacheCompat_H */ nxcomp/Statistics.h0000644000076400007640000004524511323113031014633 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Statistics_H #define Statistics_H #include "NXproto.h" #include "Misc.h" #include "Timestamp.h" class Proxy; // // Opcode 255 is for generic requests, 1 is for // generic replies (those which haven't a speci- // fic differential encoding), opcode 0 is for // generic messages from the auxiliary channels. // #define STATISTICS_OPCODE_MAX 256 // // Maximum length of the buffer allocated for // the statistics output. // #define STATISTICS_LENGTH 16384 // // Query type. // #define TOTAL_STATS 1 #define PARTIAL_STATS 2 #define NO_STATS 3 // // Log level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Log the operations related to updating // the control tokens. // #undef TOKEN class Statistics { public: // // Use the proxy class to get access // to the message stores. // Statistics(Proxy *proxy); ~Statistics(); void addIdleTime(unsigned int numMs) { transportPartial_.idleTime_ += numMs; transportTotal_.idleTime_ += numMs; } void subIdleTime(unsigned int numMs) { transportPartial_.idleTime_ -= numMs; transportTotal_.idleTime_ -= numMs; } void addReadTime(unsigned int numMs) { transportPartial_.readTime_ += numMs; transportTotal_.readTime_ += numMs; } void subReadTime(unsigned int numMs) { transportPartial_.readTime_ -= numMs; transportTotal_.readTime_ -= numMs; } void addWriteTime(unsigned int numMs) { transportPartial_.writeTime_ += numMs; transportTotal_.writeTime_ += numMs; } void subWriteTime(unsigned int numMs) { transportPartial_.writeTime_ -= numMs; transportTotal_.writeTime_ -= numMs; } void addBytesIn(unsigned int numBytes) { transportPartial_.proxyBytesIn_ += numBytes; transportTotal_.proxyBytesIn_ += numBytes; } double getBytesIn() { return transportTotal_.proxyBytesIn_; } void addBytesOut(unsigned int numBytes) { transportPartial_.proxyBytesOut_ += numBytes; transportTotal_.proxyBytesOut_ += numBytes; } double getBytesOut() { return transportTotal_.proxyBytesOut_; } void addFrameIn() { transportPartial_.proxyFramesIn_++; transportTotal_.proxyFramesIn_++; #ifdef TEST *logofs << "Statistics: Updated total proxy frames in to " << transportTotal_.proxyFramesIn_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif } void addFrameOut() { transportPartial_.proxyFramesOut_++; transportTotal_.proxyFramesOut_++; #ifdef TEST *logofs << "Statistics: Updated total proxy frames out to " << transportTotal_.proxyFramesOut_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif } void addWriteOut() { transportPartial_.proxyWritesOut_++; transportTotal_.proxyWritesOut_++; #ifdef TEST *logofs << "Statistics: Updated total proxy writes out to " << transportTotal_.proxyWritesOut_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif } void addCompressedBytes(unsigned int bytesIn, unsigned int bytesOut); void addDecompressedBytes(unsigned int bytesIn, unsigned int bytesOut) { transportPartial_.decompressedBytesIn_ += bytesIn; transportTotal_.decompressedBytesIn_ += bytesIn; transportPartial_.decompressedBytesOut_ += bytesOut; transportTotal_.decompressedBytesOut_ += bytesOut; } void addFramingBits(unsigned int bitsOut) { transportPartial_.framingBitsOut_ += bitsOut; transportTotal_.framingBitsOut_ += bitsOut; proxyData_.protocolCount_ += bitsOut; } void addCachedRequest(unsigned int opcode) { protocolPartial_.requestCached_[opcode]++; protocolTotal_.requestCached_[opcode]++; } void addRenderCachedRequest(unsigned int opcode) { protocolPartial_.renderRequestCached_[opcode]++; protocolTotal_.renderRequestCached_[opcode]++; } void addRepliedRequest(unsigned int opcode) { protocolPartial_.requestReplied_[opcode]++; protocolTotal_.requestReplied_[opcode]++; } void addCachedReply(unsigned int opcode) { protocolPartial_.replyCached_[opcode]++; protocolTotal_.replyCached_[opcode]++; } void addRequestBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut) { #ifdef DEBUG *logofs << "Statistcs: Added " << bitsIn << " bits in and " << bitsOut << " bits out to opcode " << opcode << ".\n" << logofs_flush; #endif protocolPartial_.requestCount_[opcode]++; protocolTotal_.requestCount_[opcode]++; protocolPartial_.requestBitsIn_[opcode] += bitsIn; protocolTotal_.requestBitsIn_[opcode] += bitsIn; protocolPartial_.requestBitsOut_[opcode] += bitsOut; protocolTotal_.requestBitsOut_[opcode] += bitsOut; // // Don't account the split bits // to the control token. // if (opcode != X_NXSplitData && opcode != X_NXSplitEvent) { proxyData_.protocolCount_ += bitsOut; } } void addRenderRequestBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut) { #ifdef DEBUG *logofs << "Statistcs: Added " << bitsIn << " bits in and " << bitsOut << " bits out to render opcode " << opcode << ".\n" << logofs_flush; #endif protocolPartial_.renderRequestCount_[opcode]++; protocolTotal_.renderRequestCount_[opcode]++; protocolPartial_.renderRequestBitsIn_[opcode] += bitsIn; protocolTotal_.renderRequestBitsIn_[opcode] += bitsIn; protocolPartial_.renderRequestBitsOut_[opcode] += bitsOut; protocolTotal_.renderRequestBitsOut_[opcode] += bitsOut; } void addReplyBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut) { protocolPartial_.replyCount_[opcode]++; protocolTotal_.replyCount_[opcode]++; protocolPartial_.replyBitsIn_[opcode] += bitsIn; protocolTotal_.replyBitsIn_[opcode] += bitsIn; protocolPartial_.replyBitsOut_[opcode] += bitsOut; protocolTotal_.replyBitsOut_[opcode] += bitsOut; proxyData_.protocolCount_ += bitsOut; } void addEventBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut) { protocolPartial_.eventCount_[opcode]++; protocolTotal_.eventCount_[opcode]++; protocolPartial_.eventBitsIn_[opcode] += bitsIn; protocolTotal_.eventBitsIn_[opcode] += bitsIn; protocolPartial_.eventBitsOut_[opcode] += bitsOut; protocolTotal_.eventBitsOut_[opcode] += bitsOut; proxyData_.protocolCount_ += bitsOut; } void addCupsBits(unsigned int bitsIn, unsigned int bitsOut) { protocolPartial_.cupsCount_++; protocolTotal_.cupsCount_++; protocolPartial_.cupsBitsIn_ += bitsIn; protocolTotal_.cupsBitsIn_ += bitsIn; protocolPartial_.cupsBitsOut_ += bitsOut; protocolTotal_.cupsBitsOut_ += bitsOut; } void addSmbBits(unsigned int bitsIn, unsigned int bitsOut) { protocolPartial_.smbCount_++; protocolTotal_.smbCount_++; protocolPartial_.smbBitsIn_ += bitsIn; protocolTotal_.smbBitsIn_ += bitsIn; protocolPartial_.smbBitsOut_ += bitsOut; protocolTotal_.smbBitsOut_ += bitsOut; } void addMediaBits(unsigned int bitsIn, unsigned int bitsOut) { protocolPartial_.mediaCount_++; protocolTotal_.mediaCount_++; protocolPartial_.mediaBitsIn_ += bitsIn; protocolTotal_.mediaBitsIn_ += bitsIn; protocolPartial_.mediaBitsOut_ += bitsOut; protocolTotal_.mediaBitsOut_ += bitsOut; } void addHttpBits(unsigned int bitsIn, unsigned int bitsOut) { protocolPartial_.httpCount_++; protocolTotal_.httpCount_++; protocolPartial_.httpBitsIn_ += bitsIn; protocolTotal_.httpBitsIn_ += bitsIn; protocolPartial_.httpBitsOut_ += bitsOut; protocolTotal_.httpBitsOut_ += bitsOut; } void addFontBits(unsigned int bitsIn, unsigned int bitsOut) { protocolPartial_.fontCount_++; protocolTotal_.fontCount_++; protocolPartial_.fontBitsIn_ += bitsIn; protocolTotal_.fontBitsIn_ += bitsIn; protocolPartial_.fontBitsOut_ += bitsOut; protocolTotal_.fontBitsOut_ += bitsOut; } void addSlaveBits(unsigned int bitsIn, unsigned int bitsOut) { protocolPartial_.slaveCount_++; protocolTotal_.slaveCount_++; protocolPartial_.slaveBitsIn_ += bitsIn; protocolTotal_.slaveBitsIn_ += bitsIn; protocolPartial_.slaveBitsOut_ += bitsOut; protocolTotal_.slaveBitsOut_ += bitsOut; } void addPackedBytesIn(unsigned int numBytes) { packedPartial_.packedBytesIn_ += numBytes; packedTotal_.packedBytesIn_ += numBytes; } void addPackedBytesOut(unsigned int numBytes) { packedPartial_.packedBytesOut_ += numBytes; packedTotal_.packedBytesOut_ += numBytes; } void addSplit() { splitPartial_.splitCount_++; splitTotal_.splitCount_++; } void addSplitAborted() { splitPartial_.splitAborted_++; splitTotal_.splitAborted_++; } void addSplitAbortedBytesOut(unsigned int numBytes) { splitPartial_.splitAbortedBytesOut_ += numBytes; splitTotal_.splitAbortedBytesOut_ += numBytes; } // // Add the recorded protocol data to the proxy // token counters. We want to send bpth the token // request message and the data payload using a // single system write, so we must guess how many // output bytes we will generate. // void updateControlToken(int &count) { // // Total is the total number of protocol bytes // generated so far. We have saved the number // of bytes generated the last time the function // was called so we can calculate the difference. // // The number of protocol bits is updated as soon // as new bits are accumulated, to avoid summing // up all the opcodes in this routine. We add a // byte to the control bytes difference to account // for the framing bits that are very likely to // be added to the payload. // double total = proxyData_.protocolCount_ / 8; double diff = total - proxyData_.controlCount_ + 1; #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! Protocol bytes are " << total << " control bytes are " << proxyData_.controlCount_ << " difference is " << diff << ".\n" << logofs_flush; #endif count += (int) (diff / proxyData_.streamRatio_); #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! Adding " << (int) (diff / proxyData_.streamRatio_) << " bytes to the control token with ratio " << proxyData_.streamRatio_ << ".\n" << logofs_flush; #endif proxyData_.controlCount_ = total; #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! New control token has " << count << " bytes with total control bytes " << proxyData_.controlCount_ << ".\n" << logofs_flush; #endif } void updateSplitToken(int &count) { double total = (protocolTotal_.requestBitsOut_[X_NXSplitData] + protocolTotal_.eventBitsOut_[X_NXSplitEvent]) / 8; double diff = total - proxyData_.splitCount_; #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! Protocol bytes are " << total << " split bytes are " << proxyData_.splitCount_ << " difference is " << diff << ".\n" << logofs_flush; #endif count += (int) (diff / proxyData_.streamRatio_); #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! Adding " << (int) (diff / proxyData_.streamRatio_) << " bytes to the split token with ratio " << proxyData_.streamRatio_ << ".\n" << logofs_flush; #endif proxyData_.splitCount_ = total; #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! New split token has " << count << " bytes with total split bytes " << proxyData_.splitCount_ << ".\n" << logofs_flush; #endif } void updateDataToken(int &count) { double total = (protocolTotal_.cupsBitsOut_ + protocolTotal_.smbBitsOut_ + protocolTotal_.mediaBitsOut_ + protocolTotal_.httpBitsOut_ + protocolTotal_.fontBitsOut_ + protocolTotal_.slaveBitsOut_) / 8; double diff = total - proxyData_.dataCount_; #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! Protocol bytes are " << total << " data bytes are " << proxyData_.dataCount_ << " difference is " << diff << ".\n" << logofs_flush; #endif count += (int) (diff / proxyData_.streamRatio_); #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! Adding " << (int) (diff / proxyData_.streamRatio_) << " bytes to the data token with ratio " << proxyData_.streamRatio_ << ".\n" << logofs_flush; #endif proxyData_.dataCount_ = total; #if defined(TEST) || defined(TOKEN) *logofs << "Statistics: TOKEN! New data token has " << count << " bytes with total data bytes " << proxyData_.dataCount_ << ".\n" << logofs_flush; #endif } // // Update the current bitrate. // void updateBitrate(int bytes); // // Return the current bitrate. // int getBitrateInShortFrame() { return bitrateInShortFrame_; } int getBitrateInLongFrame() { return bitrateInLongFrame_; } int getTopBitrate() { return topBitrate_; } void resetTopBitrate() { topBitrate_ = 0; } double getStreamRatio() { return proxyData_.streamRatio_; } // // Manage the congestion level. // void updateCongestion(int remaining, int limit); double getCongestionInFrame() { return congestionInFrame_; } // // Produce a dump of the statistics on // the provided buffer. // int getClientCacheStats(int type, char *&buffer); int getClientProtocolStats(int type, char *&buffer); int getClientOverallStats(int type, char *&buffer); int getServerCacheStats(int type, char *&buffer); int getServerProtocolStats(int type, char *&buffer); int getServerOverallStats(int type, char *&buffer); int resetPartialStats(); private: int getTimeStats(int type, char *&buffer); int getServicesStats(int type, char *&buffer); int getFramingStats(int type, char *&buffer); int getBitrateStats(int type, char *&buffer); int getStreamStats(int type, char *&buffer); int getSplitStats(int type, char *&buffer); struct T_protocolData { double requestCached_[STATISTICS_OPCODE_MAX]; double requestReplied_[STATISTICS_OPCODE_MAX]; double requestCount_[STATISTICS_OPCODE_MAX]; double requestBitsIn_[STATISTICS_OPCODE_MAX]; double requestBitsOut_[STATISTICS_OPCODE_MAX]; double renderRequestCached_[STATISTICS_OPCODE_MAX]; double renderRequestCount_[STATISTICS_OPCODE_MAX]; double renderRequestBitsIn_[STATISTICS_OPCODE_MAX]; double renderRequestBitsOut_[STATISTICS_OPCODE_MAX]; double replyCached_[STATISTICS_OPCODE_MAX]; double replyCount_[STATISTICS_OPCODE_MAX]; double replyBitsIn_[STATISTICS_OPCODE_MAX]; double replyBitsOut_[STATISTICS_OPCODE_MAX]; double eventCached_[STATISTICS_OPCODE_MAX]; double eventCount_[STATISTICS_OPCODE_MAX]; double eventBitsIn_[STATISTICS_OPCODE_MAX]; double eventBitsOut_[STATISTICS_OPCODE_MAX]; double cupsCount_; double cupsBitsIn_; double cupsBitsOut_; double smbCount_; double smbBitsIn_; double smbBitsOut_; double mediaCount_; double mediaBitsIn_; double mediaBitsOut_; double httpCount_; double httpBitsIn_; double httpBitsOut_; double fontCount_; double fontBitsIn_; double fontBitsOut_; double slaveCount_; double slaveBitsIn_; double slaveBitsOut_; }; struct T_transportData { double idleTime_; double readTime_; double writeTime_; double proxyBytesIn_; double proxyBytesOut_; double proxyFramesIn_; double proxyFramesOut_; double proxyWritesOut_; double compressedBytesIn_; double compressedBytesOut_; double decompressedBytesIn_; double decompressedBytesOut_; double framingBitsOut_; }; struct T_packedData { double packedBytesIn_; double packedBytesOut_; }; struct T_splitData { double splitCount_; double splitAborted_; double splitAbortedBytesOut_; }; struct T_overallData { double overallBytesIn_; double overallBytesOut_; }; struct T_proxyData { double protocolCount_; double controlCount_; double splitCount_; double dataCount_; double streamRatio_; }; T_protocolData protocolPartial_; T_protocolData protocolTotal_; T_transportData transportPartial_; T_transportData transportTotal_; T_packedData packedPartial_; T_packedData packedTotal_; T_splitData splitPartial_; T_splitData splitTotal_; T_overallData overallPartial_; T_overallData overallTotal_; T_proxyData proxyData_; // // Used to calculate the bandwidth usage // of the proxy link. // T_timestamp startShortFrameTs_; T_timestamp startLongFrameTs_; T_timestamp startFrameTs_; int bytesInShortFrame_; int bytesInLongFrame_; int bitrateInShortFrame_; int bitrateInLongFrame_; int topBitrate_; double congestionInFrame_; // // Need the proxy pointer to print the // statistics related to the client and // server stores and to add the protocol // data to the proxy token accumulators. // Proxy *proxy_; }; #endif /* Statistics_H */ nxcomp/ClearArea.h0000644000076400007640000001043711323113030014312 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ClearArea_H #define ClearArea_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define CLEARAREA_ENABLE_CACHE 1 #define CLEARAREA_ENABLE_DATA 0 #define CLEARAREA_ENABLE_SPLIT 0 #define CLEARAREA_ENABLE_COMPRESS 0 #define CLEARAREA_DATA_LIMIT 0 #define CLEARAREA_DATA_OFFSET 16 #define CLEARAREA_CACHE_SLOTS 3000 #define CLEARAREA_CACHE_THRESHOLD 5 #define CLEARAREA_CACHE_LOWER_THRESHOLD 1 // // The message class. // class ClearAreaMessage : public Message { friend class ClearAreaStore; public: ClearAreaMessage() { } ~ClearAreaMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char exposures; unsigned int window; unsigned short x; unsigned short y; unsigned short width; unsigned short height; }; class ClearAreaStore : public MessageStore { // // Constructors and destructors. // public: ClearAreaStore() : MessageStore() { enableCache = CLEARAREA_ENABLE_CACHE; enableData = CLEARAREA_ENABLE_DATA; enableSplit = CLEARAREA_ENABLE_SPLIT; enableCompress = CLEARAREA_ENABLE_COMPRESS; dataLimit = CLEARAREA_DATA_LIMIT; dataOffset = CLEARAREA_DATA_OFFSET; cacheSlots = CLEARAREA_CACHE_SLOTS; cacheThreshold = CLEARAREA_CACHE_THRESHOLD; cacheLowerThreshold = CLEARAREA_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~ClearAreaStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "ClearArea"; } virtual unsigned char opcode() const { return X_ClearArea; } virtual unsigned int storage() const { return sizeof(ClearAreaMessage); } // // Message handling methods. // public: virtual Message *create() const { return new ClearAreaMessage(); } virtual Message *create(const Message &message) const { return new ClearAreaMessage((const ClearAreaMessage &) message); } virtual void destroy(Message *message) const { delete (ClearAreaMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* ClearArea_H */ nxcomp/CreatePixmapCompat.cpp0000644000076400007640000002136111323113030016552 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "CreatePixmapCompat.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Constructors and destructors. // CreatePixmapCompatStore::CreatePixmapCompatStore() : MessageStore() { enableCache = CREATEPIXMAP_ENABLE_CACHE; enableData = CREATEPIXMAP_ENABLE_DATA; enableSplit = CREATEPIXMAP_ENABLE_SPLIT; enableCompress = CREATEPIXMAP_ENABLE_COMPRESS; dataLimit = CREATEPIXMAP_DATA_LIMIT; dataOffset = CREATEPIXMAP_DATA_OFFSET; cacheSlots = CREATEPIXMAP_CACHE_SLOTS; cacheThreshold = CREATEPIXMAP_CACHE_THRESHOLD; cacheLowerThreshold = CREATEPIXMAP_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } CreatePixmapCompatStore::~CreatePixmapCompatStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int CreatePixmapCompatStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(*(buffer + 1), 8, clientCache -> depthCache); encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian), clientCache -> createPixmapLastId, 29, clientCache -> createPixmapIdCache, 4); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> drawableCache); encodeBuffer.encodeCachedValue(GetUINT(buffer + 12, bigEndian), 16, clientCache -> createPixmapXCache, 8); encodeBuffer.encodeCachedValue(GetUINT(buffer + 14, bigEndian), 16, clientCache -> createPixmapYCache, 8); #ifdef TEST *logofs << name() << ": Encoded message. Size is " << size << ".\n" << logofs_flush; #endif return 1; } int CreatePixmapCompatStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; unsigned char cValue; unsigned int value; size = 16; buffer = writeBuffer -> addMessage(size); decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); *(buffer + 1) = cValue; decodeBuffer.decodeDiffCachedValue(value, clientCache -> createPixmapLastId, 29, clientCache -> createPixmapIdCache, 4); PutULONG(value, buffer + 4, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); PutULONG(value, buffer + 8, bigEndian); decodeBuffer.decodeCachedValue(value, 16, clientCache -> createPixmapXCache, 8); PutUINT(value, buffer + 12, bigEndian); decodeBuffer.decodeCachedValue(value, 16, clientCache -> createPixmapYCache, 8); PutUINT(value, buffer + 14, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Size is " << size << ".\n" << logofs_flush; #endif return 1; } int CreatePixmapCompatStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message; createPixmap -> depth = *(buffer + 1); createPixmap -> id = GetULONG(buffer + 4, bigEndian); createPixmap -> drawable = GetULONG(buffer + 8, bigEndian); createPixmap -> width = GetUINT(buffer + 12, bigEndian); createPixmap -> height = GetUINT(buffer + 14, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Size is " << createPixmap -> size_ << " identity is " << createPixmap -> i_size_ << ".\n" << logofs_flush; #endif return 1; } int CreatePixmapCompatStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message; *(buffer + 1) = createPixmap -> depth; PutULONG(createPixmap -> id, buffer + 4, bigEndian); PutULONG(createPixmap -> drawable, buffer + 8, bigEndian); PutUINT(createPixmap -> width, buffer + 12, bigEndian); PutUINT(createPixmap -> height, buffer + 14, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Size is " << createPixmap -> size_ << " identity is " << createPixmap -> i_size_ << ".\n" << logofs_flush; #endif return 1; } void CreatePixmapCompatStore::dumpIdentity(const Message *message) const { #ifdef DUMP #ifdef WARNING *logofs << name() << ": WARNING! Dump of identity not implemented.\n" << logofs_flush; #endif #endif } void CreatePixmapCompatStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 1, 1); md5_append(md5_state_, buffer + 8, 8); } void CreatePixmapCompatStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message; CreatePixmapCompatMessage *cachedCreatePixmap = (CreatePixmapCompatMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeDiffCachedValue(createPixmap -> id, clientCache -> createPixmapLastId, 29, clientCache -> createPixmapIdCache, 4); cachedCreatePixmap -> id = createPixmap -> id; encodeBuffer.encodeXidValue(createPixmap -> drawable, clientCache -> drawableCache); cachedCreatePixmap -> drawable = createPixmap -> drawable; #ifdef TEST *logofs << name() << ": Encoded update. Size is " << createPixmap -> size_ << " identity is " << createPixmap -> i_size_ << ".\n" << logofs_flush; #endif } void CreatePixmapCompatStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeDiffCachedValue(createPixmap -> id, clientCache -> createPixmapLastId, 29, clientCache -> createPixmapIdCache, 4); decodeBuffer.decodeXidValue(createPixmap -> drawable, clientCache -> drawableCache); #ifdef TEST *logofs << name() << ": Decoded update. Size is " << createPixmap -> size_ << " identity is " << createPixmap -> i_size_ << ".\n" << logofs_flush; #endif } nxcomp/NX.h0000644000076400007640000003745011323113030013024 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef NX_H #define NX_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #define NX_FD_ANY -1 #define NX_MODE_ANY -1 #define NX_MODE_CLIENT 1 #define NX_MODE_SERVER 2 #define NX_DISPLAY_ANY NULL #define NX_SIGNAL_ANY -1 #define NX_SIGNAL_ENABLE 1 #define NX_SIGNAL_DISABLE 2 #define NX_SIGNAL_RAISE 3 #define NX_SIGNAL_FORWARD 4 #define NX_POLICY_IMMEDIATE 1 #define NX_POLICY_DEFERRED 2 #define NX_ALERT_REMOTE 0 #define NX_ALERT_LOCAL 1 #define NX_HANDLER_FLUSH 0 #define NX_HANDLER_STATISTICS 1 #define NX_STATISTICS_PARTIAL 0 #define NX_STATISTICS_TOTAL 1 #define NX_CHANNEL_X11 0 #define NX_CHANNEL_CUPS 1 #define NX_CHANNEL_SMB 2 #define NX_CHANNEL_MEDIA 3 #define NX_CHANNEL_HTTP 4 #define NX_CHANNEL_FONT 5 #define NX_CHANNEL_SLAVE 6 #define NX_FILE_SESSION 0 #define NX_FILE_ERRORS 1 #define NX_FILE_OPTIONS 2 #define NX_FILE_STATS 3 /* * The following are the new interfaces to the NX transport. The * NX proxy software is now intended to be run as a library of a * higher level communication manager (nxssh, nxhttp, nxrtp, etc, * not only nxproxy). This is a work-in-progress, so expect these * interfaces to change in future. At the present moment, as an * example, there is no provision for creating and managing mul- * tiple proxy connections. */ /* * Attach a NX transport to the provided descriptor. This should be * done after having created a pair of connected sockets. */ extern int NXTransCreate(int fd, int mode, const char *options); /* * Tell the proxy to use the second descriptor as its own end of * the internal connection to the NX agent. The NX agent will use * the first descriptor. Setting an agent connection will have the * effect of disabling further X client connections and, if it is * possible, will trigger the use of the memory-to-memory transport. */ extern int NXTransAgent(int fd[2]); /* * Prepare the file sets and the timeout for a later execution of * the select(). The masks and the timeout must persist across all * the calls, so if you don't need any of the values, it is requi- * red that you create empty masks and a default timeout. To save * a check at each run, all the functions below assume that valid * pointers are passed. */ extern int NXTransPrepare(int *maxfds, fd_set *readfds, fd_set *writefds, struct timeval *timeout); /* * Call select() to find out the descriptors in the sets having * pending data. */ extern int NXTransSelect(int *result, int *error, int *maxfds, fd_set *readfds, fd_set *writefds, struct timeval *timeout); /* * Perform the required I/O on all the NX descriptors having pen- * ding data. This can include reading and writing to the NX chan- * nels, encoding and decoding the proxy data or managing any of * the other NX resources. */ extern int NXTransExecute(int *result, int *error, int *maxfds, fd_set *readfds, fd_set *writefds, struct timeval *timeout); /* * Run an empty loop, giving to the NX transport a chance to check * its descriptors. */ extern int NXTransContinue(struct timeval *timeout); /* * Perform I/O on the given descriptors. If memory-to-memory trans- * port has been activated and the descriptor is recognized as a * valid agent connection, then the functions will read and write * the data directly to the proxy buffer, otherwise the correspond- * ing network operation will be performed. */ extern int NXTransRead(int fd, char *data, int size); extern int NXTransWrite(int fd, char *data, int size); extern int NXTransReadable(int fd, int *readable); extern int NXTransReadVector(int fd, struct iovec *iovdata, int iovsize); extern int NXTransWriteVector(int fd, struct iovec *iovdata, int iovsize); extern int NXTransClose(int fd); /* * Return true if the NX transport is running. The fd parameter can * be either the local descriptor attached to the NX transport or * NX_FD_ANY. */ extern int NXTransRunning(int fd); /* * Close down the NX transport and free all the allocated resources. * The fd parameter can be either the local descriptor or NX_FD_ANY. * This must be explicitly called by the agent before the proxy can * start the tear down procedure. */ extern int NXTransDestroy(int fd); /* * Tell to the proxy how to handle the standard POSIX signals. For * example, given the SIGINT signal, the caller can specify any of * the following actions: * * NX_SIGNAL_ENABLE: A signal handler will have to be installed by * the library, so that it can be intercepted by * the proxy. * * NX_SIGNAL_DISABLE: The signal will be handled by the caller and, * eventually, forwarded to the proxy by calling * NXTransSignal() explicitly. * * NX_SIGNAL_RAISE: The signal must be handled now, as if it had * been delivered by the operating system. This * function can be called by the agent with the * purpose of propagating a signal to the proxy. * * NX_SIGNAL_FORWARD: A signal handler will have to be installed by * the library but the library will have to call * the original signal handler when the signal * is received. * * As a rule of thumb, agents should let the proxy handle SIGUSR1 * and SIGUSR2, used for producing the NX protocol statistics, and * SIGHUP, used for disconnecting the NX transport. * * The following signals are blocked by default upon creation of the * NX transport: * * SIGCHLD These signals should be always put under the control * SIGUSR1 of the proxy. If agents are intercepting them, agents * SIGUSR2 should later call NXTransSignal(..., NX_SIGNAL_RAISE) * SIGHUP to forward the signal to the proxy. As an alternative * they can specify a NX_SIGNAL_FORWARD action, so they, * in turn, can be notified about the signal. This can * be especially useful for SIGCHLD. * * SIGINT These signals should be intercepted by agents. Agents * SIGTERM should ensure that NXTransDestroy() is called before * exiting, to give the proxy a chance to shut down the * NX transport. * * SIGPIPE This signal is blocked by the proxy, but not used to * implement any functionality. It can be handled by the * NX agent without affecting the proxy. * * SIGALRM This is now used by the proxy and agents should not * redefine it. Agents can use the signal to implement * their own timers but should not interleave calls to * the NX transport and should restore the old handler * when the timeout is raised. * * SIGVTALRM These signals are not used but may be used in future * SIGWINCH versions of the library. * SIGIO * SIGTSTP * SIGTTIN * SIGTTOU * * By calling NXTransSignal(..., NX_SIGNAL_DISABLE) nxcomp will res- * tore the signal handler that was saved at the time the proxy hand- * ler was installed. This means that you should call the function * just after the XOpenDisplay() or any other function used to init- * ialize the NX transport. */ extern int NXTransSignal(int signal, int action); /* * Return a value between 0 and 9 indicating the congestion level * based on the tokens still available. A value of 9 means that * the link is congested and no further data can be sent. */ extern int NXTransCongestion(int fd); /* * Let the application to be notified by the proxy when an event oc- * curs. The parameter, as set at the time the handler is installed, * is passed each time to the callback function. The parameter is * presumably the display pointer, given that at the present moment * the NX transport doesn't have access to the display structure and * so wouldn't be able to determine the display to pass to the call- * back function. * * NX_HANDLER_FLUSH: The handler function is called when some * more data has been written to the proxy * link. * * The data is the number of bytes written. * * NX_HANDLER_STATISTICS: This handler is called to let the agent * include arbitrary data in the transport * statistics. The parameter, in this case, * is a pointer to a pointer to a null term- * inated string. The pointer is set at the * time the handler is registered. The point- * ed string will have to be filled by the * agent with its statistics data. * * The data can be NX_STATISTICS_PARTIAL or NX_STATISTICS_TOTAL. The * agent can refer to the value by using the NXStatisticsPartial and * NXStatisticsTotal constants defined in NXvars.h. * * Note that these interfaces are used by Xlib and nxcompext. Agents * should never call these interfaces directly, but use the nxcompext * wrapper. */ extern int NXTransHandler(int fd, int type, void (*handler)(void *parameter, int reason), void *parameter); /* * Set the policy to be used by the NX transport to write data to the * proxy link: * * NX_POLICY_IMMEDIATE: When set to immediate, the proxy will try to * write the data just after having encoded it. * * NX_POLICY_DEFERRED: When policy is set to deferred, data will be * accumulated in a buffer and written to the * remote proxy when NXTransFlush() is called by * the agent. */ extern int NXTransPolicy(int fd, int type); /* * Query the number of bytes that have been accumulated for a deferred * flush. */ extern int NXTransFlushable(int fd); /* * Tell to the NX transport to write all the accumulated data to the * remote proxy. */ extern int NXTransFlush(int fd); /* * Create a new channel of the given type. It returns 1 on success, * 0 if the NX transport is not running, or -1 in the case of error. * On success, the descriptor provided by the caller can be later * used for the subsequent I/O. The type parameter not only tells to * the proxy the remote port where the channel has to be connected, * but also gives a hint about the type of data that will be carried * by the channel, so that the proxy can try to optimize the traffic * on the proxy link. * * NX_CHANNEL_X: The channel will carry X traffic and it * will be connected to the remote X display. * * NX_CHANNEL_CUPS: The channel will carry CUPS/IPP protocol. * * NX_CHANNEL_SMB: The channel will carry SMB/CIFS protocol. * * NX_CHANNEL_MEDIA: The channel will transport audio or other * multimedia data. * * NX_CHANNEL_HTTP: The channel will carry HTTP protocol. * * NX_CHANNEL_FONT: The channel will forward a X font server * connection. * * Only a proxy running at the NX server/X client side will be able * to create a X, CUPS, SMB, MEDIA and HTTP channel. A proxy running * at the NX client/X server side can create font server connections. * The channel creation will also fail if the remote end has not been * set up to forward the connection. * * To create a new channel the agent will have to set up a socketpair * and pass to the proxy one of the socket descriptors. * * Example: * * #include * #include * * int fds[2]; * * if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) * { * ... * } * else * { * // * // Use fds[0] locally and let the * // proxy use fds[1]. * // * * if (NXTransChannel(NX_FD_ANY, fds[1], NX_CHANNEL_X) <= 0) * { * ... * } * * // * // The agent can now use fds[0] in * // read(), write() and select() * // system calls. * // * * ... * } * * Note that all the I/O on the descriptor should be non-blocking, to * give a chance to the NX transport to run in the background and handle * the data that will be fed to the agent's side of the socketpair. This * will happen automatically, as long as the agent uses the XSelect() * version of the select() function (as it is normal whenever performing * Xlib I/O). In all the other cases, like presumably in the agent's main * loop, the agent will have to loop through NXTransPrepare(), NXTrans- * Select() and NXTransExecute() functions explicitly, adding to the sets * the descriptors that are awaited by the agent. Please check the imple- * mentation of _XSelect() in nx-X11/lib/X11/XlibInt.c for an example. */ extern int NXTransChannel(int fd, int channelfd, int type); /* * Return the name of the files used by the proxy for the current session. * * The type parameter can be: * * NX_FILE_SESSION: Usually the file 'session' in the user's session * directory. * * NX_FILE_ERRORS: The file used for the diagnostic output. Usually * the file 'errors' in the session directory. * * NX_FILE_OPTIONS: The file containing the NX options, if any. * * NX_FILE_STATS: The file used for the statistics output. * * The returned string is allocated in static memory. The caller should * copy the string upon returning from the function, without freeing the * pointer. */ extern const char *NXTransFile(int type); /* * Return the time in milliseconds elapsed since the last call to this * same function. */ extern long NXTransTime(void); /* * Other interfaces to the internal transport functions. */ extern int NXTransProxy(int fd, int mode, const char *display); extern int NXTransClient(const char *display); extern int NXTransDialog(const char *caption, const char *message, const char *window, const char *type, int local, const char *display); extern int NXTransAlert(int code, int local); extern int NXTransWatchdog(int timeout); extern int NXTransKeeper(int caches, int images, const char *root); extern void NXTransExit(int code) __attribute__((noreturn)); extern int NXTransParseCommandLine(int argc, const char **argv); extern int NXTransParseEnvironment(const char *env, int force); extern void NXTransCleanup(void) __attribute__((noreturn)); #ifdef __cplusplus } #endif #endif /* NX_H */ nxcomp/Fork.h0000644000076400007640000000246211342773403013414 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Try again if the fork() fails, as it can happen // often on Cygwin. // extern int Fork(); nxcomp/Jpeg.h0000644000076400007640000000300511323113030013351 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Jpeg_H #define Jpeg_H #include "Misc.h" #include "Unpack.h" int UnpackJpeg(T_geometry *geometry, unsigned char method, unsigned char *srcData, int srcSize, int dstBpp, int dstWidth, int dstHeight, unsigned char *dstData, int dstSize); #endif /* Jpeg_H */ nxcomp/SetUnpackColormap.cpp0000644000076400007640000002012011323113031016407 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "SetUnpackColormap.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // SetUnpackColormapStore::SetUnpackColormapStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = SETUNPACKCOLORMAP_ENABLE_CACHE; enableData = SETUNPACKCOLORMAP_ENABLE_DATA; enableSplit = SETUNPACKCOLORMAP_ENABLE_SPLIT; enableCompress = SETUNPACKCOLORMAP_ENABLE_COMPRESS_IF_PROTO_STEP_7; dataLimit = SETUNPACKCOLORMAP_DATA_LIMIT; dataOffset = SETUNPACKCOLORMAP_DATA_OFFSET_IF_PROTO_STEP_7; cacheSlots = SETUNPACKCOLORMAP_CACHE_SLOTS; cacheThreshold = SETUNPACKCOLORMAP_CACHE_THRESHOLD; cacheLowerThreshold = SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD; if (control -> isProtoStep8() == 1) { enableSplit = SETUNPACKCOLORMAP_ENABLE_SPLIT_IF_PROTO_STEP_8; } messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } SetUnpackColormapStore::~SetUnpackColormapStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int SetUnpackColormapStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif // // Encode the source length first because // we need it to determine the size of // the output buffer. // // SrcLength. encodeBuffer.encodeValue(GetULONG(buffer + 8, bigEndian), 32, 9); // Client. encodeBuffer.encodeCachedValue(*(buffer + 1), 8, clientCache -> resourceCache); // Method. encodeBuffer.encodeCachedValue(*(buffer + 4), 8, clientCache -> methodCache); // DstLength. encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9); #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int SetUnpackColormapStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif unsigned int value; unsigned char cValue; // SrcLength. decodeBuffer.decodeValue(value, 32, 9); size = RoundUp4(value) + 16; buffer = writeBuffer -> addMessage(size); PutULONG(value, buffer + 8, bigEndian); // Client. decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> resourceCache); *(buffer + 1) = cValue; // Method. decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> methodCache); *(buffer + 4) = cValue; // DstLength. decodeBuffer.decodeValue(value, 32, 9); PutULONG(value, buffer + 12, bigEndian); #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int SetUnpackColormapStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message; setUnpackColormap -> client = *(buffer + 1); setUnpackColormap -> method = *(buffer + 4); setUnpackColormap -> src_length = GetULONG(buffer + 8, bigEndian); setUnpackColormap -> dst_length = GetULONG(buffer + 12, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int SetUnpackColormapStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message; *(buffer + 1) = setUnpackColormap -> client; *(buffer + 4) = setUnpackColormap -> method; PutULONG(setUnpackColormap -> src_length, buffer + 8, bigEndian); PutULONG(setUnpackColormap -> dst_length, buffer + 12, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void SetUnpackColormapStore::dumpIdentity(const Message *message) const { #ifdef DUMP SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message; *logofs << name() << ": Identity client " << (unsigned int) setUnpackColormap -> client << " method " << (unsigned int) setUnpackColormap -> method << " source length " << setUnpackColormap -> src_length << " destination length " << setUnpackColormap -> dst_length << " size " << setUnpackColormap -> size_ << ".\n"; #endif } void SetUnpackColormapStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // Include the pack method and the source // and destination length. // md5_append(md5_state_, buffer + 4, 1); md5_append(md5_state_, buffer + 8, 8); } void SetUnpackColormapStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message; SetUnpackColormapMessage *cachedSetUnpackColormap = (SetUnpackColormapMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(setUnpackColormap -> client, 8, clientCache -> resourceCache); cachedSetUnpackColormap -> client = setUnpackColormap -> client; } void SetUnpackColormapStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(setUnpackColormap -> client, 8, clientCache -> resourceCache); } nxcomp/MD5.c0000644000076400007640000003070311323113027013057 0ustar svetonisvetoni/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "MD5.h" #include /* * Try to determine the CPU endianess * at compile time. */ #if defined(__linux) || defined(__CYGWIN32__) #include #if (__BYTE_ORDER == __LITTLE_ENDIAN) #define ARCH_IS_BIG_ENDIAN 0 #else #define ARCH_IS_BIG_ENDIAN 1 #endif #endif /* #if defined(__linux) || defined(__CYGWIN32__) */ #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } nxcomp/PolyArc.h0000644000076400007640000001076411323113030014047 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PolyArc_H #define PolyArc_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define POLYARC_ENABLE_CACHE 1 #define POLYARC_ENABLE_DATA 0 #define POLYARC_ENABLE_SPLIT 0 #define POLYARC_ENABLE_COMPRESS 0 #define POLYARC_DATA_LIMIT 1980 #define POLYARC_DATA_OFFSET 12 #define POLYARC_CACHE_SLOTS 2000 #define POLYARC_CACHE_THRESHOLD 2 #define POLYARC_CACHE_LOWER_THRESHOLD 1 // // The message class. // class PolyArcMessage : public Message { friend class PolyArcStore; public: PolyArcMessage() { } ~PolyArcMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int drawable; unsigned int gcontext; }; class PolyArcStore : public MessageStore { // // Constructors and destructors. // public: PolyArcStore() : MessageStore() { enableCache = POLYARC_ENABLE_CACHE; enableData = POLYARC_ENABLE_DATA; enableSplit = POLYARC_ENABLE_SPLIT; enableCompress = POLYARC_ENABLE_COMPRESS; dataLimit = POLYARC_DATA_LIMIT; dataOffset = POLYARC_DATA_OFFSET; cacheSlots = POLYARC_CACHE_SLOTS; cacheThreshold = POLYARC_CACHE_THRESHOLD; cacheLowerThreshold = POLYARC_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~PolyArcStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "PolyArc"; } virtual unsigned char opcode() const { return X_PolyArc; } virtual unsigned int storage() const { return sizeof(PolyArcMessage); } // // Message handling methods. // public: virtual Message *create() const { return new PolyArcMessage(); } virtual Message *create(const Message &message) const { return new PolyArcMessage((const PolyArcMessage &) message); } virtual void destroy(Message *message) const { delete (PolyArcMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* PolyArc_H */ nxcomp/Rgb.h0000644000076400007640000000301611323113030013200 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Rgb_H #define Rgb_H #include "Unpack.h" int UnpackRgb(T_geometry *geometry, unsigned char method, unsigned char *src_data, int src_size, int dst_bpp, int dst_width, int dst_height, unsigned char *dst_data, int dst_size); #endif /* Rgb_H */ nxcomp/Socket.cpp0000644000076400007640000004015211323113031014254 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun) #include #endif #ifdef __sun #include #include #endif #include #include #include #include #include #include // // System specific defines. // #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun) #define SOL_IP IPPROTO_IP #endif #ifdef __sun #define INADDR_NONE ((unsigned int) -1) #endif // // The TIOCOUTQ ioctl is not implemented on Cygwin. // Note also that TIOCOUTQ and IPTOS_LOWDELAY are // disabled when running on MacOS/X. // #ifdef __CYGWIN32__ #define TIOCOUTQ ((unsigned int) -1) #endif // // NX includes. // #include "Misc.h" #include "Socket.h" // // Set verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Set this only once by querying OS details. // static int _kernelStep = -1; int GetKernelStep() { if (_kernelStep < 0) { // // At the moment only NX clients run on Win32 // and MacOS/X so we are not really interested // in the relevant OS dependent functions. // #if defined(__CYGWIN32__) || defined(__APPLE__) _kernelStep = 0; #else struct utsname buffer; if (uname(&buffer) < 0) { #ifdef WARNING *logofs << "Socket: WARNING! Failed to get system info. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; *logofs << "Socket: WARNING! Assuming lowest system support.\n" << logofs_flush; #endif cerr << "Warning" << ": Failed to get system info. Error is " << EGET() << " '" << ESTR() << "'.\n"; cerr << "Warning" << ": Assuming lowest system support.\n"; _kernelStep = 0; } else { #ifdef TEST *logofs << "Socket: System is '" << buffer.sysname << "' nodename '" << buffer.nodename << "' release '" << buffer.release << "'.\n" << logofs_flush; *logofs << "Socket: Version is '" << buffer.version << "' machine '" << buffer.machine << "'.\n" << logofs_flush; #endif // // Should test support on other operating systems. // if (strcmp(buffer.sysname, "Linux") == 0) { if (strncmp(buffer.release, "2.0.", 4) == 0 || strncmp(buffer.release, "2.2.", 4) == 0) { #ifdef TEST *logofs << "Socket: Assuming level 2 system support.\n" << logofs_flush; #endif _kernelStep = 2; } else { #ifdef TEST *logofs << "Socket: Assuming level 3 system support.\n" << logofs_flush; #endif _kernelStep = 3; } } else if (strcmp(buffer.sysname, "SunOS") == 0) { #ifdef TEST *logofs << "Socket: Assuming level 1 system support.\n" << logofs_flush; #endif _kernelStep = 1; } else { #ifdef TEST *logofs << "Socket: Assuming level 0 system support.\n" << logofs_flush; #endif _kernelStep = 0; } } #endif /* #if defined(__CYGWIN32__) || defined(__APPLE__) */ } return _kernelStep; } int SetReuseAddress(int fd) { int flag = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(flag)) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set SO_REUSEADDR flag on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set SO_REUSEADDR flag on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set SO_REUSEADDR flag on FD#" << fd << ".\n" << logofs_flush; } #endif return 1; } int SetNonBlocking(int fd, int value) { int flags = fcntl(fd, F_GETFL); if (flags >= 0) { if (value == 0) { flags &= ~O_NONBLOCK; } else { flags |= O_NONBLOCK; } } if (flags < 0 || fcntl(fd, F_SETFL, flags) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set O_NONBLOCK flag on FD#" << fd << " to " << value << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set O_NONBLOCK flag on FD#" << fd << " to " << value << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set O_NONBLOCK flag on FD#" << fd << " to " << value << ".\n" << logofs_flush; } #endif return 1; } int SetLingerTimeout(int fd, int timeout) { struct linger linger_value; if (timeout > 0) { linger_value.l_onoff = 1; linger_value.l_linger = timeout; } else { linger_value.l_onoff = 0; linger_value.l_linger = 0; } if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_value, sizeof(linger_value)) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set SO_LINGER values to " << linger_value.l_onoff << " and " << linger_value.l_linger << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set SO_LINGER values to " << linger_value.l_onoff << " and " << linger_value.l_linger << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set SO_LINGER values to " << linger_value.l_onoff << " and " << linger_value.l_linger << " on FD#" << fd << ".\n" << logofs_flush; } #endif return 1; } int SetSendBuffer(int fd, int size) { if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set SO_SNDBUF size to " << size << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set SO_SNDBUF size to " << size << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set SO_SNDBUF on FD#" << fd << " to " << size << " bytes.\n" << logofs_flush; } #endif return 1; } int SetReceiveBuffer(int fd, int size) { if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set SO_RCVBUF size to " << size << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set SO_RCVBUF size to " << size << " on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set SO_RCVBUF on FD#" << fd << " to " << size << " bytes.\n" << logofs_flush; } #endif return 1; } int SetNoDelay(int fd, int value) { int result = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); if (result == 0) { result = 1; } else if (result < 0) { // // Is it become a different error on // Mac OSX 10.4? // #if defined(__APPLE__) result = 0; #endif #if defined(__sun) if (EGET() == ENOPROTOOPT) { result = 0; } #endif #if !defined(__APPLE__) && !defined(__sun) if (EGET() == EOPNOTSUPP) { result = 0; } #endif } if (result < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set TCP_NODELAY flag on " << "FD#" << fd << " to " << value << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set TCP_NODELAY flag on " << "FD#" << fd << " to " << value << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; } #ifdef TEST else if (result == 0) { #ifdef TEST *logofs << "Socket: Option TCP_NODELAY not supported " << "on FD#" << fd << ".\n" << logofs_flush; #endif } else { *logofs << "Socket: Set TCP_NODELAY flag on FD#" << fd << " to " << value << ".\n" << logofs_flush; } #endif return result; } int SetKeepAlive(int fd) { int flag = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to set SO_KEEPALIVE flag on " << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to set SO_KEEPALIVE flag on " << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } #ifdef TEST else { *logofs << "Socket: Set SO_KEEPALIVE flag on FD#" << fd << ".\n" << logofs_flush; } #endif return 1; } int SetLowDelay(int fd) { if (_kernelStep < 0) { GetKernelStep(); } switch (_kernelStep) { case 3: case 2: case 1: { int flag = IPTOS_LOWDELAY; if (setsockopt(fd, SOL_IP, IP_TOS, &flag, sizeof(flag)) < 0) { if (EGET() == EOPNOTSUPP) { #ifdef TEST *logofs << "Socket: Option IPTOS_LOWDELAY not supported " << "on FD#" << fd << ".\n" << logofs_flush; #endif return 0; } else { #ifdef WARNING *logofs << "Socket: WARNING! Failed to set IPTOS_LOWDELAY flag on " << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Failed to set IPTOS_LOWDELAY flag on " << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } } #ifdef TEST else { *logofs << "Socket: Set IPTOS_LOWDELAY flag on FD#" << fd << ".\n" << logofs_flush; } #endif return 1; } default: { #ifdef TEST *logofs << "Socket: Option IPTOS_LOWDELAY not " << "supported on FD#" << fd << ".\n" << logofs_flush; #endif return 0; } } } int SetCloseOnExec(int fd) { if (fcntl(fd, F_SETFD, 1) != 0) { #ifdef TEST *logofs << "NXClient: PANIC! Cannot set close-on-exec " << "on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot set close-on-exec on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } return 1; } int GetBytesReadable(int fd) { long readable = 0; // // It may fail, for example at session // shutdown. // if (ioctl(fd, FIONREAD, &readable) < 0) { #ifdef TEST *logofs << "Socket: PANIC! Failed to get bytes readable " << "from FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif return -1; } #ifdef TEST *logofs << "Socket: Returning " << (int) readable << " bytes readable on FD#" << fd << ".\n" << logofs_flush; #endif return (int) readable; } int GetBytesWritable(int fd) { if (_kernelStep < 0) { GetKernelStep(); } long writable; switch (_kernelStep) { case 3: { // // TODO: Should query the real size // of the TCP write buffer. // writable = 16384 - GetBytesQueued(fd); if (writable < 0) { writable = 0; } break; } case 2: { if (ioctl(fd, TIOCOUTQ, (void *) &writable) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to get bytes writable " << "on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to get bytes writable " << "on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } break; } default: { #ifdef TEST *logofs << "Socket: Option TIOCOUTQ not supported " << "on FD#" << fd << ",\n" << logofs_flush; #endif // // TODO: Should query the real size // of the TCP write buffer. // writable = 16384; break; } } #ifdef TEST *logofs << "Socket: Returning " << writable << " bytes writable on FD#" << fd << ".\n" << logofs_flush; #endif return (int) writable; } int GetBytesQueued(int fd) { // // The TIOCOUTQ ioctl is not implemented on Cygwin // and returns the space available on Linux Kernels // 2.0 and 2.2 (like current MIPS for PS/2). // if (_kernelStep < 0) { GetKernelStep(); } long queued; switch (_kernelStep) { case 3: { if (ioctl(fd, TIOCOUTQ, (void *) &queued) < 0) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to get bytes queued " << "on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to get bytes queued " << "on FD#" << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; return -1; } break; } case 2: { // // TODO: Should query the real size // of the TCP write buffer. // queued = 16384 - GetBytesWritable(fd); if (queued < 0) { queued = 0; } break; } default: { #ifdef TEST *logofs << "Socket: Option TIOCOUTQ not supported " << "on FD#" << fd << ",\n" << logofs_flush; #endif queued = 0; break; } } #ifdef TEST *logofs << "Socket: Returning " << queued << " bytes queued on FD#" << fd << ".\n" << logofs_flush; #endif return (int) queued; } int GetHostAddress(const char *name) { hostent *host = gethostbyname(name); if (host == NULL) { // // On some Unices gethostbyname() doesn't // accept IP addresses, so try inet_addr. // IN_ADDR_T address = inet_addr(name); if (address == INADDR_NONE) { #ifdef PANIC *logofs << "Socket: PANIC! Failed to resolve address of '" << name << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to resolve address of '" << name << "'.\n"; return 0; } return (int) address; } else { return (*((int *) host -> h_addr_list[0])); } } nxcomp/GenericReply.h0000644000076400007640000001112111323113027015060 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef GenericReply_H #define GenericReply_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define GENERICREPLY_ENABLE_CACHE 1 #define GENERICREPLY_ENABLE_DATA 1 #define GENERICREPLY_ENABLE_SPLIT 0 #define GENERICREPLY_ENABLE_COMPRESS 1 #define GENERICREPLY_DATA_LIMIT 1048576 - 32 #define GENERICREPLY_DATA_OFFSET 32 #define GENERICREPLY_CACHE_SLOTS 400 #define GENERICREPLY_CACHE_THRESHOLD 5 #define GENERICREPLY_CACHE_LOWER_THRESHOLD 1 #define GENERICREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 // // The message class. // class GenericReplyMessage : public Message { friend class GenericReplyStore; public: GenericReplyMessage() { } ~GenericReplyMessage() { } // // Put here the fields which constitute the // 'identity' part of the message. Starting // from protocol level 3 we use short data // to increase cache efficiency. // private: unsigned char byte_data; unsigned int int_data[6]; unsigned short short_data[12]; }; class GenericReplyStore : public MessageStore { public: GenericReplyStore(StaticCompressor *compressor); virtual ~GenericReplyStore(); virtual const char *name() const { return "GenericReply"; } virtual unsigned char opcode() const { return X_NXInternalGenericReply; } virtual unsigned int storage() const { return sizeof(GenericReplyMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new GenericReplyMessage(); } virtual Message *create(const Message &message) const { return new GenericReplyMessage((const GenericReplyMessage &) message); } virtual void destroy(Message *message) const { delete (GenericReplyMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* GenericReply_H */ nxcomp/ChangeGC.h0000644000076400007640000001120411323113030014063 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ChangeGC_H #define ChangeGC_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define CHANGEGC_ENABLE_CACHE 1 #define CHANGEGC_ENABLE_DATA 0 #define CHANGEGC_ENABLE_SPLIT 0 #define CHANGEGC_ENABLE_COMPRESS 0 #define CHANGEGC_DATA_LIMIT 144 #define CHANGEGC_DATA_OFFSET 12 #define CHANGEGC_CACHE_SLOTS 3000 #define CHANGEGC_CACHE_THRESHOLD 3 #define CHANGEGC_CACHE_LOWER_THRESHOLD 1 // // The message class. // class ChangeGCMessage : public Message { friend class ChangeGCStore; public: ChangeGCMessage() { } ~ChangeGCMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int gcontext; unsigned int value_mask; }; class ChangeGCStore : public MessageStore { // // Constructors and destructors. // public: ChangeGCStore() : MessageStore() { enableCache = CHANGEGC_ENABLE_CACHE; enableData = CHANGEGC_ENABLE_DATA; enableSplit = CHANGEGC_ENABLE_SPLIT; enableCompress = CHANGEGC_ENABLE_COMPRESS; dataLimit = CHANGEGC_DATA_LIMIT; dataOffset = CHANGEGC_DATA_OFFSET; cacheSlots = CHANGEGC_CACHE_SLOTS; cacheThreshold = CHANGEGC_CACHE_THRESHOLD; cacheLowerThreshold = CHANGEGC_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~ChangeGCStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "ChangeGC"; } virtual unsigned char opcode() const { return X_ChangeGC; } virtual unsigned int storage() const { return sizeof(ChangeGCMessage); } // // Message handling methods. // public: virtual Message *create() const { return new ChangeGCMessage(); } virtual Message *create(const Message &message) const { return new ChangeGCMessage((const ChangeGCMessage &) message); } virtual void destroy(Message *message) const { delete (ChangeGCMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; }; #endif /* ChangeGC_H */ nxcomp/Channel.cpp0000644000076400007640000015052011342773403014415 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Channel.h" #include "List.h" #include "Proxy.h" #include "Statistics.h" #include "StaticCompressor.h" #include "NXalert.h" extern Proxy *proxy; // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Log the operations related to splits. // #undef SPLIT #undef COUNT #define COUNT_MAJOR_OPCODE 154 #undef MONITOR #define MONITOR_MAJOR_OPCODE 154 #define MONITOR_MINOR_OPCODE 23 #undef CLEAR #define CLEAR_MAJOR_OPCODE 154 #define CLEAR_MINOR_OPCODE 23 // // Define this to know how many messages // are allocated and deallocated. // #undef REFERENCES // // Set to the descriptor of the first X // channel successfully connected. // int Channel::firstClient_ = -1; // // Port used for font server connections. // int Channel::fontPort_ = -1; // // This is used for reference count. // #ifdef REFERENCES int Channel::references_ = 0; #endif Channel::Channel(Transport *transport, StaticCompressor *compressor) : transport_(transport), compressor_(compressor) { fd_ = transport_ -> fd(); finish_ = 0; closing_ = 0; drop_ = 0; congestion_ = 0; priority_ = 0; alert_ = 0; firstRequest_ = 1; firstReply_ = 1; enableCache_ = 1; enableSplit_ = 1; enableSave_ = 1; enableLoad_ = 1; // // Must be set by proxy. // opcodeStore_ = NULL; clientStore_ = NULL; serverStore_ = NULL; clientCache_ = NULL; serverCache_ = NULL; #ifdef REFERENCES *logofs << "Channel: Created new Channel at " << this << " out of " << ++references_ << " allocated references.\n" << logofs_flush; #endif } Channel::~Channel() { if (firstClient_ == fd_) { firstClient_ = -1; } #ifdef REFERENCES *logofs << "Channel: Deleted Channel at " << this << " out of " << --references_ << " allocated references.\n" << logofs_flush; #endif } int Channel::handleEncode(EncodeBuffer &encodeBuffer, ChannelCache *channelCache, MessageStore *store, const unsigned char opcode, const unsigned char *buffer, const unsigned int size) { #ifdef MONITOR static float totalMessages = 0; static float totalBits = 0; int bits; int diff; bits = encodeBuffer.getBits(); #endif // // Check if message can be differentially // encoded using a similar message in the // message store. // #ifdef COUNT if (*(buffer) == COUNT_MAJOR_OPCODE) { if (*(buffer) < 128) { *logofs << "handleEncode: Handling OPCODE#" << (unsigned int) *(buffer) << ".\n" << logofs_flush; } else { *logofs << "handleEncode: Handling OPCODE#" << (unsigned int) *(buffer) << " MINOR#" << (unsigned int) *(buffer + 1) << ".\n" << logofs_flush; } } #endif #ifdef CLEAR if (*(buffer) == CLEAR_MAJOR_OPCODE && (CLEAR_MINOR_OPCODE == -1 || *(buffer + 1) == CLEAR_MINOR_OPCODE)) { *((unsigned char *) buffer) = X_NoOperation; *((unsigned char *) buffer + 1) = '\0'; CleanData((unsigned char *) buffer + 4, size - 4); } #endif if (handleEncodeCached(encodeBuffer, channelCache, store, buffer, size) == 1) { #ifdef MONITOR diff = encodeBuffer.getBits() - bits; if (*(buffer) == MONITOR_MAJOR_OPCODE && (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE)) { totalMessages++; totalBits += diff; *logofs << "handleEncode: Handled cached OPCODE#" << (unsigned int) *(buffer) << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size << " bytes in, " << diff << " bits (" << ((float) diff) / 8 << " bytes) out. Average " << totalBits / totalMessages << "/1.\n" << logofs_flush; } #endif // // Let the channel update the split store // and notify the agent in the case of a // cache hit. // if (store -> enableSplit) { handleSplit(encodeBuffer, store, store -> lastAction, store -> lastHit, opcode, buffer, size); } return 1; } // // A similar message could not be found in // cache or message must be discarded. Must // transmit the message using the field by // field differential encoding. // handleEncodeIdentity(encodeBuffer, channelCache, store, buffer, size, bigEndian_); // // Check if message has a distinct data part. // if (store -> enableData) { // // If message split was requested by agent then send data // out-of-band, dividing it in small chunks. Until message // is completely transferred, keep in the split store a // dummy version of the message, with data replaced with a // pattern. // // While data is being transferred, agent should have put // the resource (for example its client) asleep. It can // happen, though, that a different client would reference // the same message. We cannot issue a cache hit for images // being split (such images are put in store in 'incomplete' // state), so we need to handle this case. // if (store -> enableSplit == 1) { // // Let the channel decide what to do with the // message. If the split can't take place be- // cause the split store is full, the channel // will tell the remote side that the data is // going to follow. // if (handleSplit(encodeBuffer, store, store -> lastAction, (store -> lastAction == IS_ADDED ? store -> lastAdded : 0), opcode, buffer, size) == 1) { #ifdef MONITOR diff = encodeBuffer.getBits() - bits; if (*(buffer) == MONITOR_MAJOR_OPCODE && (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE)) { totalMessages++; totalBits += diff; *logofs << "handleEncode: Handled split OPCODE#" << (unsigned int) *(buffer) << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size << " bytes in, " << diff << " bits (" << ((float) diff) / 8 << " bytes) out. Average " << totalBits / totalMessages << "/1.\n" << logofs_flush; } #endif return 0; } } // // The split did not take place and we are going // to transfer the data part. Check if the static // compression of the data section is enabled. // This is the case of all messages not having a // special differential encoding or messages that // we want to store in cache in compressed form. // unsigned int offset = store -> identitySize(buffer, size); if (store -> enableCompress) { unsigned char *data = NULL; unsigned int dataSize = 0; int compressed = handleCompress(encodeBuffer, opcode, offset, buffer, size, data, dataSize); if (compressed < 0) { return -1; } else if (compressed > 0) { // // Update the size of the message according // to the result of the data compression. // handleUpdate(store, size - offset, dataSize); } } else { handleCopy(encodeBuffer, opcode, offset, buffer, size); } } #ifdef MONITOR diff = encodeBuffer.getBits() - bits; if (*(buffer) == MONITOR_MAJOR_OPCODE && (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE)) { totalMessages++; totalBits += diff; *logofs << "handleEncode: Handled OPCODE#" << (unsigned int) *(buffer) << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size << " bytes in, " << diff << " bits (" << ((float) diff) / 8 << " bytes) out. Average " << totalBits / totalMessages << "/1.\n" << logofs_flush; } #endif return 0; } int Channel::handleDecode(DecodeBuffer &decodeBuffer, ChannelCache *channelCache, MessageStore *store, unsigned char &opcode, unsigned char *&buffer, unsigned int &size) { // // Check first if the message is in the // message store. // unsigned int split = 0; if (handleDecodeCached(decodeBuffer, channelCache, store, buffer, size) == 1) { // // Let the channel update the split store // in the case of a message being cached. // if (store -> enableSplit == 1) { if (control -> isProtoStep7() == 1) { #ifdef DEBUG *logofs << "handleDecode: " << store -> name() << ": Checking if the message was split.\n" << logofs_flush; #endif decodeBuffer.decodeBoolValue(split); } if (split == 1) { handleSplit(decodeBuffer, store, store -> lastAction, store -> lastHit, opcode, buffer, size); handleCleanAndNullRequest(opcode, buffer, size); } } return 1; } // // Decode the full identity. // handleDecodeIdentity(decodeBuffer, channelCache, store, buffer, size, bigEndian_, &writeBuffer_); // // Check if the message has a distinct // data part. // if (store -> enableData) { // // Check if message has been split. // if (store -> enableSplit) { #ifdef DEBUG *logofs << "handleDecode: " << store -> name() << ": Checking if the message was split.\n" << logofs_flush; #endif decodeBuffer.decodeBoolValue(split); if (split == 1) { // // If the message was added to the store, // create the entry without the data part. // handleSaveSplit(store, buffer, size); handleSplit(decodeBuffer, store, store -> lastAction, (store -> lastAction == IS_ADDED ? store -> lastAdded : 0), opcode, buffer, size); handleCleanAndNullRequest(opcode, buffer, size); return 0; } } // // Decode the data part. // unsigned int offset = store -> identitySize(buffer, size); if (store -> enableCompress) { const unsigned char *data = NULL; unsigned int dataSize = 0; int decompressed = handleDecompress(decodeBuffer, opcode, offset, buffer, size, data, dataSize); if (decompressed < 0) { return -1; } else if (decompressed > 0) { // // The message has been transferred // in compressed format. // handleSave(store, buffer, size, data, dataSize); if (store -> enableSplit) { if (split == 1) { handleSplit(decodeBuffer, store, store -> lastAction, (store -> lastAction == IS_ADDED ? store -> lastAdded : 0), opcode, buffer, size); handleCleanAndNullRequest(opcode, buffer, size); } } return 0; } } else { // // Static compression of the data part // was not enabled for this message. // handleCopy(decodeBuffer, opcode, offset, buffer, size); } } // // The message doesn't have a data part // or the data was not compressed. // handleSave(store, buffer, size); if (store -> enableSplit) { if (split == 1) { handleSplit(decodeBuffer, store, store -> lastAction, (store -> lastAction == IS_ADDED ? store -> lastAdded : 0), opcode, buffer, size); handleCleanAndNullRequest(opcode, buffer, size); } } return 0; } int Channel::handleEncodeCached(EncodeBuffer &encodeBuffer, ChannelCache *channelCache, MessageStore *store, const unsigned char *buffer, const unsigned int size) { if (control -> LocalDeltaCompression == 0 || enableCache_ == 0 || store -> enableCache == 0) { if (control -> isProtoStep7() == 1) { encodeBuffer.encodeActionValue(is_discarded, store -> lastActionCache); } else { encodeBuffer.encodeActionValueCompat(is_discarded, store -> lastActionCacheCompat); } store -> lastAction = is_discarded; return 0; } #ifdef DEBUG *logofs << "handleEncodeCached: " << store -> name() << ": Going to handle a new message of this class.\n" << logofs_flush; #endif // // Check if the estimated size of cache is greater // than the requested limit. If it is the case make // some room by deleting one or more messages. // int position; while (mustCleanStore(store) == 1 && canCleanStore(store) == 1) { #ifdef DEBUG *logofs << "handleEncodeCached: " << store -> name() << ": Trying to reduce size of message store.\n" << logofs_flush; #endif position = store -> clean(use_checksum); if (position == nothing) { #ifdef TEST *logofs << "handleEncodeCached: " << store -> name() << ": WARNING! No message found to be " << "actually removed.\n" << logofs_flush; #endif break; } #ifdef DEBUG *logofs << "handleEncodeCached: " << store -> name() << ": Message at position " << position << " will be removed.\n" << logofs_flush; #endif // // Encode the position of message to // be discarded. // store -> lastRemoved = position; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeActionValue(is_removed, store -> lastRemoved, store -> lastActionCache); } else { encodeBuffer.encodeActionValueCompat(is_removed, store -> lastActionCacheCompat); encodeBuffer.encodePositionValueCompat(store -> lastRemoved, store -> lastRemovedCacheCompat); } #ifdef DEBUG *logofs << "handleEncodeCached: " << store -> name() << ": Going to " << "clean up message at position " << position << ".\n" << logofs_flush; #endif store -> remove(position, use_checksum, discard_data); #ifdef DEBUG *logofs << "handleEncodeCached: " << store -> name() << ": There are " << store -> getSize() << " messages in the store out of " << store -> cacheSlots << " slots.\n" << logofs_flush; *logofs << "handleEncodeCached: " << store -> name() << ": Size of store is " << store -> getLocalStorageSize() << " bytes locally and " << store -> getRemoteStorageSize() << " bytes remotely.\n" << logofs_flush; *logofs << "handleEncodeCached: " << store -> name() << ": Size of total cache is " << store -> getLocalTotalStorageSize() << " bytes locally and " << store -> getRemoteTotalStorageSize() << " bytes remotely.\n" << logofs_flush; #endif } #ifdef DEBUG if (mustCleanStore(store) == 1 && canCleanStore(store) == 0) { *logofs << "handleEncodeCached: " << store -> name() << ": Store would need a clean but operation will be delayed.\n" << logofs_flush; *logofs << "handleEncodeCached: " << store -> name() << ": There are " << store -> getSize() << " messages in the store out of " << store -> cacheSlots << " slots.\n" << logofs_flush; *logofs << "handleEncodeCached: " << store -> name() << ": Size of store is " << store -> getLocalStorageSize() << " bytes locally and " << store -> getRemoteStorageSize() << " bytes remotely.\n" << logofs_flush; *logofs << "handleEncodeCached: " << store -> name() << ": Size of total cache is " << store -> getLocalTotalStorageSize() << " bytes locally and " << store -> getRemoteTotalStorageSize() << " bytes remotely.\n" << logofs_flush; } #endif // // If 'on the wire' size of message exceeds the // allowed limit then avoid to store it in the // cache. // if (store -> validateMessage(buffer, size) == 0) { #ifdef TEST *logofs << "handleEncodeCached: " << store -> name() << ": Message with size " << size << " ignored.\n" << logofs_flush; #endif if (control -> isProtoStep7() == 1) { encodeBuffer.encodeActionValue(is_discarded, store -> lastActionCache); } else { encodeBuffer.encodeActionValueCompat(is_discarded, store -> lastActionCacheCompat); } store -> lastAction = is_discarded; return 0; } // // Fill the message object with the // received data. // Message *message = store -> getTemporary(); if (message == NULL) { #ifdef PANIC *logofs << "handleEncodeCached: " << store -> name() << ": PANIC! Can't allocate memory for " << "a new message.\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory for " << "a new message in context [D].\n"; HandleCleanup(); } // // As we are at encoding side, it is enough to store the // checksum for the object while data can be erased. Both // the identity and the data will never be sent through // the wire again as long as they are stored in the cache // at the decoding side. The split parameter is always // set to 0 as the data will not be stored in any case. // store -> parse(message, 0, buffer, size, use_checksum, discard_data, bigEndian_); #ifdef DUMP store -> dump(message); #endif // // Search the object in the message // store. If found get the position. // #ifdef DEBUG *logofs << "handleEncodeCached: " << store -> name() << ": Searching object of size " << size << " in the cache.\n" << logofs_flush; #endif int added; int locked; position = store -> findOrAdd(message, use_checksum, discard_data, added, locked); if (position == nothing) { #ifdef WARNING *logofs << "handleEncodeCached: " << store -> name() << ": WARNING! Can't store object in the cache.\n" << logofs_flush; #endif if (control -> isProtoStep7() == 1) { encodeBuffer.encodeActionValue(is_discarded, store -> lastActionCache); } else { encodeBuffer.encodeActionValueCompat(is_discarded, store -> lastActionCacheCompat); } store -> lastAction = is_discarded; return 0; } else if (locked == 1) { // // We can't issue a cache hit. Encoding identity // differences while message it's being split // would later result in agent to commit a wrong // version of message. // #ifdef WARNING *logofs << "handleEncodeCached: " << store -> name() << ": WARNING! Message of size " << store -> plainSize(position) << " at position " << position << " is locked.\n" << logofs_flush; #endif cerr << "Warning" << ": Message of size " << store -> plainSize(position) << " at position " << position << " is locked.\n"; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeActionValue(is_discarded, store -> lastActionCache); } else { encodeBuffer.encodeActionValueCompat(is_discarded, store -> lastActionCacheCompat); } store -> lastAction = is_discarded; return 0; } else if (added == 1) { store -> resetTemporary(); #ifdef DEBUG *logofs << "handleEncodeCached: " << store -> name() << ": Message of size " << store -> plainSize(position) << " has been stored at position " << position << ".\n" << logofs_flush; *logofs << "handleEncodeCached: " << store -> name() << ": There are " << store -> getSize() << " messages in the store out of " << store -> cacheSlots << " slots.\n" << logofs_flush; *logofs << "handleEncodeCached: " << store -> name() << ": Size of store is " << store -> getLocalStorageSize() << " bytes locally and " << store -> getRemoteStorageSize() << " bytes remotely.\n" << logofs_flush; *logofs << "handleEncodeCached: " << store -> name() << ": Size of total cache is " << store -> getLocalTotalStorageSize() << " bytes locally and " << store -> getRemoteTotalStorageSize() << " bytes remotely.\n" << logofs_flush; #endif // // Inform the decoding side that message // must be inserted in cache and encode // the position where the insertion took // place. // store -> lastAction = IS_ADDED; store -> lastAdded = position; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeActionValue(IS_ADDED, store -> lastAdded, store -> lastActionCache); } else { encodeBuffer.encodeActionValueCompat(IS_ADDED, store -> lastActionCacheCompat); encodeBuffer.encodePositionValueCompat(store -> lastAdded, store -> lastAddedCacheCompat); } return 0; } else { #ifdef DEBUG *logofs << "handleEncodeCached: " << store -> name() << ": Cache hit. Found object at position " << position << ".\n" << logofs_flush; #endif // // Must abort the connection if the // the position is invalid. // Message *cachedMessage = store -> get(position); // // Increase the rating of the cached // message. // store -> touch(cachedMessage); #ifdef DEBUG *logofs << "handleEncodeCached: " << store -> name() << ": Hits for " << "object at position " << position << " are now " << store -> getTouches(position) << ".\n" << logofs_flush; #endif // // Send to the decoding side position // where object can be found in cache. // store -> lastAction = IS_HIT; store -> lastHit = position; if (control -> isProtoStep7() == 1) { encodeBuffer.encodeActionValue(IS_HIT, store -> lastHit, store -> lastActionCache); } else { encodeBuffer.encodeActionValueCompat(IS_HIT, store -> lastActionCacheCompat); encodeBuffer.encodePositionValueCompat(store -> lastHit, store -> lastHitCacheCompat); } // // Send the field by field differences in // respect to the original message stored // in cache. // store -> updateIdentity(encodeBuffer, message, cachedMessage, channelCache); return 1; } } void Channel::handleUpdateAdded(MessageStore *store, unsigned int dataSize, unsigned int compressedDataSize) { #ifdef TEST if (store -> lastAction != IS_ADDED) { #ifdef PANIC *logofs << "handleUpdateAdded: " << store -> name() << ": PANIC! Function called for action '" << store -> lastAction << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Update function called for " << "store '" << store -> name() << "' with " << "action '" << store -> lastAction << "'.\n"; HandleCleanup(); } #endif #ifdef DEBUG *logofs << "handleUpdateAdded: " << store -> name() << ": Updating " << "object at position " << store -> lastAdded << " of size " << store -> plainSize(store -> lastAdded) << " (" << dataSize << "/" << compressedDataSize << ").\n" << logofs_flush; #endif store -> updateData(store -> lastAdded, dataSize, compressedDataSize); #ifdef DEBUG *logofs << "handleUpdateAdded: " << store -> name() << ": There are " << store -> getSize() << " messages in the store out of " << store -> cacheSlots << " slots.\n" << logofs_flush; *logofs << "handleUpdateAdded: " << store -> name() << ": Size of store is " << store -> getLocalStorageSize() << " bytes locally and " << store -> getRemoteStorageSize() << " bytes remotely.\n" << logofs_flush; *logofs << "handleUpdateAdded: " << store -> name() << ": Size of total cache is " << store -> getLocalTotalStorageSize() << " bytes locally and " << store -> getRemoteTotalStorageSize() << " bytes remotely.\n" << logofs_flush; #endif } int Channel::handleDecodeCached(DecodeBuffer &decodeBuffer, ChannelCache *channelCache, MessageStore *store, unsigned char *&buffer, unsigned int &size) { // // Create a new message object and // fill it with received data. // #ifdef DEBUG *logofs << "handleDecodeCached: " << store -> name() << ": Going to handle a new message of this class.\n" << logofs_flush; #endif // // Decode bits telling how to handle // this message. // unsigned char action; unsigned short int position; if (control -> isProtoStep7() == 1) { decodeBuffer.decodeActionValue(action, position, store -> lastActionCache); } else { decodeBuffer.decodeActionValueCompat(action, store -> lastActionCacheCompat); } // // Clean operations must always come // before any operation on message. // while (action == is_removed) { if (control -> isProtoStep7() == 1) { store -> lastRemoved = position; } else { decodeBuffer.decodePositionValueCompat(store -> lastRemoved, store -> lastRemovedCacheCompat); } #ifdef DEBUG if (store -> get(store -> lastRemoved)) { *logofs << "handleDecodeCached: " << store -> name() << ": Cleaning up " << "object at position " << store -> lastRemoved << " of size " << store -> plainSize(store -> lastRemoved) << " (" << store -> plainSize(store -> lastRemoved) << "/" << store -> compressedSize(store -> lastRemoved) << ").\n" << logofs_flush; } #endif // // If the message can't be found we // will abort the connection. // store -> remove(store -> lastRemoved, discard_checksum, use_data); if (control -> isProtoStep7() == 1) { decodeBuffer.decodeActionValue(action, position, store -> lastActionCache); } else { decodeBuffer.decodeActionValueCompat(action, store -> lastActionCacheCompat); } } // // If it's a cache hit, the position // where object can be found follows. // if ((T_store_action) action == IS_HIT) { if (control -> isProtoStep7() == 1) { store -> lastHit = position; } else { decodeBuffer.decodePositionValueCompat(store -> lastHit, store -> lastHitCacheCompat); } // // Get data from the cache at given position. // #ifdef DEBUG if (store -> get(store -> lastHit)) { *logofs << "handleDecodeCached: " << store -> name() << ": Retrieving " << "object at position " << store -> lastHit << " of size " << store -> plainSize(store -> lastHit) << " (" << store -> plainSize(store -> lastHit) << "/" << store -> compressedSize(store -> lastHit) << ").\n" << logofs_flush; } #endif // // Must abort the connection if the // the position is invalid. // Message *message = store -> get(store -> lastHit); // // Make room for the outgoing message. // size = store -> plainSize(store -> lastHit); buffer = writeBuffer_.addMessage(size); #ifdef DEBUG *logofs << "handleDecodeCached: " << store -> name() << ": Prepared an outgoing buffer of " << size << " bytes.\n" << logofs_flush; #endif // // Decode the variant part. Pass client // or server cache to the message store. // store -> updateIdentity(decodeBuffer, message, channelCache); // // Write each field in the outgoing buffer. // store -> unparse(message, buffer, size, bigEndian_); #ifdef DUMP store -> dump(message); #endif store -> lastAction = IS_HIT; return 1; } else if ((T_store_action) action == IS_ADDED) { if (control -> isProtoStep7() == 1) { store -> lastAdded = position; } else { decodeBuffer.decodePositionValueCompat(store -> lastAdded, store -> lastAddedCacheCompat); } #ifdef DEBUG *logofs << "handleDecodeCached: " << store -> name() << ": Message will be later stored at position " << store -> lastAdded << ".\n" << logofs_flush; #endif store -> lastAction = IS_ADDED; return 0; } else { #ifdef DEBUG *logofs << "handleDecodeCached: " << store -> name() << ": Message will be later discarded.\n" << logofs_flush; #endif store -> lastAction = is_discarded; return 0; } } void Channel::handleSaveAdded(MessageStore *store, int split, unsigned char *buffer, unsigned int size, const unsigned char *compressedData, const unsigned int compressedDataSize) { #ifdef TEST if (store -> lastAction != IS_ADDED) { #ifdef PANIC *logofs << "handleSaveAdded: " << store -> name() << ": PANIC! Function called for action '" << store -> lastAction << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Save function called for " << "store '" << store -> name() << "' with " << "action '" << store -> lastAction << "'.\n"; HandleCleanup(); } #endif Message *message = store -> getTemporary(); if (message == NULL) { #ifdef PANIC *logofs << "handleSaveAdded: " << store -> name() << ": PANIC! Can't access temporary storage " << "for message at position " << store -> lastAdded << ".\n" << logofs_flush; #endif cerr << "Error" << ": Can't access temporary storage " << "for message at position " << store -> lastAdded << ".\n"; HandleCleanup(); } if (compressedData == NULL) { // // If the data part has been split // avoid to copy it into the message. // store -> parse(message, split, buffer, size, discard_checksum, use_data, bigEndian_); } else { store -> parse(message, buffer, size, compressedData, compressedDataSize, discard_checksum, use_data, bigEndian_); } if (store -> add(message, store -> lastAdded, discard_checksum, use_data) == nothing) { #ifdef PANIC *logofs << "handleSaveAdded: " << store -> name() << ": PANIC! Can't store message in the cache " << "at position " << store -> lastAdded << ".\n" << logofs_flush; #endif cerr << "Error" << ": Can't store message of type " << store -> name() << "in the cache at position " << store -> lastAdded << ".\n"; HandleCleanup(); } else { store -> resetTemporary(); #ifdef DEBUG *logofs << "handleSaveAdded: " << store -> name() << ": Stored " << (compressedData == NULL ? "plain" : "compressed") << " object at position " << store -> lastAdded << " of size " << store -> plainSize(store -> lastAdded) << " (" << store -> plainSize(store -> lastAdded) << "/" << store -> compressedSize(store -> lastAdded) << ").\n" << logofs_flush; #endif } #ifdef DEBUG *logofs << "handleSaveAdded: " << store -> name() << ": Size of store is " << store -> getLocalStorageSize() << " bytes locally and " << store -> getRemoteStorageSize() << " bytes remotely.\n" << logofs_flush; *logofs << "handleSaveAdded: " << store -> name() << ": Size of total cache is " << store -> getLocalTotalStorageSize() << " bytes locally and " << store -> getRemoteTotalStorageSize() << " bytes remotely.\n" << logofs_flush; #endif } int Channel::handleWait(int timeout) { #ifdef TEST *logofs << "handleWait: Going to wait for more data " << "on FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif T_timestamp startTs = getNewTimestamp(); T_timestamp nowTs = startTs; int readable; int remaining; for (;;) { remaining = timeout - diffTimestamp(startTs, nowTs); if (transport_ -> blocked() == 1) { #ifdef WARNING *logofs << "handleWait: WARNING! Having to drain with " << "channel " << "for FD#" << fd_ << " blocked.\n" << logofs_flush; #endif handleDrain(0, remaining); continue; } if (remaining <= 0) { #ifdef TEST *logofs << "handleWait: Timeout raised while waiting " << "for more data for FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif return 0; } #ifdef TEST *logofs << "handleWait: Waiting " << remaining << " Ms " << "for a new message on FD#" << fd_ << ".\n" << logofs_flush; #endif readable = transport_ -> wait(remaining); if (readable > 0) { #ifdef TEST *logofs << "handleWait: WARNING! Encoding more data " << "for FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (proxy -> handleAsyncRead(fd_) < 0) { return -1; } return 1; } else if (readable == -1) { return -1; } nowTs = getNewTimestamp(); } } int Channel::handleDrain(int limit, int timeout) { #ifdef TEST *logofs << "handleDrain: Going to drain FD#" << fd_ << " with a limit of " << limit << " bytes " << "at " << strMsTimestamp() << ".\n" << logofs_flush; #endif T_timestamp startTs = getNewTimestamp(); T_timestamp nowTs = startTs; int drained; int remaining; int result; for (;;) { remaining = timeout - diffTimestamp(startTs, nowTs); if (remaining <= 0) { #ifdef TEST *logofs << "handleDrain: Timeout raised while draining " << "FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif result = 0; goto ChannelDrainEnd; } #ifdef TEST *logofs << "handleDrain: Trying to write to FD#" << fd_ << " with " << remaining << " Ms " << "remaining.\n" << logofs_flush; #endif drained = transport_ -> drain(limit, remaining); if (drained == 1) { #ifdef TEST *logofs << "handleDrain: Transport for FD#" << fd_ << " drained to " << transport_ -> length() << " bytes at " << strMsTimestamp() << ".\n" << logofs_flush; #endif result = 1; goto ChannelDrainEnd; } else if (drained == 0 && transport_ -> readable() > 0) { #ifdef TEST *logofs << "handleDrain: WARNING! Encoding more data " << "for FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (proxy -> handleAsyncRead(fd_) < 0) { goto ChannelDrainError; } } else if (drained == -1) { goto ChannelDrainError; } nowTs = getNewTimestamp(); if (diffTimestamp(startTs, nowTs) >= control -> ChannelTimeout) { int seconds = (remaining + control -> LatencyTimeout * 10) / 1000; #ifdef WARNING *logofs << "handleDrain: WARNING! Could not drain FD#" << fd_ << " within " << seconds << " seconds.\n" << logofs_flush; #endif cerr << "Warning" << ": Can't write to connection on FD#" << fd_ << " since " << seconds << " seconds.\n"; if (alert_ == 0) { if (control -> ProxyMode == proxy_client) { alert_ = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT; } else { alert_ = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT; } HandleAlert(alert_, 1); } } } ChannelDrainEnd: // // Maybe we drained the channel and are // now out of the congestion state. // handleCongestion(); return result; ChannelDrainError: finish_ = 1; return -1; } int Channel::handleCongestion() { // // Send a begin congestion control code // if the local end of the channel does // not consume its data. // if (isCongested() == 1) { if (congestion_ == 0) { #if defined(TEST) || defined(INFO) *logofs << "handleCongestion: Sending congestion for FD#" << fd_ << " with length " << transport_ -> length() << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif congestion_ = 1; // // Use the callback to send the control // code immediately. // if (proxy -> handleAsyncCongestion(fd_) < 0) { finish_ = 1; return -1; } } } else { // // If the channel was in congestion state // send an end congestion control code. // if (congestion_ == 1) { #if defined(TEST) || defined(INFO) *logofs << "handleCongestion: Sending decongestion for FD#" << fd_ << " with length " << transport_ -> length() << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif congestion_ = 0; if (proxy -> handleAsyncDecongestion(fd_) < 0) { finish_ = 1; return -1; } } // // Remove the "channel unresponsive" // dialog. // if (alert_ != 0) { #if defined(TEST) || defined(INFO) *logofs << "handleCongestion: Displacing the dialog " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif HandleAlert(DISPLACE_MESSAGE_ALERT, 1); } } return 1; } int Channel::handleFlush(T_flush type, int bufferLength, int scratchLength) { if (finish_ == 1) { #ifdef TEST *logofs << "handleFlush: Not flushing data for " << "finishing channel for FD#" << fd_ << ".\n" << logofs_flush; #endif writeBuffer_.fullReset(); return -1; } #ifdef TEST *logofs << "handleFlush: Flushing " << bufferLength << " + " << scratchLength << " bytes " << "to FD#" << fd_ << ".\n" << logofs_flush; #endif // // Check if the channel has data available. // Recent Linux kernels are very picky. // They require that we read often or they // assume that the process is non-interact- // ive. // int result = 0; if (handleAsyncEvents() < 0) { goto ChannelFlushError; } // // Write the data in the main buffer first, // followed by the data in the scratch buffer. // if (bufferLength > 0) { result = transport_ -> write(write_immediate, writeBuffer_.getData(), bufferLength); } if (result >= 0 && scratchLength > 0) { result = transport_ -> write(write_immediate, writeBuffer_.getScratchData(), scratchLength); } if (type == flush_if_any) { writeBuffer_.fullReset(); } else { writeBuffer_.partialReset(); } // // If we failed to write to the X connection then // set the finish flag. The caller should continue // to handle all the remaining messages or it will // corrupt the decode buffer. At the real end, an // error will be propagated to the upper layers // which will perform any needed cleanup. // if (result < 0) { goto ChannelFlushError; } // // Reset transport buffers. // transport_ -> partialReset(); // // Check if the X server has generated // any event in response to our data. // if (handleAsyncEvents() < 0) { goto ChannelFlushError; } // // Check if the channel has entered in // congestion state and, in this case, // send an immediate congestion control // code to the remote. // handleCongestion(); // // We could optionally drain the output // buffer if this is X11 channel. // // if (isCongested() == 1 && isReliable() == 1) // { // if (handleDrain(0, control -> ChannelTimeout) < 0) // { // goto ChannelFlushError; // } // } // return 1; ChannelFlushError: finish_ = 1; return -1; } int Channel::handleFlush() { #ifdef TEST *logofs << "handleFlush: Flushing " << transport_ -> length() << " bytes to FD#" << fd_ << " with descriptor writable.\n" << logofs_flush; #endif // // Check if there is anything to read // before anf after having written to // the socket. // if (handleAsyncEvents() < 0) { goto ChannelFlushError; } if (transport_ -> flush() < 0) { #ifdef TEST *logofs << "handleFlush: Failure detected " << "flushing data to FD#" << fd_ << ".\n" << logofs_flush; #endif goto ChannelFlushError; } if (handleAsyncEvents() < 0) { goto ChannelFlushError; } // // Reset channel's transport buffers. // transport_ -> partialReset(); // // Check if the channel went out of the // congestion state. // handleCongestion(); return 1; ChannelFlushError: finish_ = 1; return -1; } void Channel::handleResetAlert() { if (alert_ != 0) { #ifdef TEST *logofs << "handleResetAlert: The channel alert '" << alert_ << "' was displaced.\n" << logofs_flush; #endif alert_ = 0; } } int Channel::handleCompress(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned int offset, const unsigned char *buffer, const unsigned int size, unsigned char *&compressedData, unsigned int &compressedDataSize) { if (size <= offset) { #ifdef DEBUG *logofs << "handleCompress: Not compressing data for FD#" << fd_ << " as offset is " << offset << " with data size " << size << ".\n" << logofs_flush; #endif return 0; } #ifdef DEBUG *logofs << "handleCompress: Compressing data for FD#" << fd_ << " with data size " << size << " and offset " << offset << ".\n" << logofs_flush; #endif // // It is responsibility of the compressor to // mark the buffer as such if the compression // couldn't take place. // if (compressor_ -> compressBuffer(buffer + offset, size - offset, compressedData, compressedDataSize, encodeBuffer) <= 0) { #ifdef DEBUG *logofs << "handleCompress: Sent " << size - offset << " bytes of plain data for FD#" << fd_ << ".\n" << logofs_flush; #endif return 0; } else { #ifdef DEBUG *logofs << "handleCompress: Sent " << compressedDataSize << " bytes of compressed data for FD#" << fd_ << ".\n" << logofs_flush; #endif return 1; } } int Channel::handleDecompress(DecodeBuffer &decodeBuffer, const unsigned char opcode, const unsigned int offset, unsigned char *buffer, const unsigned int size, const unsigned char *&compressedData, unsigned int &compressedDataSize) { if (size <= offset) { return 0; } int result = compressor_ -> decompressBuffer(buffer + offset, size - offset, compressedData, compressedDataSize, decodeBuffer); if (result < 0) { #ifdef PANIC *logofs << "handleDecompress: PANIC! Failed to decompress " << size - offset << " bytes of data for FD#" << fd_ << " with OPCODE#" << (unsigned int) opcode << ".\n" << logofs_flush; #endif cerr << "Error" << ": Data decompression failed for OPCODE#" << (unsigned int) opcode << ".\n"; return -1; } else if (result == 0) { #ifdef DEBUG *logofs << "handleDecompress: Received " << size - offset << " bytes of plain data for FD#" << fd_ << ".\n" << logofs_flush; #endif return 0; } else { #ifdef DEBUG *logofs << "handleDecompress: Received " << compressedDataSize << " bytes of compressed data for FD#" << fd_ << ".\n" << logofs_flush; #endif return 1; } } int Channel::handleCleanAndNullRequest(unsigned char &opcode, unsigned char *&buffer, unsigned int &size) { #ifdef TEST *logofs << "handleCleanAndNullRequest: Removing the previous data " << "and sending an X_NoOperation " << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) opcode << " (" << DumpOpcode(opcode) << ").\n" << logofs_flush; #endif writeBuffer_.removeMessage(size - 4); size = 4; opcode = X_NoOperation; return 1; } int Channel::handleNullRequest(unsigned char &opcode, unsigned char *&buffer, unsigned int &size) { #ifdef TEST *logofs << "handleNullRequest: Sending an X_NoOperation for FD#" << fd_ << " due to OPCODE#" << (unsigned int) opcode << " (" << DumpOpcode(opcode) << ").\n" << logofs_flush; #endif size = 4; buffer = writeBuffer_.addMessage(size); opcode = X_NoOperation; return 1; } void Channel::handleSplitStoreError(int resource) { if (resource < 0 || resource >= CONNECTIONS_LIMIT) { #ifdef PANIC *logofs << "handleSplitStoreError: PANIC! Resource " << resource << " is out of range with limit " << "set to " << CONNECTIONS_LIMIT << ".\n" << logofs_flush; #endif cerr << "Error" << ": Resource " << resource << " is out of range with limit set to " << CONNECTIONS_LIMIT << ".\n"; HandleCleanup(); } else { #ifdef PANIC *logofs << "handleSplitStoreError: PANIC! Cannot " << "allocate the split store for resource " << resource << ".\n" << logofs_flush; #endif cerr << "Error" << ": Cannot allocate the " << "split store for resource " << resource << ".\n"; HandleCleanup(); } } void Channel::handleSplitStoreAlloc(List *list, int resource) { if (resource < 0 || resource >= CONNECTIONS_LIMIT) { handleSplitStoreError(resource); } if (clientStore_ -> getSplitStore(resource) == NULL) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitStoreAlloc: Allocating a new " << "split store for resource " << resource << ".\n" << logofs_flush; #endif SplitStore *splitStore = clientStore_ -> createSplitStore(resource); if (splitStore == NULL) { handleSplitStoreError(resource); } list -> add(resource); } #if defined(TEST) || defined(SPLIT) else { // // Old proxy versions only use a single // split store. // if (resource != 0) { *logofs << "handleSplitStoreAlloc: WARNING! A split " << "store for resource " << resource << " already exists.\n" << logofs_flush; } } #endif } void Channel::handleSplitStoreRemove(List *list, int resource) { if (resource < 0 || resource >= CONNECTIONS_LIMIT) { handleSplitStoreError(resource); } SplitStore *splitStore = clientStore_ -> getSplitStore(resource); if (splitStore != NULL) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitStoreRemove: Deleting the " << "split store for resource " << resource << ".\n" << logofs_flush; #endif clientStore_ -> destroySplitStore(resource); #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitStoreRemove: Deleting resource " << resource << " from the list " << ".\n" << logofs_flush; #endif list -> remove(resource); } #if defined(TEST) || defined(SPLIT) else { *logofs << "handleSplitStoreRemove: WARNING! A split " << "store for resource " << resource << " does not exist.\n" << logofs_flush; } #endif } Split *Channel::handleSplitCommitRemove(int request, int resource, int position) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitCommitRemove: SPLIT! Checking split " << "commit with resource " << resource << " request " << request << " and position " << position << ".\n" << logofs_flush; #endif // // Remove the split from the split queue. // CommitStore *commitStore = clientStore_ -> getCommitStore(); Split *split = commitStore -> pop(); if (split == NULL) { #ifdef PANIC *logofs << "handleSplitCommitRemove: PANIC! Can't " << "find the split in the commit queue.\n" << logofs_flush; #endif cerr << "Error" << ": Can't find the " << "split in the commit queue.\n"; HandleCleanup(); } #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitCommitRemove: SPLIT! Element from " << "the queue has resource " << split -> getResource() << " request " << split -> getRequest() << " and " << "position " << split -> getPosition() << ".\n" << logofs_flush; #endif if ((control -> isProtoStep7() == 1 && (resource != split -> getResource() || request != split -> getRequest() || position != split -> getPosition())) || (request != split -> getRequest() || position != split -> getPosition())) { #ifdef PANIC *logofs << "handleSplitCommitRemove: PANIC! The data in " << "the split doesn't match the commit request.\n" << logofs_flush; #endif cerr << "Error" << ": The data in the split doesn't " << "match the commit request.\n"; return NULL; } #if defined(TEST) || defined(SPLIT) commitStore -> dump(); #endif return split; } int Channel::setReferences() { #ifdef TEST *logofs << "Channel: Initializing the static " << "members for the base class.\n" << logofs_flush; #endif firstClient_ = -1; fontPort_ = -1; #ifdef REFERENCES references_ = 0; #endif return 1; } int Channel::setOpcodes(OpcodeStore *opcodeStore) { opcodeStore_ = opcodeStore; #ifdef TEST *logofs << "setOpcodes: Propagated opcodes store to channel " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif return 1; } int Channel::setStores(ClientStore *clientStore, ServerStore *serverStore) { clientStore_ = clientStore; serverStore_ = serverStore; #ifdef TEST *logofs << "setStores: Propagated message stores to channel " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif return 1; } int Channel::setCaches(ClientCache *clientCache, ServerCache *serverCache) { clientCache_ = clientCache; serverCache_ = serverCache; #ifdef TEST *logofs << "setCaches: Propagated encode caches to channel " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif return 1; } nxcomp/RenderExtension.h0000644000076400007640000003204011323113030015601 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderExtension_H #define RenderExtension_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Compression of data part is not enabled as // most messages of this type are smaller than // the current data size compression threshold. // #define RENDEREXTENSION_ENABLE_CACHE 1 #define RENDEREXTENSION_ENABLE_DATA 0 #define RENDEREXTENSION_ENABLE_SPLIT 0 #define RENDEREXTENSION_ENABLE_COMPRESS 0 #define RENDEREXTENSION_DATA_LIMIT 6144 #define RENDEREXTENSION_DATA_OFFSET 36 #define RENDEREXTENSION_CACHE_SLOTS 6000 #define RENDEREXTENSION_CACHE_THRESHOLD 20 #define RENDEREXTENSION_CACHE_LOWER_THRESHOLD 10 #define RENDEREXTENSION_CACHE_SLOTS_IF_PROTO_STEP_7 8000 // // Used to build the table of minor opcodes. // #define RENDEREXTENSION_MINOR_OPCODE_LIMIT 256 // // The message class. // class RenderMinorExtensionStore; class RenderExtensionMessage : public Message { friend class RenderExtensionStore; friend class RenderMinorExtensionStore; friend class RenderGenericRequestStore; friend class RenderCreatePictureStore; friend class RenderChangePictureStore; friend class RenderFreePictureStore; friend class RenderPictureClipStore; friend class RenderPictureTransformStore; friend class RenderPictureFilterStore; friend class RenderCreateGlyphSetStore; friend class RenderFreeGlyphSetStore; friend class RenderAddGlyphsStore; friend class RenderCompositeStore; friend class RenderCompositeGlyphsStore; friend class RenderFillRectanglesStore; friend class RenderTrapezoidsStore; friend class RenderTrianglesStore; friend class RenderCreatePictureCompatStore; friend class RenderFreePictureCompatStore; friend class RenderPictureClipCompatStore; friend class RenderCreateGlyphSetCompatStore; friend class RenderCompositeCompatStore; friend class RenderCompositeGlyphsCompatStore; public: RenderExtensionMessage() { } ~RenderExtensionMessage() { } // // We consider for this message a data offset of 36, // that is size of the biggest among all requests of // this extension. The most common requests have a // specific differential encoding, others are simply // encoded through an array of int or char caches. // private: union { struct { unsigned char type; unsigned char char_data[32]; unsigned short short_data[16]; unsigned short long_data[8]; } any; struct { unsigned char type; unsigned int src_id; unsigned int dst_id; unsigned int format; unsigned int mask; } create_picture; struct { unsigned char type; unsigned int src_id; } change_picture; struct { unsigned char type; unsigned int src_id; } free_picture; struct { unsigned char type; unsigned int src_id; unsigned short src_x; unsigned short src_y; } picture_clip; struct { unsigned char type; unsigned int src_id; } picture_transform; struct { unsigned char type; unsigned int src_id; unsigned int num_elm; } picture_filter; struct { unsigned char type; unsigned int set_id; unsigned int format; } create_set; struct { unsigned char type; unsigned int set_id; } free_set; struct { unsigned char type; unsigned int set_id; unsigned int num_elm; } add_glyphs; struct { unsigned char type; unsigned char op; unsigned int src_id; unsigned int msk_id; unsigned int dst_id; unsigned short src_x; unsigned short src_y; unsigned short msk_x; unsigned short msk_y; unsigned short dst_x; unsigned short dst_y; unsigned short width; unsigned short height; } composite; struct { unsigned char type; unsigned char op; unsigned char num_elm; unsigned int src_id; unsigned int dst_id; unsigned int format; unsigned int set_id; unsigned short src_x; unsigned short src_y; unsigned short offset_x; unsigned short offset_y; } composite_glyphs; struct { unsigned char type; unsigned char op; unsigned int dst_id; } fill_rectangles; struct { unsigned char type; unsigned char op; unsigned int src_id; unsigned int dst_id; unsigned int format; unsigned short src_x; unsigned short src_y; } trapezoids; struct { unsigned char type; unsigned char op; unsigned int src_id; unsigned int dst_id; unsigned int format; unsigned short src_x; unsigned short src_y; } triangles; struct { unsigned char type; unsigned char op; unsigned char num_elm; unsigned int src_id; unsigned int dst_id; unsigned int format; unsigned int set_id; unsigned short src_x; unsigned short src_y; unsigned short delta_x; unsigned short delta_y; } composite_glyphs_compat; } data; }; class RenderExtensionStore : public MessageStore { public: RenderExtensionStore(StaticCompressor *compressor); virtual ~RenderExtensionStore(); virtual const char *name() const { return "RenderExtension"; } virtual unsigned char opcode() const { return opcode_; } virtual unsigned int storage() const { return sizeof(RenderExtensionMessage); } // // Message handling methods. // public: virtual Message *create() const { return new RenderExtensionMessage(); } virtual Message *create(const Message &message) const { return new RenderExtensionMessage((const RenderExtensionMessage &) message); } virtual void destroy(Message *message) const { delete (RenderExtensionMessage *) message; } // // Determine if the message must be stored // in the cache. // virtual int validateMessage(const unsigned char *buffer, int size); // // Since protocol step 5 these methods are // specialized in their minor opcode stores. // virtual int identitySize(const unsigned char *buffer, unsigned int size); virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; private: unsigned char opcode_; // // Keep pointers to specialized classes. // RenderMinorExtensionStore *minors_[RENDEREXTENSION_MINOR_OPCODE_LIMIT]; RenderMinorExtensionStore *generic_; }; class RenderMinorExtensionStore : public MinorMessageStore { public: virtual const char *name() const = 0; virtual int identitySize(const unsigned char *buffer, unsigned int size) = 0; virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const = 0; virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, unsigned char type, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const = 0; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const = 0; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const = 0; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const = 0; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const = 0; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, md5_state_t *md5_state, int bigEndian) const = 0; // // Internal encode and decode utilities. // protected: void encodeLongData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const; void encodeIntData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const; void encodeCharData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const; void decodeLongData(DecodeBuffer &decodeBuffer, unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const; void decodeIntData(DecodeBuffer &decodeBuffer, unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const; void decodeCharData(DecodeBuffer &decodeBuffer, unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian, ChannelCache *channelCache) const; /* * The following methods are only used in the * encoding of the generic render request. To * be removed in future. */ void parseIntData(const Message *message, const unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian) const; void unparseIntData(const Message *message, unsigned char *buffer, unsigned int offset, unsigned int size, int bigEndian) const; void updateIntData(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, unsigned int offset, unsigned int size, ChannelCache *channelCache) const; void updateIntData(DecodeBuffer &decodeBuffer, const Message *message, unsigned int offset, unsigned int size, ChannelCache *channelCache) const; }; #endif /* RenderExtension_H */ nxcomp/SequenceQueue.cpp0000644000076400007640000000754511323113030015611 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "SequenceQueue.h" static const unsigned int INITIAL_SIZE_ = 16; static const unsigned int GROWTH_INCREMENT = 16; SequenceQueue::SequenceQueue() : queue_(new RequestSequence[INITIAL_SIZE_]), size_(INITIAL_SIZE_), length_(0), start_(0), end_(0) { } SequenceQueue::~SequenceQueue() { delete [] queue_; } void SequenceQueue::push(unsigned short int sequence, unsigned char opcode, unsigned int data1, unsigned int data2, unsigned int data3) { if (length_ == 0) { start_ = end_ = 0; queue_[0].opcode = opcode; queue_[0].sequence = sequence; queue_[0].data1 = data1; queue_[0].data2 = data2; queue_[0].data3 = data3; length_ = 1; return; } if (length_ == size_) { size_ += GROWTH_INCREMENT; RequestSequence *newQueue = new RequestSequence[size_]; for (int i = start_; (unsigned int) i < length_; i++) { newQueue[i - start_] = queue_[i]; } for (int i1 = 0; (unsigned int) i1 < start_; i1++) { newQueue[i1 + length_ - start_] = queue_[i1]; } delete [] queue_; queue_ = newQueue; start_ = 0; end_ = length_ - 1; } end_++; if (end_ == size_) { end_ = 0; } queue_[end_].opcode = opcode; queue_[end_].sequence = sequence; queue_[end_].data1 = data1; queue_[end_].data2 = data2; queue_[end_].data3 = data3; length_++; } int SequenceQueue::peek(unsigned short int &sequence, unsigned char &opcode) { if (length_ == 0) { return 0; } else { opcode = queue_[start_].opcode; sequence = queue_[start_].sequence; return 1; } } int SequenceQueue::peek(unsigned short int &sequence, unsigned char &opcode, unsigned int &data1, unsigned int &data2, unsigned int &data3) { if (length_ == 0) { return 0; } else { opcode = queue_[start_].opcode; sequence = queue_[start_].sequence; data1 = queue_[start_].data1; data2 = queue_[start_].data2; data3 = queue_[start_].data3; return 1; } } int SequenceQueue::pop(unsigned short int &sequence, unsigned char &opcode, unsigned int &data1, unsigned int &data2, unsigned int &data3) { if (length_ == 0) { return 0; } else { opcode = queue_[start_].opcode; sequence = queue_[start_].sequence; data1 = queue_[start_].data1; data2 = queue_[start_].data2; data3 = queue_[start_].data3; start_++; if (start_ == size_) { start_ = 0; } length_--; return 1; } } nxcomp/Rgb.cpp0000644000076400007640000000621711323113026013546 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Misc.h" #include "Rgb.h" #define PANIC #define WARNING #undef TEST #undef DEBUG int UnpackRgb(T_geometry *geometry, unsigned char method, unsigned char *src_data, int src_size, int dst_bpp, int dst_width, int dst_height, unsigned char *dst_data, int dst_size) { if (*src_data == 0) { if (dst_size != src_size - 1) { #ifdef TEST *logofs << "UnpackRgb: PANIC! Invalid destination size " << dst_size << " with source " << src_size << ".\n" << logofs_flush; #endif return -1; } #ifdef TEST *logofs << "UnpackRgb: Expanding " << src_size - 1 << " bytes of plain RGB data.\n" << logofs_flush; #endif memcpy(dst_data, src_data + 1, src_size - 1); return 1; } unsigned int check_size = dst_size; int result = ZDecompress(&unpackStream, dst_data, &check_size, src_data + 1, src_size - 1); if (result != Z_OK) { #ifdef PANIC *logofs << "UnpackRgb: PANIC! Failure decompressing RGB data. " << "Error is '" << zError(result) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failure decompressing RGB data. " << "Error is '" << zError(result) << "'.\n"; return -1; } else if (check_size != (unsigned int) dst_size) { #ifdef PANIC *logofs << "UnpackRgb: PANIC! Size mismatch in RGB data. " << "Resulting size is " << check_size << " with " << "expected size " << dst_size << ".\n" << logofs_flush; #endif cerr << "Error" << ": Size mismatch in RGB data. " << "Resulting size is " << check_size << " with " << "expected size " << dst_size << ".\n"; return -1; } #ifdef TEST *logofs << "UnpackRgb: Decompressed " << src_size - 1 << " bytes to " << dst_size << " bytes of RGB data.\n" << logofs_flush; #endif return 1; } nxcomp/RenderGenericRequest.cpp0000644000076400007640000002045711323113026017123 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "NXrender.h" #include "RenderExtension.h" #include "RenderGenericRequest.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Here are the methods to handle the messages' content. // int RenderGenericRequestStore::encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message.\n" << logofs_flush; unsigned char type = *(buffer + 1); #endif encodeBuffer.encodeCachedValue(size >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef DEBUG *logofs << name() << ": Encoding full unhandled message. " << "Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif encodeIntData(encodeBuffer, buffer, 4, size, bigEndian, clientCache); #ifdef DEBUG *logofs << name() << ": Encoded full message.\n" << logofs_flush; #endif return 1; } int RenderGenericRequestStore::decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, unsigned char type, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); size <<= 2; buffer = writeBuffer -> addMessage(size); *(buffer + 1) = type; #ifdef DEBUG *logofs << name() << ": Decoding full unhandled message. " << "Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif decodeIntData(decodeBuffer, buffer, 4, size, bigEndian, clientCache); #ifdef DEBUG *logofs << name() << ": Decoded full message.\n" << logofs_flush; #endif return 1; } void RenderGenericRequestStore::encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int size, int bigEndian, ChannelCache *channelCache) const { } void RenderGenericRequestStore::decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, unsigned int size, int bigEndian, ChannelCache *channelCache) const { } int RenderGenericRequestStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { #ifdef DEBUG *logofs << name() << ": Parsing identity for message at " << this << ".\n" << logofs_flush; #endif RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; unsigned char type = *(buffer + 1); renderExtension -> data.any.type = type; #ifdef DEBUG *logofs << name() << ": Parsing unhandled identity. " << "Type is " << (unsigned int) renderExtension -> data.any.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif parseIntData(message, buffer, 4, size, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int RenderGenericRequestStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { #ifdef DEBUG *logofs << name() << ": Unparsing identity for message at " << this << ".\n" << logofs_flush; #endif RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; unsigned char type = renderExtension -> data.any.type; *(buffer + 1) = type; #ifdef DEBUG *logofs << name() << ": Unparsing unhandled identity. " << "Type is " << (unsigned int) renderExtension -> data.any.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif unparseIntData(message, buffer, 4, size, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void RenderGenericRequestStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, md5_state_t *md5_state, int bigEndian) const { // // Include the minor opcode in the checksum. // Because the data offset can be beyond the // real end of the message, we need to include // the size or we will match any message whose // size is less or equal to the data offset. // md5_append(md5_state, buffer + 1, 3); } void RenderGenericRequestStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { // // Encode the variant part. // #ifdef DEBUG *logofs << name() << ": Updating identity for message at " << this << ".\n" << logofs_flush; #endif RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; #ifdef DEBUG *logofs << name() << ": Encoding unhandled update. " << "Type is " << (unsigned int) renderExtension -> data.any.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif updateIntData(encodeBuffer, message, cachedMessage, 4, renderExtension -> size_, channelCache); #ifdef DEBUG *logofs << name() << ": Updated identity for message at " << this << ".\n" << logofs_flush; #endif } void RenderGenericRequestStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { #ifdef DEBUG *logofs << name() << ": Updating identity for message at " << this << ".\n" << logofs_flush; #endif RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; #ifdef DEBUG *logofs << name() << ": Decoding unhandled update. " << "Type is " << (unsigned int) renderExtension -> data.any.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif updateIntData(decodeBuffer, message, 4, renderExtension -> size_, channelCache); #ifdef DEBUG *logofs << name() << ": Updated identity for message at " << this << ".\n" << logofs_flush; #endif } nxcomp/Bitmap.h0000644000076400007640000000304311323113031013703 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Bitmap_H #define Bitmap_H #include "Unpack.h" int UnpackBitmap(T_geometry *geometry, unsigned char method, unsigned char *src_data, int src_size, int dst_bpp, int dst_width, int dst_height, unsigned char *dst_data, int dst_size); #endif /* Bitmap_H */ nxcomp/ChangeProperty.cpp0000644000076400007640000001377011323113031015764 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ChangeProperty.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int ChangePropertyStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message; changeProperty -> mode = *(buffer + 1); changeProperty -> format = *(buffer + 16); changeProperty -> window = GetULONG(buffer + 4, bigEndian); changeProperty -> property = GetULONG(buffer + 8, bigEndian); changeProperty -> type = GetULONG(buffer + 12, bigEndian); changeProperty -> length = GetULONG(buffer + 20, bigEndian); // // Cleanup the padding bytes. // unsigned int uiFormat; unsigned int uiLengthInBytes; if ((int) size > CHANGEPROPERTY_DATA_OFFSET) { uiFormat = *(buffer + 16); uiLengthInBytes = changeProperty -> length; #ifdef DEBUG *logofs << name() << ": length " << uiLengthInBytes << ", format " << uiFormat << ", size " << size << ".\n" << logofs_flush; #endif if (uiFormat == 16) { uiLengthInBytes <<= 1; } else if (uiFormat == 32) { uiLengthInBytes <<= 2; } unsigned char *end = ((unsigned char *) buffer) + size; unsigned char *pad = ((unsigned char *) buffer) + CHANGEPROPERTY_DATA_OFFSET + uiLengthInBytes; CleanData((unsigned char *) pad, end - pad); } #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int ChangePropertyStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message; *(buffer + 1) = changeProperty -> mode; *(buffer + 16) = changeProperty -> format; PutULONG(changeProperty -> window, buffer + 4, bigEndian); PutULONG(changeProperty -> property, buffer + 8, bigEndian); PutULONG(changeProperty -> type, buffer + 12, bigEndian); PutULONG(changeProperty -> length, buffer + 20, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void ChangePropertyStore::dumpIdentity(const Message *message) const { #ifdef DUMP ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message; *logofs << name() << ": Identity mode " << (unsigned int) changeProperty -> mode << ", format " << (unsigned int) changeProperty -> format << ", window " << changeProperty -> window << ", property " << changeProperty -> property << ", type " << changeProperty -> type << ", length " << changeProperty -> length << ", size " << changeProperty -> size_ << ".\n" << logofs_flush; #endif } void ChangePropertyStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 1, 1); md5_append(md5_state_, buffer + 16, 1); md5_append(md5_state_, buffer + 8, 4); md5_append(md5_state_, buffer + 12, 4); md5_append(md5_state_, buffer + 20, 4); } void ChangePropertyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message; ChangePropertyMessage *cachedChangeProperty = (ChangePropertyMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << changeProperty -> window << " as window field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(changeProperty -> window, clientCache -> windowCache); cachedChangeProperty -> window = changeProperty -> window; } void ChangePropertyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> windowCache); changeProperty -> window = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << changeProperty -> window << " as window field.\n" << logofs_flush; #endif } nxcomp/IntCache.h0000644000076400007640000000554211323113027014160 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef IntCache_H #define IntCache_H class IntCache { public: IntCache(unsigned int size); ~IntCache() { delete [] buffer_; } unsigned int getSize() const { return length_; } int lookup(unsigned int &value, unsigned int &index, unsigned int mask, unsigned int &sameDiff); // // This can be inlined as it is only // called by decodeCachedValue(). // unsigned int get(unsigned int index) { unsigned int result = buffer_[index]; if (index != 0) { // // Using a memmove() appears to be slower. // // unsigned int target = index >> 1; // // memmove((char *) &buffer_[target + 1], (char *) &buffer_[target], // sizeof(unsigned int) * (index - target)); // // buffer_[target] = result; // unsigned int i = index; unsigned int target = (i >> 1); do { buffer_[i] = buffer_[i - 1]; i--; } while (i > target); buffer_[target] = result; } return result; } void insert(unsigned int &value, unsigned int mask); void push(unsigned int &value, unsigned int mask); void dump(); unsigned int getLastDiff(unsigned int mask) const { return lastDiff_; } unsigned int getBlockSize(unsigned int bits) const { if (bits > 0) { return (predictedBlockSize_ < bits ? predictedBlockSize_ : bits); } return predictedBlockSize_; } private: unsigned int size_; unsigned int length_; unsigned int *buffer_; unsigned int lastDiff_; unsigned int lastValueInserted_; unsigned int predictedBlockSize_; }; #endif /* IntCache_H */ nxcomp/SetUnpackGeometry.h0000644000076400007640000001124011323113027016103 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef SetUnpackGeometry_H #define SetUnpackGeometry_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define SETUNPACKGEOMETRY_ENABLE_CACHE 1 #define SETUNPACKGEOMETRY_ENABLE_DATA 0 #define SETUNPACKGEOMETRY_ENABLE_SPLIT 0 #define SETUNPACKGEOMETRY_ENABLE_COMPRESS 0 #define SETUNPACKGEOMETRY_DATA_LIMIT 24 #define SETUNPACKGEOMETRY_DATA_OFFSET 24 #define SETUNPACKGEOMETRY_CACHE_SLOTS 20 #define SETUNPACKGEOMETRY_CACHE_THRESHOLD 1 #define SETUNPACKGEOMETRY_CACHE_LOWER_THRESHOLD 0 // // The message class. // class SetUnpackGeometryMessage : public Message { friend class SetUnpackGeometryStore; public: SetUnpackGeometryMessage() { } ~SetUnpackGeometryMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char client; unsigned char depth_1_bpp; unsigned char depth_4_bpp; unsigned char depth_8_bpp; unsigned char depth_16_bpp; unsigned char depth_24_bpp; unsigned char depth_32_bpp; unsigned int red_mask; unsigned int green_mask; unsigned int blue_mask; }; class SetUnpackGeometryStore : public MessageStore { public: SetUnpackGeometryStore(StaticCompressor *compressor); virtual ~SetUnpackGeometryStore(); virtual const char *name() const { return "SetUnpackGeometry"; } virtual unsigned char opcode() const { return X_NXSetUnpackGeometry; } virtual unsigned int storage() const { return sizeof(SetUnpackGeometryMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new SetUnpackGeometryMessage(); } virtual Message *create(const Message &message) const { return new SetUnpackGeometryMessage((const SetUnpackGeometryMessage &) message); } virtual void destroy(Message *message) const { delete (SetUnpackGeometryMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* SetUnpackGeometry_H */ nxcomp/RenderCreateGlyphSet.cpp0000644000076400007640000001343311323113027017056 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderCreateGlyphSet.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> lastId, clientCache -> lastIdCache, clientCache -> renderGlyphSetCache, clientCache -> renderFreeGlyphSetCache); encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 32, clientCache -> renderFormatCache); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeNewXidValue(value, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> renderGlyphSetCache, clientCache -> renderFreeGlyphSetCache); PutULONG(value, buffer + 4, bigEndian); decodeBuffer.decodeCachedValue(value, 32, clientCache -> renderFormatCache); PutULONG(value, buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.create_set.type = *(buffer + 1); renderExtension -> data.create_set.set_id = GetULONG(buffer + 4, bigEndian); renderExtension -> data.create_set.format = GetULONG(buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.create_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.create_set.type; PutULONG(renderExtension -> data.create_set.set_id, buffer + 4, bigEndian); PutULONG(renderExtension -> data.create_set.format, buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.create_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); md5_append(md5_state, buffer + 8, 4); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeNewXidValue(renderExtension -> data.create_set.set_id, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> renderGlyphSetCache, clientCache -> renderFreeGlyphSetCache); cachedRenderExtension -> data.create_set.set_id = renderExtension -> data.create_set.set_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.create_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeNewXidValue(renderExtension -> data.create_set.set_id, clientCache -> lastId, clientCache -> lastIdCache, clientCache -> renderGlyphSetCache, clientCache -> renderFreeGlyphSetCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.create_set.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/TranslateCoords.cpp0000644000076400007640000000723211323113027016142 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "TranslateCoords.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int TranslateCoordsStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message; // // Here is the fingerprint. // translateCoords -> src_window = GetULONG(buffer + 4, bigEndian); translateCoords -> dst_window = GetULONG(buffer + 8, bigEndian); translateCoords -> src_x = GetUINT(buffer + 12, bigEndian); translateCoords -> src_y = GetUINT(buffer + 14, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int TranslateCoordsStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message; // // Fill all the message's fields. // PutULONG(translateCoords -> src_window, buffer + 4, bigEndian); PutULONG(translateCoords -> dst_window, buffer + 8, bigEndian); PutUINT(translateCoords -> src_x, buffer + 12, bigEndian); PutUINT(translateCoords -> src_y, buffer + 14, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void TranslateCoordsStore::dumpIdentity(const Message *message) const { #ifdef DUMP TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message; *logofs << name() << ": Identity src_window " << translateCoords -> src_window << ", dst_window " << translateCoords -> dst_window << ", src_x " << translateCoords -> src_x << ", src_y " << translateCoords -> src_y << ", size " << translateCoords -> size_ << ".\n" << logofs_flush; #endif } void TranslateCoordsStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 4, 4); md5_append(md5_state_, buffer + 8, 4); md5_append(md5_state_, buffer + 12, 2); md5_append(md5_state_, buffer + 14, 2); } nxcomp/RenderPictureFilter.h0000644000076400007640000000465511323113026016426 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderPictureFilter_H #define RenderPictureFilter_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderPictureFilter" #undef MESSAGE_STORE #define MESSAGE_STORE RenderPictureFilterStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 12 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderPictureFilter_H */ nxcomp/Children.cpp0000644000076400007640000005151611342773403014602 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include "NX.h" #include "Misc.h" #include "Types.h" #include "Timestamp.h" #include "Control.h" #include "Statistics.h" #include "Proxy.h" #include "Keeper.h" #include "Fork.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #define DISPLAY_LENGTH_LIMIT 256 #define DEFAULT_STRING_LIMIT 512 // // These are from the main loop. // extern Keeper *keeper; extern int (*handler)(int); extern int useUnixSocket; extern int lastDialog; extern int lastWatchdog; extern int lastKeeper; extern void CleanupListeners(); extern void CleanupSockets(); extern void CleanupAgent(); extern void CleanupGlobal(); extern void InstallSignals(); extern char *GetClientPath(); extern int CheckParent(const char *name, const char *type, int parent); #ifdef __sun extern char **environ; #endif // // Close all the unused descriptors and // install any signal handler that might // have been disabled in the main process. // static void SystemCleanup(const char *name); // // Release all objects allocated in the // heap. static void MemoryCleanup(const char *name); // // Remove 'name' from the environment. // static int UnsetEnv(const char *name); static int NXTransKeeperHandler(int signal); static void NXTransKeeperCheck(); // // Start a nxclient process in dialog mode. // int NXTransDialog(const char *caption, const char *message, const char *window, const char *type, int local, const char* display) { // // Be sure log file is valid. // if (logofs == NULL) { logofs = &cerr; } int pid; #ifdef TEST *logofs << "NXTransDialog: Going to fork with NX pid '" << getpid() << "'.\n" << logofs_flush; #endif pid = Fork(); if (pid != 0) { if (pid < 0) { #ifdef TEST *logofs << "NXTransDialog: WARNING! Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; } #ifdef TEST else { *logofs << "NXTransDialog: Created NX dialog process " << "with pid '" << pid << "'.\n" << logofs_flush; } #endif return pid; } #ifdef TEST *logofs << "NXTransDialog: Executing child with pid '" << getpid() << "' and parent '" << getppid() << "'.\n" << logofs_flush; #endif SystemCleanup("NXTransDialog"); // // Copy the client command before // freeing up the control class. // char command[DEFAULT_STRING_LIMIT]; if (control != NULL) { strcpy(command, control -> ClientPath); } else { char *path = GetClientPath(); strcpy(command, path); delete [] path; } // // Get rid of the unused resources. // MemoryCleanup("NXTransDialog"); #ifdef TEST *logofs << "NXTransDialog: Running external NX dialog with caption '" << caption << "' message '" << message << "' type '" << type << "' local '" << local << "' display '" << display << "'.\n" << logofs_flush; #endif int pulldown = (strcmp(type, "pulldown") == 0); char parent[DEFAULT_STRING_LIMIT]; snprintf(parent, DEFAULT_STRING_LIMIT, "%d", getppid()); parent[DEFAULT_STRING_LIMIT - 1] = '\0'; UnsetEnv("LD_LIBRARY_PATH"); for (int i = 0; i < 2; i++) { if (local != 0) { if (pulldown) { execlp(command, command, "--dialog", type, "--caption", caption, "--window", window, "--local", "--parent", parent, "--display", display, NULL); } else { execlp(command, command, "--dialog", type, "--caption", caption, "--message", message, "--local", "--parent", parent, "--display", display, NULL); } } else { if (pulldown) { execlp(command, command, "--dialog", type, "--caption", caption, "--window", window, "--parent", parent, "--display", display, NULL); } else { execlp(command, command, "--dialog", type, "--caption", caption, "--message", message, "--parent", parent, "--display", display, NULL); } } #ifdef WARNING *logofs << "NXTransDialog: WARNING! Couldn't start '" << command << "'. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Couldn't start '" << command << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"; // // Retry by looking for the default name // in the default NX path. // strcpy(command, "nxclient"); char newPath[DEFAULT_STRING_LIMIT]; strcpy(newPath, "/usr/NX/bin:/opt/NX/bin:/usr/local/NX/bin:"); #ifdef __APPLE__ strcat(newPath, "/Applications/NX Client for OSX.app/Contents/MacOS:"); #endif #ifdef __CYGWIN32__ strcat(newPath, ".:"); #endif int newLength = strlen(newPath); char *oldPath = getenv("PATH"); strncpy(newPath + newLength, oldPath, DEFAULT_STRING_LIMIT - newLength - 1); newPath[DEFAULT_STRING_LIMIT - 1] = '\0'; #ifdef WARNING *logofs << "NXTransDialog: WARNING! Trying with path '" << newPath << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Trying with path '" << newPath << "'.\n"; // // Solaris doesn't seem to have // function setenv(). // #ifdef __sun char newEnv[DEFAULT_STRING_LIMIT + 5]; sprintf(newEnv,"PATH=%s", newPath); putenv(newEnv); #else setenv("PATH", newPath, 1); #endif } // // Hopefully useless. // exit(0); } // // Start a nxclient process in dialog mode. // int NXTransClient(const char* display) { // // Be sure log file is valid. // if (logofs == NULL) { logofs = &cerr; } int pid; #ifdef TEST *logofs << "NXTransClient: Going to fork with NX pid '" << getpid() << "'.\n" << logofs_flush; #endif pid = Fork(); if (pid != 0) { if (pid < 0) { #ifdef TEST *logofs << "NXTransClient: WARNING! Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; } #ifdef TEST else { *logofs << "NXTransClient: Created NX client process " << "with pid '" << pid << "'.\n" << logofs_flush; } #endif return pid; } #ifdef TEST *logofs << "NXTransClient: Executing child with pid '" << getpid() << "' and parent '" << getppid() << "'.\n" << logofs_flush; #endif SystemCleanup("NXTransClient"); // // Copy the client command before // freeing up the control class. // char command[DEFAULT_STRING_LIMIT]; if (control != NULL) { strcpy(command, control -> ClientPath); } else { char *path = GetClientPath(); strcpy(command, path); delete [] path; } // // Get rid of unused resources. // MemoryCleanup("NXTransClient"); #ifdef TEST *logofs << "NXTransClient: Running external NX client with display '" << display << "'.\n" << logofs_flush; #endif // // Provide the display in the environment. // char newDisplay[DISPLAY_LENGTH_LIMIT]; #ifdef __sun snprintf(newDisplay, DISPLAY_LENGTH_LIMIT - 1, "DISPLAY=%s", display); newDisplay[DISPLAY_LENGTH_LIMIT - 1] = '\0'; putenv(newDisplay); #else strncpy(newDisplay, display, DISPLAY_LENGTH_LIMIT - 1); newDisplay[DISPLAY_LENGTH_LIMIT - 1] = '\0'; setenv("DISPLAY", newDisplay, 1); #endif UnsetEnv("LD_LIBRARY_PATH"); for (int i = 0; i < 2; i++) { execlp(command, command, NULL); #ifdef WARNING *logofs << "NXTransClient: WARNING! Couldn't start '" << command << "'. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Couldn't start '" << command << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"; // // Retry by looking for the default name // in the default NX path. // strcpy(command, "nxclient"); char newPath[DEFAULT_STRING_LIMIT]; strcpy(newPath, "/usr/NX/bin:/opt/NX/bin:/usr/local/NX/bin:"); #ifdef __APPLE__ strcat(newPath, "/Applications/NX Client for OSX.app/Contents/MacOS:"); #endif #ifdef __CYGWIN32__ strcat(newPath, ".:"); #endif int newLength = strlen(newPath); char *oldPath = getenv("PATH"); strncpy(newPath + newLength, oldPath, DEFAULT_STRING_LIMIT - newLength - 1); newPath[DEFAULT_STRING_LIMIT - 1] = '\0'; #ifdef WARNING *logofs << "NXTransClient: WARNING! Trying with path '" << newPath << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Trying with path '" << newPath << "'.\n"; // // Solaris doesn't seem to have // function setenv(). // #ifdef __sun char newEnv[DEFAULT_STRING_LIMIT + 5]; sprintf(newEnv,"PATH=%s", newPath); putenv(newEnv); #else setenv("PATH", newPath, 1); #endif } // // Hopefully useless. // exit(0); } // // Wait until the timeout is expired. // The timeout is expressed in milli- // seconds. // int NXTransWatchdog(int timeout) { // // Be sure log file is valid. // if (logofs == NULL) { logofs = &cerr; } int pid; #ifdef TEST *logofs << "NXTransWatchdog: Going to fork with NX pid '" << getpid() << "'.\n" << logofs_flush; #endif pid = Fork(); if (pid != 0) { if (pid < 0) { #ifdef TEST *logofs << "NXTransWatchdog: WARNING! Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; } #ifdef TEST else { *logofs << "NXTransWatchdog: Created NX watchdog process " << "with pid '" << pid << "'.\n" << logofs_flush; } #endif return pid; } int parent = getppid(); #ifdef TEST *logofs << "NXTransWatchdog: Executing child with pid '" << getpid() << "' and parent '" << parent << "'.\n" << logofs_flush; #endif SystemCleanup("NXTransWatchdog"); // // Get rid of unused resources. // MemoryCleanup("NXTransWatchdog"); // // Run until the timeout is expired // or forever, if no timeout is // provided. // T_timestamp startTs = getTimestamp(); int diffTs = 0; for (;;) { // // Complain if the parent is dead. // if (CheckParent("NXTransWatchdog", "watchdog", parent) == 0) { #ifdef TEST *logofs << "NXTransWatchdog: Exiting with no parent " << "running.\n" << logofs_flush; #endif HandleCleanup(); } if (timeout > 0) { if (diffTs >= timeout) { #ifdef TEST *logofs << "NXTransWatchdog: Timeout of " << timeout << " Ms raised in watchdog.\n" << logofs_flush; #endif // // We will just exit. Our parent should be // monitoring us and detect that the process // is gone. // HandleCleanup(); } } if (timeout > 0) { #ifdef TEST *logofs << "NXTransWatchdog: Waiting for the timeout " << "with " << timeout - diffTs << " Ms to run.\n" << logofs_flush; #endif usleep((timeout - diffTs) * 1000); diffTs = diffTimestamp(startTs, getNewTimestamp()); } else { #ifdef TEST *logofs << "NXTransWatchdog: Waiting for a signal.\n" << logofs_flush; #endif sleep(10); } } // // Hopefully useless. // exit(0); } int NXTransKeeperHandler(int signal) { if (keeper != NULL) { switch (signal) { case SIGTERM: case SIGINT: case SIGHUP: { #ifdef TEST *logofs << "NXTransKeeperHandler: Requesting giveup " << "because of signal " << signal << " ,'" << DumpSignal(signal) << "'.\n" << logofs_flush; #endif keeper -> setSignal(signal); return 0; } } } return 1; } void NXTransKeeperCheck() { if (CheckParent("NXTransKeeper", "keeper", keeper -> getParent()) == 0 || keeper -> getSignal() != 0) { #ifdef TEST *logofs << "NXTransKeeperCheck: Exiting because of signal " << "or no parent running.\n" << logofs_flush; #endif HandleCleanup(); } } int NXTransKeeper(int caches, int images, const char *root) { // // Be sure log file is valid. // if (logofs == NULL) { logofs = &cerr; } if (caches == 0 && images == 0) { #ifdef TEST *logofs << "NXTransKeeper: No NX cache house-keeping needed.\n" << logofs_flush; #endif return 0; } int pid; #ifdef TEST *logofs << "NXTransKeeper: Going to fork with NX pid '" << getpid() << "'.\n" << logofs_flush; #endif pid = Fork(); if (pid != 0) { if (pid < 0) { #ifdef TEST *logofs << "NXTransKeeper: WARNING! Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Function fork failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; } #ifdef TEST else { *logofs << "NXTransKeeper: Created NX keeper process " << "with pid '" << pid << "'.\n" << logofs_flush; } #endif return pid; } int parent = getppid(); #ifdef TEST *logofs << "NXTransKeeper: Executing child with pid '" << getpid() << "' and parent '" << parent << "'.\n" << logofs_flush; #endif SystemCleanup("NXTransKeeper"); #ifdef TEST *logofs << "NXTransKeeper: Going to run with caches " << caches << " images " << images << " and root " << root << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif // // Create the house-keeper class. // int timeout = control -> KeeperTimeout; keeper = new Keeper(caches, images, root, 100, parent); handler = NXTransKeeperHandler; if (keeper == NULL) { #ifdef PANIC *logofs << "NXTransKeeper: PANIC! Failed to create the keeper object.\n" << logofs_flush; #endif cerr << "Error" << ": Failed to create the keeper object.\n"; HandleCleanup(); } // // Get rid of unused resources. Root path // must be copied in keeper's constructor // before control is deleted. // MemoryCleanup("NXTransKeeper"); // // Decrease the priority of this process. // // The following applies to Cygwin: "Cygwin processes can be // set to IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, HIGH_- // PRIORITY_CLASS, or REALTIME_PRIORITY_CLASS with the nice // call. If you pass a positive number to nice(), then the // priority level will decrease by one (within the above list // of priorities). A negative number would make it increase // by one. It is not possible to change it by more than one // at a time without making repeated calls". // if (nice(5) < 0 && errno != 0) { #ifdef WARNING *logofs << "NXTransKeeper: WARNING! Failed to renice process to +5. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Failed to renice process to +5. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; } // // Delay a bit the first run to give // a boost to the session startup. // #ifdef TEST *logofs << "NXTransKeeper: Going to sleep for " << timeout / 20 << " Ms.\n" << logofs_flush; #endif usleep(timeout / 20 * 1000); NXTransKeeperCheck(); // // The house keeping of the persistent // caches is performed only once. // if (caches != 0) { #ifdef TEST *logofs << "NXTransKeeper: Going to cleanup the NX cache " << "directories at " << strMsTimestamp() << ".\n" << logofs_flush; #endif keeper -> cleanupCaches(); #ifdef TEST *logofs << "NXTransKeeper: Completed cleanup of NX cache " << "directories at " << strMsTimestamp() << ".\n" << logofs_flush; #endif } #ifdef TEST else { *logofs << "NXTransKeeper: Nothing to do for the " << "persistent caches.\n" << logofs_flush; } #endif if (images == 0) { #ifdef TEST *logofs << "NXTransKeeper: Nothing to do for the " << "persistent images.\n" << logofs_flush; #endif HandleCleanup(); } // // Take care of the persisten image cache. // Run a number of iterations and then exit, // so we can keep the memory consumption // low. The parent will check our exit code // and will eventually restart us. // for (int iterations = 0; iterations < 100; iterations++) { #ifdef TEST *logofs << "NXTransKeeper: Running iteration " << iterations << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif NXTransKeeperCheck(); #ifdef TEST *logofs << "NXTransKeeper: Going to cleanup the NX images " << "directories at " << strMsTimestamp() << ".\n" << logofs_flush; #endif if (keeper -> cleanupImages() < 0) { #ifdef TEST *logofs << "NXTransKeeper: Exiting because of error " << "handling the image cache.\n" << logofs_flush; #endif HandleCleanup(); } #ifdef TEST *logofs << "NXTransKeeper: Completed cleanup of NX images " << "directories at " << strMsTimestamp() << ".\n" << logofs_flush; #endif NXTransKeeperCheck(); #ifdef TEST *logofs << "NXTransKeeper: Going to sleep for " << timeout << " Ms.\n" << logofs_flush; #endif usleep(timeout * 1000); } HandleCleanup(2); // // Hopefully useless. // exit(0); } void SystemCleanup(const char *name) { #ifdef TEST *logofs << name << ": Performing system cleanup in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif // // Reinstall signals that might // have been restored by agents. // InstallSignals(); } void MemoryCleanup(const char *name) { #ifdef TEST *logofs << name << ": Performing memory cleanup in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif DisableSignals(); // // Prevent deletion of unix socket // and lock file. // useUnixSocket = 0; // // Don't let cleanup kill other // children. // lastDialog = 0; lastWatchdog = 0; lastKeeper = 0; CleanupListeners(); CleanupSockets(); CleanupGlobal(); EnableSignals(); } int UnsetEnv(const char *name) { int result; #ifdef __sun char **pEnv = environ; int nameLen = strlen(name) + 1; char *varName = new char[nameLen + 1]; strcpy(varName, name); strcat(varName, "="); pEnv = environ; while (*pEnv != NULL) { if (!strncmp(varName, *pEnv, nameLen)) { break; } *pEnv++; } while (*pEnv != NULL) { *pEnv = *(pEnv + 1); pEnv++; } result = 0; #else #ifdef __APPLE__ unsetenv(name); result = 0; #else result = unsetenv(name); #endif #endif return result; } nxcomp/PolySegment.h0000644000076400007640000001122211323113030014732 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PolySegment_H #define PolySegment_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define POLYSEGMENT_ENABLE_CACHE 1 #define POLYSEGMENT_ENABLE_DATA 0 #define POLYSEGMENT_ENABLE_SPLIT 0 #define POLYSEGMENT_ENABLE_COMPRESS 0 #define POLYSEGMENT_DATA_LIMIT 8192 #define POLYSEGMENT_DATA_OFFSET 12 #define POLYSEGMENT_CACHE_SLOTS 3000 #define POLYSEGMENT_CACHE_THRESHOLD 5 #define POLYSEGMENT_CACHE_LOWER_THRESHOLD 1 // // The message class. // class PolySegmentMessage : public Message { friend class PolySegmentStore; public: PolySegmentMessage() { } ~PolySegmentMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int drawable; unsigned int gcontext; }; class PolySegmentStore : public MessageStore { // // Constructors and destructors. // public: PolySegmentStore() : MessageStore() { enableCache = POLYSEGMENT_ENABLE_CACHE; enableData = POLYSEGMENT_ENABLE_DATA; enableSplit = POLYSEGMENT_ENABLE_SPLIT; enableCompress = POLYSEGMENT_ENABLE_COMPRESS; dataLimit = POLYSEGMENT_DATA_LIMIT; dataOffset = POLYSEGMENT_DATA_OFFSET; cacheSlots = POLYSEGMENT_CACHE_SLOTS; cacheThreshold = POLYSEGMENT_CACHE_THRESHOLD; cacheLowerThreshold = POLYSEGMENT_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~PolySegmentStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "PolySegment"; } virtual unsigned char opcode() const { return X_PolySegment; } virtual unsigned int storage() const { return sizeof(PolySegmentMessage); } // // Message handling methods. // public: virtual Message *create() const { return new PolySegmentMessage(); } virtual Message *create(const Message &message) const { return new PolySegmentMessage((const PolySegmentMessage &) message); } virtual void destroy(Message *message) const { delete (PolySegmentMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* PolySegment_H */ nxcomp/ClientProxy.h0000644000076400007640000000613411323113027014760 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ClientProxy_H #define ClientProxy_H #include "Proxy.h" // // Set the verbosity level. // #undef TEST #undef DEBUG class ClientProxy : public Proxy { public: ClientProxy(int proxyFD); virtual ~ClientProxy(); virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily, sockaddr *xServerAddr, unsigned int xServerAddrLength); virtual void handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort, int httpServerPort, const char *fontServerPort); protected: // // Create a new channel. // virtual int handleNewConnection(T_channel_type type, int clientFd); virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId); virtual int handleNewAgentConnection(Agent *agent); virtual int handleNewXConnection(int clientFd); virtual int handleNewXConnectionFromProxy(int channelId); // // Implement persistence according // to our proxy mode. // virtual int handleLoad(T_load_type type); virtual int handleSave(); virtual int handleAsyncEvents(); virtual int handleLoadFromProxy(); virtual int handleSaveFromProxy(); virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient) const; virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const; // // Utility function used to realize // a new connection. // protected: virtual int checkLocalChannelMap(int channelId) { if (control -> isProtoStep7() == 1) { return ((channelId & control -> ChannelMask) != 0); } else { return 1; } } // // Ports where to forward extended services' // TCP connections. // private: char *fontServerPort_; }; #endif /* ClientProxy_H */ nxcomp/Z.cpp0000644000076400007640000000710511323113030013235 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Z.h" #include "Misc.h" int ZCompress(z_stream *stream, unsigned char *dest, unsigned int *destLen, const unsigned char *source, unsigned int sourceLen) { // // Deal with the possible overflow. // if (stream -> total_out & 0x80000000) { #ifdef TEST *logofs << "ZCompress: Reset stream counters with " << "total in " << stream -> total_in << " and total out " << stream -> total_out << ".\n" << logofs_flush; #endif stream -> total_in = 0; stream -> total_out = 0; } unsigned int saveOut = stream -> total_out; stream -> next_in = (Bytef *) source; stream -> avail_in = sourceLen; // // Check if the source is bigger than // 64K on 16-bit machine. // #ifdef MAXSEG_64K if ((uLong) stream -> avail_in != sourceLen) return Z_BUF_ERROR; #endif stream -> next_out = dest; stream -> avail_out = *destLen; #ifdef MAXSEG_64K if ((uLong) stream -> avail_out != *destLen) return Z_BUF_ERROR; #endif int result = deflate(stream, Z_FINISH); if (result != Z_STREAM_END) { deflateReset(stream); return (result == Z_OK ? Z_BUF_ERROR : result); } *destLen = stream -> total_out - saveOut; result = deflateReset(stream); return result; } int ZDecompress(z_stream *stream, unsigned char *dest, unsigned int *destLen, const unsigned char *source, unsigned int sourceLen) { stream -> next_in = (Bytef *) source; stream -> avail_in = sourceLen; // // Deal with the possible overflow. // if (stream -> total_out & 0x80000000) { #ifdef TEST *logofs << "ZDecompress: Reset stream counters with " << "total in " << stream -> total_in << " and total out " << stream -> total_out << ".\n" << logofs_flush; #endif stream -> total_in = 0; stream -> total_out = 0; } unsigned int saveOut = stream -> total_out; if (stream -> avail_in != sourceLen) { return Z_BUF_ERROR; } stream -> next_out = dest; stream -> avail_out = *destLen; if (stream -> avail_out != *destLen) { return Z_BUF_ERROR; } int result = inflate(stream, Z_FINISH); if (result != Z_STREAM_END) { inflateReset(stream); return (result == Z_OK ? Z_BUF_ERROR : result); } *destLen = stream -> total_out - saveOut; result = inflateReset(stream); return result; } nxcomp/ShapeExtension.h0000644000076400007640000001122011323113030015417 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ShapeExtension_H #define ShapeExtension_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define SHAPEEXTENSION_ENABLE_CACHE 1 #define SHAPEEXTENSION_ENABLE_DATA 1 #define SHAPEEXTENSION_ENABLE_SPLIT 0 #define SHAPEEXTENSION_ENABLE_COMPRESS 1 #define SHAPEEXTENSION_DATA_LIMIT 3200 #define SHAPEEXTENSION_DATA_OFFSET 20 #define SHAPEEXTENSION_CACHE_SLOTS 3000 #define SHAPEEXTENSION_CACHE_THRESHOLD 10 #define SHAPEEXTENSION_CACHE_LOWER_THRESHOLD 5 #define SHAPEEXTENSION_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 // // The message class. // class ShapeExtensionMessage : public Message { friend class ShapeExtensionStore; public: ShapeExtensionMessage() { } ~ShapeExtensionMessage() { } // // Note for encoding in protocol level 1: we consider // for this message a data offset of 4. Bytes from 5 // to 20, if present, are taken as part of identity // and encoded through an array of int caches. // private: unsigned char opcode; unsigned short data[8]; }; class ShapeExtensionStore : public MessageStore { public: ShapeExtensionStore(StaticCompressor *compressor); virtual ~ShapeExtensionStore(); virtual const char *name() const { return "ShapeExtension"; } virtual unsigned char opcode() const { return opcode_; } virtual unsigned int storage() const { return sizeof(ShapeExtensionMessage); } // // Message handling methods. // public: virtual Message *create() const { return new ShapeExtensionMessage(); } virtual Message *create(const Message &message) const { return new ShapeExtensionMessage((const ShapeExtensionMessage &) message); } virtual void destroy(Message *message) const { delete (ShapeExtensionMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; private: unsigned char opcode_; }; #endif /* ShapeExtension_H */ nxcomp/PutImage.cpp0000644000076400007640000003050611323113027014546 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "PutImage.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // PutImageStore::PutImageStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = PUTIMAGE_ENABLE_CACHE; enableData = PUTIMAGE_ENABLE_DATA; enableSplit = PUTIMAGE_ENABLE_SPLIT; enableCompress = PUTIMAGE_ENABLE_COMPRESS; if (control -> isProtoStep7() == 1) { enableCompress = PUTIMAGE_ENABLE_COMPRESS_IF_PROTO_STEP_7; } dataLimit = PUTIMAGE_DATA_LIMIT; dataOffset = PUTIMAGE_DATA_OFFSET; cacheSlots = PUTIMAGE_CACHE_SLOTS; cacheThreshold = PUTIMAGE_CACHE_THRESHOLD; cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD; if (control -> isProtoStep8() == 1) { enableSplit = PUTIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8; } messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } PutImageStore::~PutImageStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int PutImageStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif encodeBuffer.encodeValue(GetUINT(buffer + 2, bigEndian), 16, 8); encodeBuffer.encodeValue((unsigned int) buffer[1], 2); encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> drawableCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> gcCache); unsigned int width = GetUINT(buffer + 12, bigEndian); encodeBuffer.encodeCachedValue(width, 16, clientCache -> putImageWidthCache, 8); unsigned int height = GetUINT(buffer + 14, bigEndian); encodeBuffer.encodeCachedValue(height, 16, clientCache -> putImageHeightCache, 8); unsigned int x = GetUINT(buffer + 16, bigEndian); int xDiff = x - clientCache -> putImageLastX; clientCache -> putImageLastX = x; encodeBuffer.encodeCachedValue(xDiff, 16, clientCache -> putImageXCache, 8); unsigned int y = GetUINT(buffer + 18, bigEndian); int yDiff = y - clientCache -> putImageLastY; clientCache -> putImageLastY = y; encodeBuffer.encodeCachedValue(yDiff, 16, clientCache -> putImageYCache, 8); encodeBuffer.encodeCachedValue(buffer[20], 8, clientCache -> putImageLeftPadCache); encodeBuffer.encodeCachedValue(buffer[21], 8, clientCache -> depthCache); #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int PutImageStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif unsigned int value; unsigned char cValue; decodeBuffer.decodeValue(value, 16, 8); size = (value << 2); buffer = writeBuffer -> addMessage(size); decodeBuffer.decodeValue(value, 2); buffer[1] = (unsigned char) value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); PutULONG(value, buffer + 4, bigEndian); decodeBuffer.decodeXidValue(value, clientCache -> gcCache); PutULONG(value, buffer + 8, bigEndian); unsigned int width; decodeBuffer.decodeCachedValue(width, 16, clientCache -> putImageWidthCache, 8); PutUINT(width, buffer + 12, bigEndian); unsigned int height; decodeBuffer.decodeCachedValue(height, 16, clientCache -> putImageHeightCache, 8); PutUINT(height, buffer + 14, bigEndian); decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageXCache, 8); clientCache -> putImageLastX += value; clientCache -> putImageLastX &= 0xffff; PutUINT(clientCache -> putImageLastX, buffer + 16, bigEndian); decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageYCache, 8); clientCache -> putImageLastY += value; clientCache -> putImageLastY &= 0xffff; PutUINT(clientCache -> putImageLastY, buffer + 18, bigEndian); decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> putImageLeftPadCache); buffer[20] = cValue; decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); buffer[21] = cValue; #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int PutImageStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { PutImageMessage *putImage = (PutImageMessage *) message; // // Here is the fingerprint. // putImage -> format = *(buffer + 1); putImage -> depth = *(buffer + 21); putImage -> left_pad = *(buffer + 20); putImage -> width = GetUINT(buffer + 12, bigEndian); putImage -> height = GetUINT(buffer + 14, bigEndian); putImage -> pos_x = GetUINT(buffer + 16, bigEndian); putImage -> pos_y = GetUINT(buffer + 18, bigEndian); putImage -> drawable = GetULONG(buffer + 4, bigEndian); putImage -> gcontext = GetULONG(buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int PutImageStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { PutImageMessage *putImage = (PutImageMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = putImage -> format; PutULONG(putImage -> drawable, buffer + 4, bigEndian); PutULONG(putImage -> gcontext, buffer + 8, bigEndian); PutUINT(putImage -> width, buffer + 12, bigEndian); PutUINT(putImage -> height, buffer + 14, bigEndian); PutUINT(putImage -> pos_x, buffer + 16, bigEndian); PutUINT(putImage -> pos_y, buffer + 18, bigEndian); *(buffer + 20) = (unsigned char) putImage -> left_pad; *(buffer + 21) = (unsigned char) putImage -> depth; #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void PutImageStore::dumpIdentity(const Message *message) const { #ifdef DUMP PutImageMessage *putImage = (PutImageMessage *) message; *logofs << name() << ": Identity format " << (unsigned) putImage -> format << ", depth " << (unsigned) putImage -> depth << ", left_pad " << (unsigned) putImage -> left_pad << ", width " << putImage -> width << ", height " << putImage -> height << ", pos_x " << putImage -> pos_x << ", pos_y " << putImage -> pos_y << ", drawable " << putImage -> drawable << ", gcontext " << putImage -> gcontext << ", size " << putImage -> size_ << ".\n" << logofs_flush; #endif } void PutImageStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // Fields format, width, height, left_pad, depth. // md5_append(md5_state_, buffer + 1, 1); md5_append(md5_state_, buffer + 12, 4); md5_append(md5_state_, buffer + 20, 2); } void PutImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { // // Encode the variant part. // PutImageMessage *putImage = (PutImageMessage *) message; PutImageMessage *cachedPutImage = (PutImageMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << putImage -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(putImage -> drawable, clientCache -> drawableCache); cachedPutImage -> drawable = putImage -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << putImage -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(putImage -> gcontext, clientCache -> gcCache); cachedPutImage -> gcontext = putImage -> gcontext; #ifdef TEST *logofs << name() << ": Encoding value " << putImage -> pos_x << " as " << "pos_x" << " field.\n" << logofs_flush; #endif unsigned short int diff_x = putImage -> pos_x - cachedPutImage -> pos_x; encodeBuffer.encodeCachedValue(diff_x, 16, clientCache -> putImageXCache, 8); cachedPutImage -> pos_x = putImage -> pos_x; #ifdef TEST *logofs << name() << ": Encoding value " << putImage -> pos_y << " as " << "pos_y" << " field.\n" << logofs_flush; #endif unsigned short int diff_y = putImage -> pos_y - cachedPutImage -> pos_y; encodeBuffer.encodeCachedValue(diff_y, 16, clientCache -> putImageYCache, 8); cachedPutImage -> pos_y = putImage -> pos_y; } void PutImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { // // Decode the variant part. // PutImageMessage *putImage = (PutImageMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); putImage -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << putImage -> drawable << " as drawable field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); putImage -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << putImage -> gcontext << " as gcontext field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageXCache, 8); putImage -> pos_x += value; putImage -> pos_x &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << putImage -> pos_x << " as pos_x field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageYCache, 8); putImage -> pos_y += value; putImage -> pos_y &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << putImage -> pos_y << " as pos_y field.\n" << logofs_flush; #endif } nxcomp/GetImage.cpp0000644000076400007640000001264211323113027014516 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "GetImage.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int GetImageStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { GetImageMessage *getImage = (GetImageMessage *) message; // // Here is the fingerprint. // getImage -> format = *(buffer + 1); #ifdef TEST if (getImage -> format != 1 && getImage -> format != 2) { *logofs << name() << ": WARNING! Dirty value " << getImage -> format << " for field format.\n" << logofs_flush; } #endif getImage -> drawable = GetULONG(buffer + 4, bigEndian); getImage -> x = GetUINT(buffer + 8, bigEndian); getImage -> y = GetUINT(buffer + 10, bigEndian); getImage -> width = GetUINT(buffer + 12, bigEndian); getImage -> height = GetUINT(buffer + 14, bigEndian); getImage -> plane_mask = GetULONG(buffer + 16, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int GetImageStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { GetImageMessage *getImage = (GetImageMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = getImage -> format; PutULONG(getImage -> drawable, buffer + 4, bigEndian); PutUINT(getImage -> x, buffer + 8, bigEndian); PutUINT(getImage -> y, buffer + 10, bigEndian); PutUINT(getImage -> width, buffer + 12, bigEndian); PutUINT(getImage -> height, buffer + 14, bigEndian); PutULONG(getImage -> plane_mask, buffer + 16, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void GetImageStore::dumpIdentity(const Message *message) const { #ifdef DUMP GetImageMessage *getImage = (GetImageMessage *) message; *logofs << name() << ": Identity format " << (unsigned) getImage -> format << ", drawable " << getImage -> drawable << ", x " << getImage -> x << ", y " << getImage -> y << ", width " << getImage -> width << ", height " << getImage -> height << ", plane_mask " << getImage -> plane_mask << ", size " << getImage -> size_ << ".\n" << logofs_flush; #endif } void GetImageStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 1, 1); md5_append(md5_state_, buffer + 8, 2); md5_append(md5_state_, buffer + 10, 2); md5_append(md5_state_, buffer + 12, 2); md5_append(md5_state_, buffer + 14, 2); md5_append(md5_state_, buffer + 16, 4); } void GetImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { GetImageMessage *getImage = (GetImageMessage *) message; GetImageMessage *cachedGetImage = (GetImageMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << getImage -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(getImage -> drawable, clientCache -> drawableCache); cachedGetImage -> drawable = getImage -> drawable; } void GetImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { GetImageMessage *getImage = (GetImageMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); getImage -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << getImage -> drawable << " as drawable field.\n" << logofs_flush; #endif } nxcomp/RenderCompositeGlyphs.h0000644000076400007640000000545511323113030016770 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderCompositeGlyphs_H #define RenderCompositeGlyphs_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderCompositeGlyphs" #undef MESSAGE_STORE #define MESSAGE_STORE RenderCompositeGlyphsStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 28 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Encode the first 8 bytes of the // data differentially in newer // protocol versions. // #undef MESSAGE_OFFSET_IF_PROTO_STEP_8 #define MESSAGE_OFFSET_IF_PROTO_STEP_8 36 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { unsigned int offset = (control -> isProtoStep8() == 1 ? MESSAGE_OFFSET_IF_PROTO_STEP_8 : MESSAGE_OFFSET); return (size >= offset ? offset : size); } #include MESSAGE_METHODS }; #endif /* RenderCompositeGlyphs_H */ nxcomp/ActionCache.cpp0000644000076400007640000000274111323113026015173 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Control.h" #include "ActionCache.h" ActionCache::ActionCache() { for (int i = 0; i < 256; i++) { base_[i] = new IntCache(8); } slot_ = 0; last_ = 0; } ActionCache::~ActionCache() { for (int i = 0; i < 256; i++) { delete base_[i]; } } nxcomp/ClearArea.cpp0000644000076400007640000000747211323113031014653 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ClearArea.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int ClearAreaStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { ClearAreaMessage *clearArea = (ClearAreaMessage *) message; // // Here is the fingerprint. // clearArea -> exposures = *(buffer + 1); clearArea -> window = GetULONG(buffer + 4, bigEndian); clearArea -> x = GetUINT(buffer + 8, bigEndian); clearArea -> y = GetUINT(buffer + 10, bigEndian); clearArea -> width = GetUINT(buffer + 12, bigEndian); clearArea -> height = GetUINT(buffer + 14, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int ClearAreaStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { ClearAreaMessage *clearArea = (ClearAreaMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = clearArea -> exposures; PutULONG(clearArea -> window, buffer + 4, bigEndian); PutUINT(clearArea -> x, buffer + 8, bigEndian); PutUINT(clearArea -> y, buffer + 10, bigEndian); PutUINT(clearArea -> width, buffer + 12, bigEndian); PutUINT(clearArea -> height, buffer + 14, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void ClearAreaStore::dumpIdentity(const Message *message) const { #ifdef DUMP ClearAreaMessage *clearArea = (ClearAreaMessage *) message; *logofs << name() << ": Identity exposures " << clearArea -> (unsigned int) exposures << ", window " << clearArea -> window << ", x " << clearArea -> x << ", y " << clearArea -> y << ", width " << clearArea -> width << ", height " << clearArea -> height << ", size " << clearArea -> size_ << ".\n"; #endif } void ClearAreaStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 1, 1); md5_append(md5_state_, buffer + 4, 4); md5_append(md5_state_, buffer + 8, 2); md5_append(md5_state_, buffer + 10, 2); md5_append(md5_state_, buffer + 12, 2); md5_append(md5_state_, buffer + 14, 2); } nxcomp/PutImage.h0000644000076400007640000001155411323113031014210 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PutImage_H #define PutImage_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define PUTIMAGE_ENABLE_CACHE 1 #define PUTIMAGE_ENABLE_DATA 1 #define PUTIMAGE_ENABLE_SPLIT 1 #define PUTIMAGE_ENABLE_COMPRESS 1 #define PUTIMAGE_DATA_LIMIT 262144 - 24 #define PUTIMAGE_DATA_OFFSET 24 #define PUTIMAGE_CACHE_SLOTS 6000 #define PUTIMAGE_CACHE_THRESHOLD 70 #define PUTIMAGE_CACHE_LOWER_THRESHOLD 50 #define PUTIMAGE_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 #define PUTIMAGE_CACHE_THRESHOLD_IF_PACKED 10 #define PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED 5 #define PUTIMAGE_CACHE_THRESHOLD_IF_SHADOW 97 #define PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_SHADOW 90 #define PUTIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8 0 // // The message class. // class PutImageMessage : public Message { friend class PutImageStore; public: PutImageMessage() { } ~PutImageMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char format; unsigned char depth; unsigned char left_pad; unsigned short width; unsigned short height; unsigned int drawable; unsigned int gcontext; unsigned short pos_x; unsigned short pos_y; }; class PutImageStore : public MessageStore { public: PutImageStore(StaticCompressor *compressor); virtual ~PutImageStore(); virtual const char *name() const { return "PutImage"; } virtual unsigned char opcode() const { return X_PutImage; } virtual unsigned int storage() const { return sizeof(PutImageMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new PutImageMessage(); } virtual Message *create(const Message &message) const { return new PutImageMessage((const PutImageMessage &) message); } virtual void destroy(Message *message) const { delete (PutImageMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* PutImage_H */ nxcomp/Message.h0000644000076400007640000006151311323113030014060 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Message_H #define Message_H #include #include "NXproto.h" #include "Misc.h" #include "Control.h" #include "Types.h" #include "Timestamp.h" #include "ActionCache.h" #include "ActionCacheCompat.h" #include "PositionCacheCompat.h" #include "StaticCompressor.h" // // Forward class declarations. // class ChannelCache; class EncodeBuffer; class DecodeBuffer; class WriteBuffer; // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Define this to know how many messages // are allocated and deallocated. // #undef REFERENCES // // Set default values. We limit the maximum // size of a request to 262144 but we need to // consider the replies, whose size may be up // to 4MB. // #define MESSAGE_ENABLE_CACHE 0 #define MESSAGE_ENABLE_DATA 0 #define MESSAGE_ENABLE_SPLIT 0 #define MESSAGE_ENABLE_COMPRESS 0 #define MESSAGE_DATA_LIMIT 4194304 - 4 #define MESSAGE_DATA_OFFSET 4 #define MESSAGE_CACHE_SLOTS 6000 #define MESSAGE_CACHE_THRESHOLD 50 #define MESSAGE_CACHE_LOWER_THRESHOLD 5 // // Base message class. // class Message { friend class MessageStore; friend class RenderExtensionStore; public: Message() { hits_ = 0; last_ = 0; locks_ = 0; size_ = 0; c_size_ = 0; md5_digest_ = NULL; #ifdef REFERENCES references_++; *logofs << "Message: Created new message at " << this << " out of " << references_ << " allocated messages.\n" << logofs_flush; #endif } Message(const Message &message) { size_ = message.size_; c_size_ = message.c_size_; i_size_ = message.i_size_; hits_ = message.hits_; last_ = message.last_; locks_ = message.locks_; data_ = message.data_; #ifdef REFERENCES references_++; *logofs << "Message: Creating new copied message at " << this << " out of " << references_ << " allocated messages.\n" << logofs_flush; #endif if (message.md5_digest_ != NULL) { md5_digest_ = new md5_byte_t[MD5_LENGTH]; memcpy(md5_digest_, message.md5_digest_, MD5_LENGTH); #ifdef DEBUG *logofs << "Message: Created MD5 digest for object at " << this << ".\n" << logofs_flush; #endif } else { md5_digest_ = NULL; } } ~Message() { #ifdef DEBUG if (md5_digest_ != NULL) { *logofs << "Message: Deleted MD5 digest for object at " << this << ".\n" << logofs_flush; } #endif delete [] md5_digest_; #ifdef REFERENCES references_--; *logofs << "Message: Deleted message at " << this << " out of " << references_ << " allocated messages.\n" << logofs_flush; #endif } // // This is the original message size // including the data part regardless // data is still stored in the object. // int size_; // // This is the size of the identity. // int i_size_; // // This is the size, including identity, // after message has been 'updated' to // reflect storage of data in compressed // format. // int c_size_; protected: // // This is the data part. // T_data data_; // // Time of last hit. // time_t last_; // // This is the number of cache hits // registered for the object. // short int hits_; // // This is used to mark messages // that have been split. // short int locks_; // // This is the MD5 checksum. // md5_byte_t *md5_digest_; // // Keep a reference counter // of allocated objects. // #ifdef REFERENCES static int references_; #endif }; // // Repository of messages. // class MessageStore { public: // // Enable or disable cache of messages in store. // int enableCache; // // Does message have a distinct data part. // int enableData; // // Enable or disable split of data part. // int enableSplit; // // Enable or disable compression of data part. // int enableCompress; // // Set starting point of data part in the message. // int dataOffset; // // Set maximum size for the data part of each message. // int dataLimit; // // Set maximum elements in cache. // int cacheSlots; // // Set the percentage of total cache memory which // a given type of message is allowed to occupy. // When threshold is exceeded store is cleaned to // make room for a new message of the same type. // int cacheThreshold; // // Don't clean the store if percentage of cache // memory occupied by messages of this type is // below the threshold. // int cacheLowerThreshold; // // Last operation performed on cache. // T_store_action lastAction; // // Position of last element stored in cache. // short int lastAdded; // // Positions of last element found in cache. // short int lastHit; // // Position of last element erased. // short int lastRemoved; // // Used to encode the the action to // perform on the store and the slot // involved. // ActionCache lastActionCache; // // Used in old protocol versions. // ActionCacheCompat lastActionCacheCompat; PositionCacheCompat lastAddedCacheCompat; PositionCacheCompat lastHitCacheCompat; PositionCacheCompat lastRemovedCacheCompat; // // Position in cache where next insertion // is going to take place. // short int lastRated; // // Size of data part of last split message // once compressed. This is used only for // compatibility with older proxies. // int lastResize; // // Constructors and destructors. // public: MessageStore(StaticCompressor *compressor = NULL); virtual ~MessageStore(); virtual const char *name() const = 0; virtual unsigned char opcode() const = 0; virtual unsigned int storage() const = 0; // // These are members that must be specialized. // public: virtual Message *create() const = 0; virtual Message *create(const Message &message) const = 0; virtual void destroy(Message *message) const = 0; void validateSize(int size) { if (size < control -> MinimumMessageSize || size > control -> MaximumMessageSize) { *logofs << name() << ": PANIC! Invalid size " << size << " for message.\n" << logofs_flush; cerr << "Error" << ": Invalid size " << size << " for message opcode " << opcode() << ".\n"; HandleAbort(); } } void validateSize(int dataSize, int compressedDataSize) { if (dataSize < 0 || dataSize > control -> MaximumMessageSize - 4 || compressedDataSize < 0 || compressedDataSize >= dataSize) { *logofs << name() << ": PANIC! Invalid data size " << dataSize << " and compressed data size " << compressedDataSize << " for message.\n" << logofs_flush; cerr << "Error" << ": Invalid data size " << dataSize << " and compressed data size " << compressedDataSize << " for message " << "opcode " << (unsigned) opcode() << ".\n"; HandleAbort(); } } // // Determine if the message can be stored // in the cache. // virtual int validateMessage(const unsigned char *buffer, int size) { return (size >= control -> MinimumMessageSize && size <= control -> MaximumMessageSize); } // // Get data offset based on major and minor // opcode of the message. // virtual int identitySize(const unsigned char *buffer, unsigned int size) { return dataOffset; } // // Encode identity and data using the // specific message encoding. // // Some messages do not implement these // methods because the encoding is done // directly in the channel loop. Should // move the encoding methods in in the // message classes. // virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int size, int bigEndian, ChannelCache *channelCache) const { return 1; } virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { return 1; } // // Encode differences between message // in cache and the one to be encoded. // virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { } // // Decode differences and update the // cached version of the same message. // virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { } // // Post process the message information // contained in the store by either up- // dating the size record or the actual // data part once the message has been // completely sent to our peer. // void updateData(const int position, unsigned int dataSize, unsigned int compressedDataSize); void updateData(const T_checksum checksum, unsigned int compressedDataSize); void updateData(const int position, const unsigned char *newData, unsigned int dataSize, unsigned int compressedDataSize); // // These members, used internally // in the message store class, are // mandatory. // protected: virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const = 0; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const = 0; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const = 0; virtual void dumpIdentity(const Message *message) const = 0; // // Design should preserve these from being // virtual. // int parseData(Message *message, int split, const unsigned char *buffer, unsigned int size, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian); int parseData(Message *message, const unsigned char *buffer, unsigned int size, const unsigned char *compressedData, const unsigned int compressedDataSize, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian); int unparseData(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian); // // Manage efficient allocation of messages // in the heap. // void recycle(Message *message) { #ifdef TEST if (message == NULL) { *logofs << name() << ": PANIC! Cannot recycle a null message.\n" << logofs_flush; cerr << "Error" << ": Cannot recycle a null message.\n"; HandleAbort(); } #endif if (temporary_ == NULL) { // // Be careful when reusing the message as // it can contain valid data that must be // explicitly deallocated if not needed. // Note also that you cannot count on the // initialization made in costructor. // temporary_ = message; } else { destroy(message); } } void beginChecksum(Message *message) { if (message -> md5_digest_ == NULL) { message -> md5_digest_ = new md5_byte_t[MD5_LENGTH]; #ifdef DEBUG *logofs << name() << ": Created MD5 digest structure " << "for object at " << message << ".\n" << logofs_flush; #endif } #ifdef DEBUG else { *logofs << name() << ": Using existing MD5 digest structure " << "for object at " << message << ".\n" << logofs_flush; } #endif #ifdef DEBUG *logofs << name() << ": Prepared MD5 digest for object at " << message << ".\n" << logofs_flush; #endif md5_init(md5_state_); } void endChecksum(Message *message) { md5_finish(md5_state_, message -> md5_digest_); #ifdef DEBUG *logofs << name() << ": Calculated checksum for object at " << message << ".\n" << logofs_flush; #endif } void dataChecksum(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) { // // Messages that have a data part starting // at an offset possibly beyond the end of // the message, must include the size in // the identity checksum. // if ((int) size > message -> i_size_) { md5_append(md5_state_, buffer + message -> i_size_, size - message -> i_size_); } } // // Repository handling methods. // public: // // Extract identity and data from buffer. // The size field will be updated at the // time of data parsing. // int parse(Message *message, int split, const unsigned char *buffer, unsigned int size, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian); int parse(Message *message, const unsigned char *buffer, unsigned int size, const unsigned char *compressedData, const unsigned int compressedDataSize, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian); // // From identity and data write the // final message to the raw buffer. // int unparse(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) { return (unparseData(message, buffer, size, bigEndian) && unparseIdentity(message, buffer, size, bigEndian)); } void dump(const Message *message) const { dumpIdentity(message); dumpData(message); } void dumpData(const Message *message) const; // // This returns the original message size as it // was received on the link. It takes in account // the data part, regardless data is still stored // in the message object. This information will // be used at the time message is unparsed. // int plainSize(const int position) const { return (*messages_)[position] -> size_; } // // This returns either the size of identity plus // the compressed data part or 0 if message is // stored in uncompressed format. // int compressedSize(const int position) const { return (*messages_)[position] -> c_size_; } // // Returns a pointer to message // given its position in cache. // Message *get(const int position) const { if (position < 0 || position >= cacheSlots) { #ifdef PANIC *logofs << name() << ": PANIC! Requested position " << position << " is not inside the " << "container.\n" << logofs_flush; #endif cerr << "Error" << ": Requested position " << position << " is not inside the" << "container.\n"; HandleAbort(); } else if ((*messages_)[position] == NULL) { #ifdef PANIC *logofs << name() << ": PANIC! Message at position " << position << " is NULL.\n" << logofs_flush; #endif cerr << "Error" << ": Message at position " << position << " is NULL.\n"; HandleAbort(); } return (*messages_)[position]; } // // This is the method called at encoding // side to add a message to cache. // int findOrAdd(Message *message, T_checksum_action checksumAction, T_data_action dataAction, int &added, int &locked); // // Utility interfaces to message insertion // and deletion. // int add(Message *message, const int position, T_checksum_action checksumAction, T_data_action dataAction); int remove(const int position, T_checksum_action checksumAction, T_data_action dataAction); // // Make space in the repository by remove // the first suitable message object. // int clean(T_checksum_action checksumAction); // // Increase or decrease the "rating" of // the message object. // int touch(Message *message) const; int untouch(Message *message) const; int getTouches(const int position) const { Message *message = (*messages_)[position]; if (message == NULL) { return 0; } return message -> hits_; } // // Gives a 'weight' to the cached message. A zero // value means object can be safely removed. A value // greater than zero means it is advisable to retain // the object. A negative result means it is mandato- // ry to keep object in cache. // int getRating(Message *message, T_rating type) const; // // Increase or decrease locks of message at given // position. A locked message will not be removed // from the message store until the lock counter // is zero. // int lock(const int position) const; int unlock(const int position) const; int getLocks(const int position) const { Message *message = (*messages_)[position]; if (message == NULL) { return 0; } return message -> locks_; } T_checksum const getChecksum(const int position) const { return getChecksum(get(position)); } T_checksum const getChecksum(const Message *message) const { if (message -> md5_digest_ == NULL) { #ifdef PANIC *logofs << name() << ": PANIC! Checksum not initialized " << "for object at " << message << ".\n" << logofs_flush; #endif cerr << "Error" << ": Checksum not initialized " << "for object at " << message << ".\n"; HandleAbort(); } #ifdef DEBUG *logofs << name() << ": Got checksum for object at " << message << ".\n" << logofs_flush; #endif return message -> md5_digest_; } // // Calculate the checksum on the fly based the // opcode in the buffer. Useful in the case a // message was not processed or was not stored // in the cache. The returned checksum must be // explicitly deallocated by the caller, after // use. // T_checksum getChecksum(const unsigned char *buffer, unsigned int size, int bigEndian); const unsigned char *getData(const Message *message) const { return message -> data_.begin(); } int plainSize(const Message *message) const { return message -> size_; } int identitySize(Message *message) { return message -> i_size_; } int compressedSize(const Message *message) const { return message -> c_size_; } Message *getTemporary() { if (temporary_ == NULL) { temporary_ = create(); } return temporary_; } void resetTemporary() { temporary_ = NULL; } // // On side where we don't have checksums, we // count how many messages are in the array. // This is obviously expensive and should be // only performed when reporting statistics. // int getSize() const { int size = checksums_ -> size(); if (size == 0) { for (int i = 0; i < cacheSlots; i++) { if ((*messages_)[i] != NULL) { size++; } } } return size; } int getLocalStorageSize() const { return localStorageSize_; } int getRemoteStorageSize() const { return remoteStorageSize_; } int getLocalTotalStorageSize() const { return totalLocalStorageSize_; } int getRemoteTotalStorageSize() const { return totalRemoteStorageSize_; } static int getCumulativeTotalStorageSize() { return (totalLocalStorageSize_ > totalRemoteStorageSize_ ? totalLocalStorageSize_ : totalRemoteStorageSize_); } int saveStore(ostream *cachefs, md5_state_t *md5_state_stream, md5_state_t *md5_state_client, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian); int loadStore(istream *cachefs, md5_state_t *md5_state_stream, T_checksum_action checksumAction, T_data_action dataAction, int bigEndian); protected: // // Estimate the memory requirements of given // instance of message. Size includes memory // allocated from heap to store checksum and // data. // void storageSize(const Message *message, unsigned int &local, unsigned int &remote) const; // // Just used for debug. // void printStorageSize(); // // Repositories where to save cached messages. // First is a vector of pointers, the second // is a hash table used for fast lookups. // T_messages *messages_; T_checksums *checksums_; // // A message object to be used as a temporary. // Reuse the temporary object if possible, if // not, create a new instance. // Message *temporary_; // // Used to calculate message's checksum. // md5_state_t *md5_state_; private: // // Used to compress data payload. // StaticCompressor *compressor_; // // Keep track of how many bytes // are taken by cache. // int localStorageSize_; int remoteStorageSize_; static int totalLocalStorageSize_; static int totalRemoteStorageSize_; // // Used to track object allocation and deallocation. // #ifdef REFERENCES static int references_; #endif }; // // This is an ancillary class of the message // store, used to encode extensions based on // the minor opcode. // class MinorMessageStore { public: virtual ~MinorMessageStore() { } virtual const char *name() const = 0; virtual int identitySize(const unsigned char *buffer, unsigned int size) = 0; virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { return 1; } virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, unsigned char type, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { return 1; } virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, unsigned int size, int bigEndian, ChannelCache *channelCache) const { } virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, unsigned int size, int bigEndian, ChannelCache *channelCache) const { } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const = 0; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const = 0; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { } virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { } virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, md5_state_t *md5_state, int bigEndian) const = 0; }; #endif /* Message_H */ nxcomp/GenericReply.cpp0000644000076400007640000002073211323113031015416 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "GenericReply.h" #include "ServerCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // GenericReplyStore::GenericReplyStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = GENERICREPLY_ENABLE_CACHE; enableData = GENERICREPLY_ENABLE_DATA; enableSplit = GENERICREPLY_ENABLE_SPLIT; enableCompress = GENERICREPLY_ENABLE_COMPRESS; if (control -> isProtoStep7() == 1) { enableCompress = GENERICREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7; } dataLimit = GENERICREPLY_DATA_LIMIT; dataOffset = GENERICREPLY_DATA_OFFSET; cacheSlots = GENERICREPLY_CACHE_SLOTS; cacheThreshold = GENERICREPLY_CACHE_THRESHOLD; cacheLowerThreshold = GENERICREPLY_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } GenericReplyStore::~GenericReplyStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int GenericReplyStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ServerCache *serverCache = (ServerCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif encodeBuffer.encodeValue(GetULONG(buffer + 4, bigEndian), 32, 15); encodeBuffer.encodeCachedValue(*(buffer + 1), 8, serverCache -> genericReplyCharCache); for (unsigned int i = 0; i < 6; i++) { encodeBuffer.encodeCachedValue(GetULONG(buffer + i * 4 + 8, bigEndian), 32, *serverCache -> genericReplyIntCache[i]); } #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int GenericReplyStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ServerCache *serverCache = (ServerCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif decodeBuffer.decodeValue(size, 32, 15); size = 32 + (size << 2); buffer = writeBuffer -> addMessage(size); decodeBuffer.decodeCachedValue(*(buffer + 1), 8, serverCache -> genericReplyCharCache); unsigned int value; for (unsigned int i = 0; i < 6; i++) { decodeBuffer.decodeCachedValue(value, 32, *serverCache -> genericReplyIntCache[i]); PutULONG(value, buffer + i * 4 + 8, bigEndian); } #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int GenericReplyStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { GenericReplyMessage *genericReply = (GenericReplyMessage *) message; genericReply -> byte_data = *(buffer + 1); for (int i = 0; i < 12; i++) { genericReply -> short_data[i] = GetUINT(buffer + i * 2 + 8, bigEndian); } #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int GenericReplyStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { GenericReplyMessage *genericReply = (GenericReplyMessage *) message; *(buffer + 1) = genericReply -> byte_data; for (int i = 0; i < 12; i++) { PutUINT(genericReply -> short_data[i], buffer + i * 2 + 8, bigEndian); } #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void GenericReplyStore::dumpIdentity(const Message *message) const { #ifdef DUMP GenericReplyMessage *genericReply = (GenericReplyMessage *) message; *logofs << name() << ": Identity byte_data " << (unsigned) genericReply -> byte_data; for (int i = 0; i < 12; i++) { *logofs << ", short_data[" << i << "]" << (unsigned) genericReply -> short_data[i]; } *logofs << ", size " << genericReply -> size_ << ".\n"; #endif } void GenericReplyStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { } void GenericReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { // // Encode the variant part. // GenericReplyMessage *genericReply = (GenericReplyMessage *) message; GenericReplyMessage *cachedGenericReply = (GenericReplyMessage *) cachedMessage; ServerCache *serverCache = (ServerCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << (unsigned int) genericReply -> byte_data << " as byte_data field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(genericReply -> byte_data, 8, serverCache -> genericReplyCharCache); cachedGenericReply -> byte_data = genericReply -> byte_data; for (unsigned int i = 0; i < 12; i++) { #ifdef TEST *logofs << name() << ": Encoding value " << genericReply -> short_data[i] << " as short_data[" << i << "] field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(genericReply -> short_data[i], 16, *serverCache -> genericReplyIntCache[i]); cachedGenericReply -> short_data[i] = genericReply -> short_data[i]; } } void GenericReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { // // Decode the variant part. // GenericReplyMessage *genericReply = (GenericReplyMessage *) message; ServerCache *serverCache = (ServerCache *) channelCache; decodeBuffer.decodeCachedValue(genericReply -> byte_data, 8, serverCache -> genericReplyCharCache); #ifdef TEST *logofs << name() << ": Decoded value " << (unsigned int) genericReply -> byte_data << " as byte_data field.\n" << logofs_flush; #endif unsigned int value; for (unsigned int i = 0; i < 12; i++) { decodeBuffer.decodeCachedValue(value, 16, *serverCache -> genericReplyIntCache[i]); genericReply -> short_data[i] = (unsigned short) value; #ifdef TEST *logofs << name() << ": Decoded value " << genericReply -> short_data[i] << " as short_data[" << i << "] field.\n" << logofs_flush; #endif } } nxcomp/NXalert.h0000644000076400007640000002442211323113030014047 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef NXalert_H #define NXalert_H #define ALERT_CAPTION_PREFIX "NX - " #define INTERNAL_ERROR_ALERT 1 #define INTERNAL_ERROR_ALERT_TYPE "error" #define INTERNAL_ERROR_ALERT_STRING \ "\ An unrecoverable internal error was detected.\n\ Press OK to terminate the current session.\n\ " #define CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT 2 #define CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_TYPE "yesno" #define CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_STRING \ "\ One of the applications currently in use is not responding.\n\ Do you want to terminate the current session?\n\ " #define CLOSE_DEAD_X_CONNECTION_SERVER_ALERT 3 #define CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_TYPE "yesno" #define CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_STRING \ "\ One of the applications did not behave correctly and caused\n\ the X server to stop responding in a timely fashion. Do you\n\ want to terminate the current session?\n\ " #define CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT 4 #define CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT_TYPE NULL #define CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT_STRING NULL #define CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT 5 #define CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE "yesno" #define CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING \ "\ No response received from the remote server.\n\ Do you want to terminate the current session?\n\ " #define RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT 6 #define RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT_TYPE NULL #define RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT_STRING NULL #define RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT 7 #define RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE "yesno" #define RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING \ "\ Connection with remote server was shut down. NX will try\n\ to establish a new server connection. Session could have\n\ been left in a unusable state. Do you want to terminate\n\ the session?\n\ " #define CLOSE_UNRESPONSIVE_X_SERVER_ALERT 8 #define CLOSE_UNRESPONSIVE_X_SERVER_ALERT_TYPE "panic" #define CLOSE_UNRESPONSIVE_X_SERVER_ALERT_STRING \ "\ You pressed the key sequence CTRL+ALT+SHIFT+ESC.\n\ This is probably because your X server has become\n\ unresponsive. Session will be terminated in 30\n\ seconds unless you abort the procedure by pressing\n\ the Cancel button.\n\ " #define WRONG_PROXY_VERSION_ALERT 9 #define WRONG_PROXY_VERSION_ALERT_TYPE "ok" #define WRONG_PROXY_VERSION_ALERT_STRING \ "\ Local NX libraries version " VERSION " do not match the NX\n\ version of the remote server. Please check the error\n\ log on the server to find out which client version you\n\ need to install to be able to access this server.\n\ " #define FAILED_PROXY_CONNECTION_CLIENT_ALERT 10 #define FAILED_PROXY_CONNECTION_CLIENT_ALERT_TYPE NULL #define FAILED_PROXY_CONNECTION_CLIENT_ALERT_STRING NULL #define FAILED_PROXY_CONNECTION_SERVER_ALERT 11 #define FAILED_PROXY_CONNECTION_SERVER_ALERT_TYPE "yesno" #define FAILED_PROXY_CONNECTION_SERVER_ALERT_STRING \ "\ Could not yet establish the connection to the remote\n\ proxy. Do you want to terminate the current session?\n\ " #define MISSING_PROXY_CACHE_ALERT 12 #define MISSING_PROXY_CACHE_ALERT_TYPE "ok" #define MISSING_PROXY_CACHE_ALERT_STRING \ "\ NX was unable to negotiate a cache for this session.\n\ This may happen if this is the first time you run a\n\ session on this server or if cache was corrupted or\n\ produced by an incompatible NX version.\n\ " #define ABORT_PROXY_CONNECTION_ALERT 13 #define ABORT_PROXY_CONNECTION_ALERT_TYPE "ok" #define ABORT_PROXY_CONNECTION_ALERT_STRING \ "\ The connection with the remote server was shut down.\n\ Please check the state of your network connection.\n\ " /* * The one below is a special alert, used to close * a previous alert that is running on the given * side. This can be used to get rid of a message * that has ceased to hold true. */ #define DISPLACE_MESSAGE_ALERT 14 #define DISPLACE_MESSAGE_ALERT_TYPE NULL #define DISPLACE_MESSAGE_ALERT_STRING NULL /* * These are the other alert messages that were * added in the 1.5.0 release. The first is never * shown and is intended just for testing. */ #define GREETING_MESSAGE_ALERT 15 #define GREETING_MESSAGE_ALERT_TYPE "ok" #define GREETING_MESSAGE_ALERT_STRING \ "\ Welcome to NX from the NoMachine team. We really\n\ hope you will enjoy this wonderful software as much\n\ as we had fun making it ;-).\n\ " /* * These alerts are intended to notify the user * of the reason why the agent failed to resume * the session. */ #define START_RESUME_SESSION_ALERT 16 #define START_RESUME_SESSION_ALERT_TYPE "ok" #define START_RESUME_SESSION_ALERT_STRING \ "\ You appear to run your NX session across a slow network\n\ connection. Resuming the session may require some time.\n\ Please wait.\ " #define FAILED_RESUME_DISPLAY_ALERT 17 #define FAILED_RESUME_DISPLAY_ALERT_TYPE "error" #define FAILED_RESUME_DISPLAY_ALERT_STRING \ "\ Failed to open the display. Can't resume the NX\n\ session on this display.\n\ " #define FAILED_RESUME_DISPLAY_BROKEN_ALERT 18 #define FAILED_RESUME_DISPLAY_BROKEN_TYPE "error" #define FAILED_RESUME_DISPLAY_BROKEN_STRING \ "\ The display connection was broken while trying to\n\ resume the session. Please, check your network\n\ connection and try again.\n\ " #define FAILED_RESUME_VISUALS_ALERT 19 #define FAILED_RESUME_VISUALS_ALERT_TYPE "error" #define FAILED_RESUME_VISUALS_ALERT_STRING \ "\ Failed to restore all the required visuals.\n\ Can't resume the NX session on this display.\n\ " #define FAILED_RESUME_COLORMAPS_ALERT 20 #define FAILED_RESUME_COLORMAPS_ALERT_TYPE "error" #define FAILED_RESUME_COLORMAPS_ALERT_STRING \ "\ The number of available colormaps is different\n\ on the new display. Can't resume the NX session\n\ on this display.\n\ " #define FAILED_RESUME_PIXMAPS_ALERT 21 #define FAILED_RESUME_PIXMAPS_ALERT_TYPE "error" #define FAILED_RESUME_PIXMAPS_ALERT_STRING \ "\ Failed to restore all the required pixmap formats.\n\ Can't resume the NX session on this display.\n\ " #define FAILED_RESUME_DEPTHS_ALERT 22 #define FAILED_RESUME_DEPTHS_ALERT_TYPE "error" #define FAILED_RESUME_DEPTHS_ALERT_STRING \ "\ Failed to restore all the required screen depths.\n\ Can't resume the NX session on this display.\n\ " #define FAILED_RESUME_RENDER_ALERT 23 #define FAILED_RESUME_RENDER_ALERT_TYPE "error" #define FAILED_RESUME_RENDER_ALERT_STRING \ "\ The render extension is missing or an incompatible\n\ version was detected on your X server. Can't resume\n\ the NX session on this display.\n\ " #define FAILED_RESUME_FONTS_ALERT 24 #define FAILED_RESUME_FONTS_ALERT_TYPE "error" #define FAILED_RESUME_FONTS_ALERT_STRING \ "\ One or more of the fonts that are in use by the\n\ session are missing. Can't resume the NX session\n\ on this display.\n\ " #define ABORT_PROXY_NEGOTIATION_ALERT 62 #define ABORT_PROXY_NEGOTIATION_ALERT_TYPE "ok" #define ABORT_PROXY_NEGOTIATION_ALERT_STRING \ "\ The remote proxy closed the connection while negotiating\n\ the session. This may be due to the wrong authentication\n\ credentials passed to the server.\n\ " #define ABORT_PROXY_SHUTDOWN_ALERT 64 #define ABORT_PROXY_SHUTDOWN_ALERT_TYPE "ok" #define ABORT_PROXY_SHUTDOWN_ALERT_STRING \ "\ No response received from the remote proxy while\n\ waiting for the session shutdown.\n\ " #define FAILED_XDMCP_CONNECTION_ALERT 65 #define FAILED_XDMCP_CONNECTION_ALERT_TYPE "ok" #define FAILED_XDMCP_CONNECTION_ALERT_STRING \ "\ The XDM host that was contacted by the NX server doesn't\n\ seem to be able to start the session. Please check your\n\ server configuration.\n\ " /* * Used to handle the backward compatibility. * Update the numbers if you add a new alert. */ #define LAST_PROTO_STEP_6_ALERT 63 #define LAST_PROTO_STEP_7_ALERT 65 #endif /* NXalert_H */ nxcomp/PositionCacheCompat.cpp0000644000076400007640000000316711323113031016725 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Control.h" #include "PositionCacheCompat.h" PositionCacheCompat::PositionCacheCompat() { if (control -> isProtoStep7() == 0) { for (int i = 0; i < 32; i++) { base_[i] = new IntCache(8); } slot_ = 0; last_ = 0; } } PositionCacheCompat::~PositionCacheCompat() { if (control -> isProtoStep7() == 0) { for (int i = 0; i < 32; i++) { delete base_[i]; } } } nxcomp/RenderMinorExtensionTags.h0000644000076400007640000001307111323113027017436 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderMinorExtensionTags_H #define RenderMinorExtensionTags_H // // Set in the message header file. // #if MESSAGE_HAS_SIZE #define MESSAGE_ENCODE_SIZE encodeSize(encodeBuffer, buffer, size, bigEndian, channelCache) #define MESSAGE_DECODE_SIZE decodeSize(decodeBuffer, buffer, size, type, bigEndian, writeBuffer, channelCache) #else #define MESSAGE_ENCODE_SIZE #define MESSAGE_DECODE_SIZE size = MESSAGE_OFFSET; buffer = writeBuffer -> addMessage(size); #endif #if MESSAGE_HAS_DATA #define MESSAGE_ENCODE_DATA encodeData(encodeBuffer, buffer, size, bigEndian, channelCache) #define MESSAGE_DECODE_DATA decodeData(decodeBuffer, buffer, size, bigEndian, channelCache) #else #define MESSAGE_ENCODE_DATA #define MESSAGE_DECODE_DATA #endif // // Prologue an epilogue of the message // handling functions. // #define MESSAGE_BEGIN_ENCODE_SIZE \ \ void MESSAGE_STORE::encodeSize(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \ const unsigned int size, int bigEndian, \ ChannelCache *channelCache) const \ { #define MESSAGE_END_ENCODE_SIZE \ \ } #define MESSAGE_BEGIN_DECODE_SIZE \ \ void MESSAGE_STORE::decodeSize(DecodeBuffer &decodeBuffer, unsigned char *&buffer, \ unsigned int &size, unsigned char type, int bigEndian, \ WriteBuffer *writeBuffer, ChannelCache *channelCache) const \ { #define MESSAGE_END_DECODE_SIZE \ \ } #define MESSAGE_BEGIN_ENCODE_MESSAGE \ \ int MESSAGE_STORE::encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \ const unsigned int size, int bigEndian, \ ChannelCache *channelCache) const \ { \ MESSAGE_ENCODE_SIZE; #define MESSAGE_END_ENCODE_MESSAGE \ \ MESSAGE_ENCODE_DATA; \ \ return 1; \ } #define MESSAGE_BEGIN_DECODE_MESSAGE \ \ int MESSAGE_STORE::decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, \ unsigned int &size, unsigned char type, int bigEndian, \ WriteBuffer *writeBuffer, ChannelCache *channelCache) const \ { \ MESSAGE_DECODE_SIZE; #define MESSAGE_END_DECODE_MESSAGE \ \ MESSAGE_DECODE_DATA; \ \ return 1; \ } #define MESSAGE_BEGIN_ENCODE_DATA \ \ void MESSAGE_STORE::encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \ unsigned int size, int bigEndian, \ ChannelCache *channelCache) const \ { #define MESSAGE_END_ENCODE_DATA \ \ } #define MESSAGE_BEGIN_DECODE_DATA \ \ void MESSAGE_STORE::decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, \ unsigned int size, int bigEndian, \ ChannelCache *channelCache) const \ { #define MESSAGE_END_DECODE_DATA \ \ } #define MESSAGE_BEGIN_PARSE_IDENTITY \ \ int MESSAGE_STORE::parseIdentity(Message *message, const unsigned char *buffer, \ unsigned int size, int bigEndian) const \ { #define MESSAGE_END_PARSE_IDENTITY \ \ return 1; \ \ } #define MESSAGE_BEGIN_UNPARSE_IDENTITY \ \ int MESSAGE_STORE::unparseIdentity(const Message *message, unsigned char *buffer, \ unsigned int size, int bigEndian) const \ { #define MESSAGE_END_UNPARSE_IDENTITY \ \ return 1; \ \ } #define MESSAGE_BEGIN_IDENTITY_CHECKSUM \ \ void MESSAGE_STORE::identityChecksum(const Message *message, const unsigned char *buffer, \ unsigned int size, md5_state_t *md5_state, \ int bigEndian) const \ { #define MESSAGE_END_IDENTITY_CHECKSUM \ \ } #define MESSAGE_BEGIN_ENCODE_UPDATE \ \ void MESSAGE_STORE::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, \ const Message *cachedMessage, \ ChannelCache *channelCache) const \ { #define MESSAGE_END_ENCODE_UPDATE \ \ } #define MESSAGE_BEGIN_DECODE_UPDATE \ \ void MESSAGE_STORE::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, \ ChannelCache *channelCache) const \ { #define MESSAGE_END_DECODE_UPDATE \ \ } #endif /* RenderMinorExtensionTags_H */ nxcomp/RenderFreePicture.cpp0000644000076400007640000001144411323113030016402 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderFreePicture.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeFreeXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> renderFreePictureCache); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { unsigned int value; ClientCache *clientCache = (ClientCache *) channelCache; *(buffer + 1) = type; decodeBuffer.decodeFreeXidValue(value, clientCache -> renderFreePictureCache); PutULONG(value, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.free_picture.type = *(buffer + 1); renderExtension -> data.free_picture.src_id = GetULONG(buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.free_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.free_picture.type; PutULONG(renderExtension -> data.free_picture.src_id, buffer + 4, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.free_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 3); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeFreeXidValue(renderExtension -> data.free_picture.src_id, clientCache -> renderFreePictureCache); cachedRenderExtension -> data.free_picture.src_id = renderExtension -> data.free_picture.src_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.free_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeFreeXidValue(renderExtension -> data.free_picture.src_id, clientCache -> renderFreePictureCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.free_picture.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/PolyPoint.cpp0000644000076400007640000001227611323113030014766 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "PolyPoint.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int PolyPointStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { PolyPointMessage *polyPoint = (PolyPointMessage *) message; // // Here is the fingerprint. // polyPoint -> mode = *(buffer + 1); polyPoint -> drawable = GetULONG(buffer + 4, bigEndian); polyPoint -> gcontext = GetULONG(buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int PolyPointStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { PolyPointMessage *polyPoint = (PolyPointMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = polyPoint -> mode; PutULONG(polyPoint -> drawable, buffer + 4, bigEndian); PutULONG(polyPoint -> gcontext, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void PolyPointStore::dumpIdentity(const Message *message) const { #ifdef DUMP PolyPointMessage *polyPoint = (PolyPointMessage *) message; *logofs << name() << ": Identity drawable " << polyPoint -> drawable << ", gcontext " << polyPoint -> gcontext << ", size " << polyPoint -> size_ << ".\n" << logofs_flush; #endif } void PolyPointStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { if (control -> isProtoStep8() == 1) { md5_append(md5_state_, buffer + 1, 1); } } void PolyPointStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { PolyPointMessage *polyPoint = (PolyPointMessage *) message; PolyPointMessage *cachedPolyPoint = (PolyPointMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; if (control -> isProtoStep8() == 0) { encodeBuffer.encodeBoolValue((unsigned int) polyPoint -> mode); } #ifdef TEST *logofs << name() << ": Encoding value " << polyPoint -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyPoint -> drawable, clientCache -> drawableCache); cachedPolyPoint -> drawable = polyPoint -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << polyPoint -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyPoint -> gcontext, clientCache -> gcCache); cachedPolyPoint -> gcontext = polyPoint -> gcontext; } void PolyPointStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { PolyPointMessage *polyPoint = (PolyPointMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; if (control -> isProtoStep8() == 0) { decodeBuffer.decodeBoolValue(value); polyPoint -> mode = value; } decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); polyPoint -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyPoint -> drawable << " as drawable field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); polyPoint -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyPoint -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } nxcomp/InternAtom.cpp0000644000076400007640000000711411323113031015105 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "InternAtom.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int InternAtomStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { InternAtomMessage *internAtom = (InternAtomMessage *) message; // // Here is the fingerprint. // internAtom -> only_if_exists = *(buffer + 1); internAtom -> name_length = GetUINT(buffer + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif // // Clean up padding bytes. // if ((int) size > dataOffset) { unsigned char *end = ((unsigned char *) buffer) + size; for (unsigned char *pad = ((unsigned char *) buffer) + 8 + internAtom -> name_length; pad < end; pad++) { *pad = 0; } } return 1; } int InternAtomStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { InternAtomMessage *internAtom = (InternAtomMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = internAtom -> only_if_exists; PutUINT(internAtom -> name_length, buffer + 4, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void InternAtomStore::dumpIdentity(const Message *message) const { #ifdef DUMP InternAtomMessage *internAtom = (InternAtomMessage *) message; *logofs << name() << ": Identity only_if_exists " << (unsigned int) internAtom -> only_if_exists << ", name_length " << internAtom -> name_length << ", name '"; for (int i = 0; i < internAtom -> name_length; i++) { *logofs << internAtom -> data_[i]; } *logofs << "'.\n" << logofs_flush; #endif } void InternAtomStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { md5_append(md5_state_, buffer + 1, 1); md5_append(md5_state_, buffer + 4, 2); } nxcomp/install-sh0000755000076400007640000001124511323113027014332 0ustar svetonisvetoni#! /bin/sh # # install - install a program, script, or datafile # This comes from X11R5. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. # # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 nxcomp/CreatePixmapCompat.h0000644000076400007640000001070211323113026016221 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef CreatePixmapCompat_H #define CreatePixmapCompat_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define CREATEPIXMAP_ENABLE_CACHE 1 #define CREATEPIXMAP_ENABLE_DATA 0 #define CREATEPIXMAP_ENABLE_SPLIT 0 #define CREATEPIXMAP_ENABLE_COMPRESS 0 #define CREATEPIXMAP_DATA_LIMIT 16 #define CREATEPIXMAP_DATA_OFFSET 16 #define CREATEPIXMAP_CACHE_SLOTS 1000 #define CREATEPIXMAP_CACHE_THRESHOLD 2 #define CREATEPIXMAP_CACHE_LOWER_THRESHOLD 1 // // The message class. // class CreatePixmapCompatMessage : public Message { friend class CreatePixmapCompatStore; public: CreatePixmapCompatMessage() { } ~CreatePixmapCompatMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char depth; unsigned int id; unsigned int drawable; unsigned short width; unsigned short height; }; class CreatePixmapCompatStore : public MessageStore { public: CreatePixmapCompatStore(); virtual ~CreatePixmapCompatStore(); virtual const char *name() const { return "CreatePixmapCompat"; } virtual unsigned char opcode() const { return X_CreatePixmap; } virtual unsigned int storage() const { return sizeof(CreatePixmapCompatMessage); } // // Message handling methods. // protected: virtual Message *create() const { return new CreatePixmapCompatMessage(); } virtual Message *create(const Message &message) const { return new CreatePixmapCompatMessage((const CreatePixmapCompatMessage &) message); } virtual void destroy(Message *message) const { delete (CreatePixmapCompatMessage *) message; } virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const; virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const; virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* CreatePixmapCompat_H */ nxcomp/ClientProxy.cpp0000644000076400007640000003111511323113030015302 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Socket.h" #include "Agent.h" #include "ClientProxy.h" #include "ClientChannel.h" #include "GenericChannel.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Log the operations related to sending // and receiving the control tokens. // #undef TOKEN ClientProxy::ClientProxy(int proxyFd) : Proxy(proxyFd) { fontServerPort_ = NULL; #ifdef DEBUG *logofs << "ClientProxy: Created new object at " << this << ".\n" << logofs_flush; #endif } ClientProxy::~ClientProxy() { delete [] fontServerPort_; #ifdef DEBUG *logofs << "ClientProxy: Deleted object at " << this << ".\n" << logofs_flush; #endif } void ClientProxy::handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily, sockaddr * xServerAddr, unsigned int xServerAddrLength) { #ifdef DEBUG *logofs << "ClientProxy: No display configuration to set.\n" << logofs_flush; #endif } void ClientProxy::handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort, int httpServerPort, const char *fontServerPort) { delete [] fontServerPort_; fontServerPort_ = new char[strlen(fontServerPort) + 1]; strcpy(fontServerPort_, fontServerPort); #ifdef DEBUG *logofs << "ClientProxy: Set port configuration to font '" << fontServerPort_ << "'.\n" << logofs_flush; #endif } int ClientProxy::handleNewConnection(T_channel_type type, int clientFd) { switch (type) { case channel_x11: { return handleNewXConnection(clientFd); } case channel_cups: { return handleNewGenericConnection(clientFd, channel_cups, "CUPS"); } case channel_smb: { return handleNewGenericConnection(clientFd, channel_smb, "SMB"); } case channel_media: { return handleNewGenericConnection(clientFd, channel_media, "media"); } case channel_http: { return handleNewGenericConnection(clientFd, channel_http, "HTTP"); } case channel_slave: { return handleNewSlaveConnection(clientFd); } default: { #ifdef PANIC *logofs << "ClientProxy: PANIC! Unsupported channel with type '" << getTypeName(type) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Unsupported channel with type '" << getTypeName(type) << "'.\n"; return -1; } } } int ClientProxy::handleNewConnectionFromProxy(T_channel_type type, int channelId) { switch (type) { case channel_font: { int port = atoi(fontServerPort_); if (port > 0) { // // Connect on the TCP port number. // return handleNewGenericConnectionFromProxy(channelId, channel_font, "localhost", port, "font"); } else { // // Connect to the Unix path. // return handleNewGenericConnectionFromProxy(channelId, channel_font, "localhost", fontServerPort_, "font"); } } case channel_slave: { return handleNewSlaveConnectionFromProxy(channelId); } default: { #ifdef PANIC *logofs << "ClientProxy: PANIC! Unsupported channel with type '" << getTypeName(type) << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Unsupported channel with type '" << getTypeName(type) << "'.\n"; return -1; } } } int ClientProxy::handleNewAgentConnection(Agent *agent) { int clientFd = agent -> getLocalFd(); int channelId = allocateChannelMap(clientFd); if (channelId == -1) { #ifdef PANIC *logofs << "ClientProxy: PANIC! Maximum mumber of available " << "channels exceeded.\n" << logofs_flush; #endif cerr << "Error" << ": Maximum mumber of available " << "channels exceeded.\n"; return -1; } transports_[channelId] = agent -> getTransport(); agent_ = channelId; return handleNewXConnection(clientFd); } int ClientProxy::handleNewXConnection(int clientFd) { int channelId = getChannel(clientFd); // // Check if the channel has been // already mapped. // if (channelId == -1) { channelId = allocateChannelMap(clientFd); if (channelId == -1) { #ifdef PANIC *logofs << "ClientProxy: PANIC! Maximum mumber of available " << "channels exceeded.\n" << logofs_flush; #endif cerr << "Error" << ": Maximum mumber of available " << "channels exceeded.\n"; return -1; } } #ifdef TEST *logofs << "ClientProxy: X client descriptor FD#" << clientFd << " mapped to channel ID#" << channelId << ".\n" << logofs_flush; #endif // // Turn queuing off for path proxy-to-X-client. // if (control -> OptionClientNoDelay == 1) { SetNoDelay(clientFd, control -> OptionClientNoDelay); } // // If requested, set the size of the TCP send // and receive buffers. // if (control -> OptionClientSendBuffer != -1) { SetSendBuffer(clientFd, control -> OptionClientSendBuffer); } if (control -> OptionClientReceiveBuffer != -1) { SetReceiveBuffer(clientFd, control -> OptionClientReceiveBuffer); } if (allocateTransport(clientFd, channelId) < 0) { return -1; } // // Starting from protocol level 3 client and server // caches are created in proxy and shared between all // channels. If remote proxy has older protocol level // pointers are NULL and channels must create their // own instances. // channels_[channelId] = new ClientChannel(transports_[channelId], compressor_); if (channels_[channelId] == NULL) { deallocateTransport(channelId); return -1; } increaseChannels(channelId); // // Propagate channel stores and caches to the new // channel. // channels_[channelId] -> setOpcodes(opcodeStore_); channels_[channelId] -> setStores(clientStore_, serverStore_); channels_[channelId] -> setCaches(clientCache_, serverCache_); int port = atoi(fontServerPort_); if (port > 0 || *fontServerPort_ != '\0') { channels_[channelId] -> setPorts(1); } if (handleControl(code_new_x_connection, channelId) < 0) { return -1; } // // Let channel configure itself according // to control parameters. // channels_[channelId] -> handleConfiguration(); return 1; } int ClientProxy::handleNewXConnectionFromProxy(int channelId) { #ifdef PANIC *logofs << "ClientProxy: PANIC! Can't create a new X channel " << "with ID#" << channelId << " at this side.\n" << logofs_flush; #endif cerr << "Error" << ": Can't create a new X channel " << "with ID#" << channelId << " at this side.\n"; return -1; } int ClientProxy::handleLoad(T_load_type type) { int channelCount = getChannels(channel_x11); if ((channelCount == 0 && type == load_if_first) || (channelCount > 0 && type == load_if_any)) { #ifdef TEST *logofs << "ClientProxy: Going to load content of client store.\n" << logofs_flush; #endif int result = handleLoadStores(); if (result == 1) { if (handleControl(code_load_request) < 0) { return -1; } priority_ = 1; } else if (result < 0) { #ifdef WARNING *logofs << "ClientProxy: WARNING! Failed to load content " << "of persistent cache.\n" << logofs_flush; #endif // // Don't abort the proxy connection in the case // of a corrupted cache. By not sending the load // message to the remote peer, both sides will // start encoding messages using empty stores. // This behaviour is compatible with old proxy // versions. // if (channelCount == 0 && type == load_if_first) { if (handleResetStores() < 0) { #ifdef PANIC *logofs << "ClientProxy: PANIC! Failed to reset message stores.\n" << logofs_flush; #endif return -1; } } else { return -1; } } } else { #ifdef PANIC *logofs << "ClientProxy: PANIC! Can't load the stores with " << channelCount << " remaining channels.\n" << logofs_flush; #endif return -1; } return 1; } int ClientProxy::handleSave() { // // If no more X channels are remaining // then save content of message stores. // int channelCount = getChannels(channel_x11); if (channelCount == 0) { int result = handleSaveStores(); if (result == 1) { if (handleControl(code_save_request) < 0) { return -1; } priority_ = 1; return 1; } else if (result < 0) { #ifdef PANIC *logofs << "ClientProxy: PANIC! Failed to save stores " << "to persistent cache.\n" << logofs_flush; #endif return -1; } } else { #ifdef PANIC *logofs << "ClientProxy: PANIC! Can't save the stores with " << channelCount << " remaining channels.\n" << logofs_flush; #endif return -1; } return 1; } int ClientProxy::handleAsyncEvents() { if (canRead() == 1) { #if defined(TEST) || defined(INFO) *logofs << "Proxy: WARNING! Reading while writing " << "with data available on the proxy link.\n" << logofs_flush; #endif if (handleRead() < 0) { return -1; } return 1; } return 0; } int ClientProxy::handleLoadFromProxy() { #ifdef PANIC *logofs << "ClientProxy: PANIC! Invalid load control message " << "received in proxy.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid load control message " << "received in proxy.\n"; return -1; } int ClientProxy::handleSaveFromProxy() { #ifdef PANIC *logofs << "ClientProxy: PANIC! Invalid save control message " << "received in proxy.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid save control message " << "received in proxy.\n"; return -1; } int ClientProxy::handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream, md5_state_t *md5StateClient) const { if (clientStore_ -> saveRequestStores(cachefs, md5StateStream, md5StateClient, use_checksum, discard_data) < 0) { return -1; } else if (serverStore_ -> saveReplyStores(cachefs, md5StateStream, md5StateClient, discard_checksum, use_data) < 0) { return -1; } else if (serverStore_ -> saveEventStores(cachefs, md5StateStream, md5StateClient, discard_checksum, use_data) < 0) { return -1; } return 1; } int ClientProxy::handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const { if (clientStore_ -> loadRequestStores(cachefs, md5StateStream, use_checksum, discard_data) < 0) { return -1; } else if (serverStore_ -> loadReplyStores(cachefs, md5StateStream, discard_checksum, use_data) < 0) { return -1; } else if (serverStore_ -> loadEventStores(cachefs, md5StateStream, discard_checksum, use_data) < 0) { return -1; } return 1; } nxcomp/Pack.c0000644000076400007640000001022511323113027013345 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #define PANIC #define WARNING #undef TEST #undef DEBUG #ifdef __cplusplus extern "C" { #endif #include #include "NXpack.h" const ColorMask Mask8TrueColor = { 128, 63, 240, 7 }; const ColorMask Mask64TrueColor = { 192, 7, 240, 4 }; const ColorMask Mask256TrueColor = { 255, 0, 255, 0 }; const ColorMask Mask512TrueColor = { 224, 5, 240, 4 }; const ColorMask Mask4KTrueColor = { 240, 4, 240, 2 }; const ColorMask Mask32KTrueColor = { 248, 3, 248, 2 }; const ColorMask Mask64KTrueColor = { 255, 0, 255, 0 }; const ColorMask Mask256KTrueColor = { 252, 1, 252, 1 }; const ColorMask Mask2MTrueColor = { 255, 0, 254, 1 }; const ColorMask Mask16MTrueColor = { 255, 0, 255, 0 }; const ColorMask *MethodColorMask(unsigned int method) { switch (method) { case MASK_8_COLORS: { return &Mask8TrueColor; } case MASK_64_COLORS: { return &Mask64TrueColor; } case MASK_256_COLORS: { return &Mask256TrueColor; } case MASK_512_COLORS: { return &Mask512TrueColor; } case MASK_4K_COLORS: { return &Mask4KTrueColor; } case MASK_32K_COLORS: { return &Mask32KTrueColor; } case MASK_64K_COLORS: { return &Mask64KTrueColor; } case MASK_256K_COLORS: { return &Mask256KTrueColor; } case MASK_2M_COLORS: { return &Mask2MTrueColor; } case MASK_16M_COLORS: { return &Mask16MTrueColor; } default: { return NULL; } } } int MethodBitsPerPixel(unsigned int method) { switch (method) { case PACK_MASKED_8_COLORS: case PACK_JPEG_8_COLORS: case PACK_PNG_8_COLORS: { return 8; } case PACK_MASKED_64_COLORS: case PACK_JPEG_64_COLORS: case PACK_PNG_64_COLORS: { return 8; } case PACK_MASKED_256_COLORS: case PACK_JPEG_256_COLORS: case PACK_PNG_256_COLORS: { return 8; } case PACK_MASKED_512_COLORS: case PACK_JPEG_512_COLORS: case PACK_PNG_512_COLORS: { return 16; } case PACK_MASKED_4K_COLORS: case PACK_JPEG_4K_COLORS: case PACK_PNG_4K_COLORS: { return 16; } case PACK_MASKED_32K_COLORS: case PACK_JPEG_32K_COLORS: case PACK_PNG_32K_COLORS: { return 16; } case PACK_MASKED_64K_COLORS: case PACK_JPEG_64K_COLORS: case PACK_PNG_64K_COLORS: { return 16; } case PACK_MASKED_256K_COLORS: case PACK_JPEG_256K_COLORS: case PACK_PNG_256K_COLORS: { return 24; } case PACK_MASKED_2M_COLORS: case PACK_JPEG_2M_COLORS: case PACK_PNG_2M_COLORS: { return 24; } case PACK_MASKED_16M_COLORS: case PACK_JPEG_16M_COLORS: case PACK_PNG_16M_COLORS: { return 24; } case PACK_BITMAP_16M_COLORS: case PACK_RGB_16M_COLORS: case PACK_RLE_16M_COLORS: { return 24; } default: { return 0; } } } #ifdef __cplusplus } #endif nxcomp/RenderFillRectangles.cpp0000644000076400007640000001535311323113027017074 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ // // Include the template for // this message class. // #include "RenderFillRectangles.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #include MESSAGE_TAGS // // Message handling methods. // MESSAGE_BEGIN_ENCODE_SIZE { // // The color structure (4 components, 2 bytes // each) is included in the data part, so that // it gets into the checksum. The rectangles // are in the format x, y, width, height with // 2 bytes per each field, so each request is // at least 12 + 8 + 8 = 28 bytes long. // ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, clientCache -> renderLengthCache, 5); #ifdef TEST *logofs << name() << ": Encoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_SIZE MESSAGE_BEGIN_DECODE_SIZE { ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeCachedValue(size, 16, clientCache -> renderLengthCache, 5); size = MESSAGE_OFFSET + (size << 2); buffer = writeBuffer -> addMessage(size); #ifdef TEST *logofs << name() << ": Decoded size with value " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_SIZE MESSAGE_BEGIN_ENCODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> renderSrcPictureCache); #ifdef TEST *logofs << name() << ": Encoded message. Type is " << (unsigned int) *(buffer + 1) << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_MESSAGE MESSAGE_BEGIN_DECODE_MESSAGE { ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; *(buffer + 1) = type; decodeBuffer.decodeCachedValue(*(buffer + 4), 8, clientCache -> renderOpCache); decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); PutULONG(value, buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Decoded message. Type is " << (unsigned int) type << " size is " << size << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_MESSAGE MESSAGE_BEGIN_ENCODE_DATA { encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_DATA MESSAGE_BEGIN_DECODE_DATA { decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET, size, bigEndian, channelCache); #ifdef TEST *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET << " bytes of data.\n" << logofs_flush; #endif } MESSAGE_END_DECODE_DATA MESSAGE_BEGIN_PARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; renderExtension -> data.fill_rectangles.type = *(buffer + 1); renderExtension -> data.fill_rectangles.op = *(buffer + 4); renderExtension -> data.fill_rectangles.dst_id = GetULONG(buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Parsed identity. Type is " << (unsigned int) renderExtension -> data.fill_rectangles.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_PARSE_IDENTITY MESSAGE_BEGIN_UNPARSE_IDENTITY { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; *(buffer + 1) = renderExtension -> data.fill_rectangles.type; *(buffer + 4) = renderExtension -> data.fill_rectangles.op; PutULONG(renderExtension -> data.fill_rectangles.dst_id, buffer + 8, bigEndian); #ifdef TEST *logofs << name() << ": Unparsed identity. Type is " << (unsigned int) renderExtension -> data.fill_rectangles.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_UNPARSE_IDENTITY MESSAGE_BEGIN_IDENTITY_CHECKSUM { md5_append(md5_state, buffer + 1, 4); } MESSAGE_END_IDENTITY_CHECKSUM MESSAGE_BEGIN_ENCODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; encodeBuffer.encodeXidValue(renderExtension -> data.fill_rectangles.dst_id, clientCache -> renderSrcPictureCache); cachedRenderExtension -> data.fill_rectangles.dst_id = renderExtension -> data.fill_rectangles.dst_id; #ifdef TEST *logofs << name() << ": Encoded update. Type is " << (unsigned int) renderExtension -> data.fill_rectangles.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_ENCODE_UPDATE MESSAGE_BEGIN_DECODE_UPDATE { RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; decodeBuffer.decodeXidValue(renderExtension -> data.fill_rectangles.dst_id, clientCache -> renderSrcPictureCache); #ifdef TEST *logofs << name() << ": Decoded update. Type is " << (unsigned int) renderExtension -> data.fill_rectangles.type << " size is " << renderExtension -> size_ << ".\n" << logofs_flush; #endif } MESSAGE_END_DECODE_UPDATE nxcomp/PolyPoint.h0000644000076400007640000001112211323113027014426 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PolyPoint_H #define PolyPoint_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define POLYPOINT_ENABLE_CACHE 1 #define POLYPOINT_ENABLE_DATA 0 #define POLYPOINT_ENABLE_SPLIT 0 #define POLYPOINT_ENABLE_COMPRESS 0 #define POLYPOINT_DATA_LIMIT 3200 #define POLYPOINT_DATA_OFFSET 12 #define POLYPOINT_CACHE_SLOTS 3000 #define POLYPOINT_CACHE_THRESHOLD 3 #define POLYPOINT_CACHE_LOWER_THRESHOLD 1 // // The message class. // class PolyPointMessage : public Message { friend class PolyPointStore; public: PolyPointMessage() { } ~PolyPointMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char mode; unsigned int drawable; unsigned int gcontext; }; class PolyPointStore : public MessageStore { // // Constructors and destructors. // public: PolyPointStore() : MessageStore() { enableCache = POLYPOINT_ENABLE_CACHE; enableData = POLYPOINT_ENABLE_DATA; enableSplit = POLYPOINT_ENABLE_SPLIT; enableCompress = POLYPOINT_ENABLE_COMPRESS; dataLimit = POLYPOINT_DATA_LIMIT; dataOffset = POLYPOINT_DATA_OFFSET; cacheSlots = POLYPOINT_CACHE_SLOTS; cacheThreshold = POLYPOINT_CACHE_THRESHOLD; cacheLowerThreshold = POLYPOINT_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~PolyPointStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "PolyPoint"; } virtual unsigned char opcode() const { return X_PolyPoint; } virtual unsigned int storage() const { return sizeof(PolyPointMessage); } // // Message handling methods. // public: virtual Message *create() const { return new PolyPointMessage(); } virtual Message *create(const Message &message) const { return new PolyPointMessage((const PolyPointMessage &) message); } virtual void destroy(Message *message) const { delete (PolyPointMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* PolyPoint_H */ nxcomp/PolyText8.h0000644000076400007640000001110211323113026014346 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef PolyText8_H #define PolyText8_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define POLYTEXT8_ENABLE_CACHE 1 #define POLYTEXT8_ENABLE_DATA 0 #define POLYTEXT8_ENABLE_SPLIT 0 #define POLYTEXT8_ENABLE_COMPRESS 0 #define POLYTEXT8_DATA_LIMIT 380 #define POLYTEXT8_DATA_OFFSET 16 #define POLYTEXT8_CACHE_SLOTS 3000 #define POLYTEXT8_CACHE_THRESHOLD 5 #define POLYTEXT8_CACHE_LOWER_THRESHOLD 1 // // The message class. // class PolyText8Message : public Message { friend class PolyText8Store; public: PolyText8Message() { } ~PolyText8Message() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int drawable; unsigned int gcontext; unsigned short x; unsigned short y; }; class PolyText8Store : public MessageStore { // // Constructors and destructors. // public: PolyText8Store() : MessageStore() { enableCache = POLYTEXT8_ENABLE_CACHE; enableData = POLYTEXT8_ENABLE_DATA; enableSplit = POLYTEXT8_ENABLE_SPLIT; enableCompress = POLYTEXT8_ENABLE_COMPRESS; dataLimit = POLYTEXT8_DATA_LIMIT; dataOffset = POLYTEXT8_DATA_OFFSET; cacheSlots = POLYTEXT8_CACHE_SLOTS; cacheThreshold = POLYTEXT8_CACHE_THRESHOLD; cacheLowerThreshold = POLYTEXT8_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~PolyText8Store() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "PolyText8"; } virtual unsigned char opcode() const { return X_PolyText8; } virtual unsigned int storage() const { return sizeof(PolyText8Message); } // // Message handling methods. // public: virtual Message *create() const { return new PolyText8Message(); } virtual Message *create(const Message &message) const { return new PolyText8Message((const PolyText8Message &) message); } virtual void destroy(Message *message) const { delete (PolyText8Message *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* PolyText8_H */ nxcomp/RenderPictureTransform.h0000644000076400007640000000467311323113030017147 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderPictureTransform_H #define RenderPictureTransform_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderPictureTransform" #undef MESSAGE_STORE #define MESSAGE_STORE RenderPictureTransformStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 8 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return MESSAGE_OFFSET; } #include MESSAGE_METHODS }; #endif /* RenderPictureTransform_H */ nxcomp/ClientChannel.h0000644000076400007640000003034711323113027015212 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ClientChannel_H #define ClientChannel_H #include "List.h" #include "Channel.h" #include "SequenceQueue.h" #include "ClientReadBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // If defined, the client channel will // have the chance of suppressing more // opcodes for test purposes. // #undef LAME // // Define this to log a line when a // channel is created or destroyed. // #undef REFERENCES // // This class implements the X client // side compression of the protocol. // class ClientChannel : public Channel { public: ClientChannel(Transport *transport, StaticCompressor *compressor); virtual ~ClientChannel(); virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, unsigned int length); virtual int handleWrite(const unsigned char *message, unsigned int length); virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store, T_store_action action, int position, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, T_store_action action, int position, unsigned char &opcode, unsigned char *&buffer, unsigned int &size) { return 0; } virtual int handleSplit(EncodeBuffer &encodeBuffer); virtual int handleSplit(DecodeBuffer &decodeBuffer) { return 0; } virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split); virtual int handleSplitEvent(DecodeBuffer &decodeBuffer); virtual int handleMotion(EncodeBuffer &encodeBuffer) { return 0; } virtual int handleCompletion(EncodeBuffer &encodeBuffer) { return 0; } virtual int handleConfiguration(); virtual int handleFinish(); virtual int handleAsyncEvents() { return 0; } virtual int needSplit() const { #if defined(TEST) || defined(SPLIT) *logofs << "needSplit: SPLIT! Returning pending split " << "flag " << splitState_.pending << " with " << clientStore_ -> getSplitTotalSize() << " splits in the split stores.\n" << logofs_flush; #endif return splitState_.pending; } virtual int needMotion() const { return 0; } virtual T_channel_type getType() const { return channel_x11; } int setBigEndian(int flag); // // Initialize the static members. // static int setReferences(); private: int handleFastReadRequest(EncodeBuffer &encodeBuffer, const unsigned char &opcode, const unsigned char *&buffer, const unsigned int &size); int handleFastWriteReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); int handleFastWriteEvent(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Intercept the request before the opcode // is encoded. // int handleTaintRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size) { if (control -> isProtoStep7() == 0) { if (opcode == X_NXFreeSplit || opcode == X_NXAbortSplit || opcode == X_NXFinishSplit) { return handleTaintSplitRequest(opcode, buffer, size); } else if (opcode == X_NXSetCacheParameters) { return handleTaintCacheRequest(opcode, buffer, size); } else if (opcode == X_NXGetFontParameters) { return handleTaintFontRequest(opcode, buffer, size); } } if (control -> TaintReplies > 0 && opcode == X_GetInputFocus) { return handleTaintSyncRequest(opcode, buffer, size); } #ifdef LAME return handleTaintLameRequest(opcode, buffer, size); #endif return 0; } int handleTaintCacheRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size); int handleTaintFontRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size); int handleTaintSplitRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size); int handleTaintLameRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size); int handleTaintSyncRequest(unsigned char &opcode, const unsigned char *&buffer, unsigned int &size); int handleTaintSyncError(unsigned char opcode); // // How to handle sequence counter // in notification event. // enum T_sequence_mode { sequence_immediate, sequence_deferred }; // // Send split notifications to the // agent. // int handleRestart(T_sequence_mode mode, int resource); int handleNotify(T_notification_type type, T_sequence_mode mode, int resource, int request, int position); // // Other utility functions used in // handling of the image streaming. // int mustSplitMessage(int resource) { return (clientStore_ -> getSplitStore(resource) -> getSize() != 0); } int canSplitMessage(T_split_mode mode, unsigned int size) { return ((int) size >= control -> SplitDataThreshold && (clientStore_ -> getSplitTotalStorageSize() < control -> SplitTotalStorageSize && clientStore_ -> getSplitTotalSize() < control -> SplitTotalSize)); } int canSendSplit(Split *split) { return (split -> getMode() != split_sync || split -> getState() == split_missed || split -> getState() == split_loaded); } int handleSplitSend(EncodeBuffer &encodeBuffer, int resource, int &total, int &bytes); Split *handleSplitFind(T_checksum checksum, int resource); int handleSplitChecksum(EncodeBuffer &encodeBuffer, T_checksum checksum); void handleSplitEnable() { if (control -> isProtoStep7() == 0) { #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitEnable: WARNING! Disabling split " << "with an old proxy version.\n" << logofs_flush; #endif enableSplit_ = 0; } } void handleSplitPending(int resource) { if (splitState_.pending == 0) { if (clientStore_ -> getSplitStore(resource) != NULL && clientStore_ -> getSplitStore(resource) -> getFirstSplit() != NULL) { splitState_.pending = canSendSplit(clientStore_ -> getSplitStore(resource) -> getFirstSplit()); #if defined(TEST) || defined(SPLIT) *logofs << "handleSplitPending: SPLIT! Set the pending " << "split flag to " << splitState_.pending << " with " << clientStore_ -> getSplitTotalSize() << " splits in the split stores.\n" << logofs_flush; #endif } } } // // Scan all the split stores to find // if there is any split to send. // void handleSplitPending(); // // Handle the MIT-SHM initialization // messages exchanged with the remote // proxy. // int handleShmemRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); int handleShmemReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Query the port used to tunnel // the font server connections. // int handleFontRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); int handleFontReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, unsigned char *&buffer, unsigned int &size); // // Let the agent set the cache // policy for image requests. // int handleCacheRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); // // Encode the start and end split // requests. // int handleStartSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); int handleEndSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); // // Empty a split store and send the // restart event. // int handleAbortSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); // // Force the proxy to finalize all // the pending split operations and // restart a resource. // int handleFinishSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); // // Tell the remote peer to send the // split requests to the X server. // int handleCommitSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, const unsigned char *buffer, const unsigned int size); // // Other utilities. // void handleDecodeCharInfo(DecodeBuffer &, unsigned char *); // // Own read buffer. It is able to identify // full messages read from the X descriptor. // ClientReadBuffer readBuffer_; // // Sequence number of last request coming // from the X client or the X server. // unsigned int clientSequence_; unsigned int serverSequence_; // // Last sequence number known by client. It can // be the real sequence generated by server or // the one of the last auto-generated event. // unsigned int lastSequence_; // // Used to identify replies based on sequence // number of original request. // SequenceQueue sequenceQueue_; // // This is used to test the synchronous flush // in the proxy. // int lastRequest_; // // Current resource id selected as target and // other information related to the image split. // The pending and abort flags are set when we // want the proxy to give us a chance to send // more split data. We also save the position // of the last commit operation performed by // channel so we can differentially encode the // position of next message to commit. // typedef struct { int resource; int pending; int commit; T_split_mode mode; } T_split_state; T_split_state splitState_; // // List of agent resources. // List splitResources_; // // How many sync requests we // have tainted so far. // int taintCounter_; private: // // Keep track of object // creation and deletion. // #ifdef REFERENCES static int references_; #endif }; #endif /* ClientChannel_H */ nxcomp/PolyFillRectangle.cpp0000644000076400007640000001227611323113030016410 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "PolyFillRectangle.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Here are the methods to handle messages' content. // int PolyFillRectangleStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message; // // Here is the fingerprint. // polyFillRectangle -> drawable = GetULONG(buffer + 4, bigEndian); polyFillRectangle -> gcontext = GetULONG(buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } int PolyFillRectangleStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message; // // Fill all the message's fields. // PutULONG(polyFillRectangle -> drawable, buffer + 4, bigEndian); PutULONG(polyFillRectangle -> gcontext, buffer + 8, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; #endif return 1; } void PolyFillRectangleStore::dumpIdentity(const Message *message) const { #ifdef DUMP PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message; *logofs << name() << ": Identity drawable " << polyFillRectangle -> drawable << ", gcontext " << polyFillRectangle -> gcontext << ", size " << polyFillRectangle -> size_ << ".\n" << logofs_flush; #endif } void PolyFillRectangleStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { } void PolyFillRectangleStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message; PolyFillRectangleMessage *cachedPolyFillRectangle = (PolyFillRectangleMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding value " << polyFillRectangle -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyFillRectangle -> drawable, clientCache -> drawableCache); cachedPolyFillRectangle -> drawable = polyFillRectangle -> drawable; #ifdef DEBUG *logofs << name() << ": Encoding value " << polyFillRectangle -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(polyFillRectangle -> gcontext, clientCache -> gcCache); cachedPolyFillRectangle -> gcontext = polyFillRectangle -> gcontext; } void PolyFillRectangleStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); polyFillRectangle -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyFillRectangle -> drawable << " as drawable field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); polyFillRectangle -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << polyFillRectangle -> gcontext << " as gcontext field.\n" << logofs_flush; #endif } nxcomp/RenderTriangles.h0000644000076400007640000000467311323113031015571 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef RenderTriangles_H #define RenderTriangles_H // // Define the characteristics // of this message class here. // #undef MESSAGE_NAME #define MESSAGE_NAME "RenderTriangles" #undef MESSAGE_STORE #define MESSAGE_STORE RenderTrianglesStore #undef MESSAGE_CLASS #define MESSAGE_CLASS RenderMinorExtensionStore #undef MESSAGE_METHODS #define MESSAGE_METHODS "RenderMinorExtensionMethods.h" #undef MESSAGE_HEADERS #define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" #undef MESSAGE_TAGS #define MESSAGE_TAGS "RenderMinorExtensionTags.h" #undef MESSAGE_OFFSET #define MESSAGE_OFFSET 24 #undef MESSAGE_HAS_SIZE #define MESSAGE_HAS_SIZE 1 #undef MESSAGE_HAS_DATA #define MESSAGE_HAS_DATA 1 #undef MESSAGE_HAS_FILTER #define MESSAGE_HAS_FILTER 0 // // Declare the message class. // #include MESSAGE_HEADERS class MESSAGE_STORE : public MESSAGE_CLASS { public: virtual const char *name() const { return MESSAGE_NAME; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size); } #include MESSAGE_METHODS }; #endif /* RenderTriangles_H */ nxcomp/Loop.cpp0000644000076400007640000131755111354377303013772 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Misc.h" #ifdef __sun #include #endif // // MacOSX 10.4 defines socklen_t. This is // intended to ensure compatibility with // older versions. // #ifdef __APPLE__ #include #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_3 typedef int socklen_t; #endif #endif #ifdef _AIX #include #include #endif #ifndef __CYGWIN32__ #include #include #endif // // NX include files. // #include "NX.h" #include "NXalert.h" #include "Misc.h" #include "Control.h" #include "Socket.h" #include "Statistics.h" #include "Auth.h" #include "Keeper.h" #include "Agent.h" #include "ClientProxy.h" #include "ServerProxy.h" #include "Message.h" // // System specific defines. // #if defined(__EMX__) || defined(__CYGWIN32__) struct sockaddr_un { u_short sun_family; char sun_path[108]; }; #endif // // HP-UX hides this define. // #if defined(hpux) && !defined(RLIM_INFINITY) #define RLIM_INFINITY 0x7fffffff #endif // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Enable log output in signal handler. // This is likely to hang the proxy at // random, at least on Linux. // #undef UNSAFE // // Let all logs go to the standard error. // This is useful to interleave the Xlib // log output with the proxy output in a // single file. // #undef MIXED // // Define this to check if the client and // server caches match at shutdown. This // is a test facility as it requires that // both proxies are running on the same // host. // #undef MATCH // // If defined, reduce the size of the log // file and be sure it never exceeds the // limit. // #undef QUOTA // // If defined, force very strict limits for // the proxy tokens and force the proxy to // enter often in congestion state. // #undef STRICT // // Print a line in the log if the time we // spent inside the select or handling the // messages exceeded a given time value. // #undef TIME // // This can be useful when testing the forwarding // of the SSHD connection by nxssh to the agent. // The debug output will go to a well known file // that will be opened also by nxssh when BINDER // is enabled there. // #undef BINDER // // Define this to override the limits on // the core dump size. // #define COREDUMPS // // Upper limit of pre-allocated buffers // for string parameters. // #define DEFAULT_STRING_LENGTH 256 // // Maximum length of remote options data // passed by peer proxy at startup. // #define DEFAULT_REMOTE_OPTIONS_LENGTH 512 // // Maximum length of NX display string. // #define DEFAULT_DISPLAY_OPTIONS_LENGTH 1024 // // Maximum number of cache file names to // send to the server side. // #define DEFAULT_REMOTE_CACHE_ENTRIES 100 // // Maximum length of remote options string // that can be received from the peer proxy. // #define MAXIMUM_REMOTE_OPTIONS_LENGTH 4096 // // Macro is true if we determined our proxy // mode. // #define WE_SET_PROXY_MODE (control -> ProxyMode != proxy_undefined) // // Macro is true if our side is the one that // should connect to remote. // #define WE_INITIATE_CONNECTION (*connectHost != '\0') // // Is true if we must provide our credentials // to the remote peer. // #define WE_PROVIDE_CREDENTIALS (control -> ProxyMode == proxy_server) // // Is true if we listen for a local forwarder // that will tunnel the traffic through a SSH // or HTTP link. // #define WE_LISTEN_FORWARDER (control -> ProxyMode == proxy_server && \ listenPort != -1) // // You must define FLUSH in Misc.h if // you want an immediate flush of the // log output. // ostream *logofs = NULL; // // Other stream destriptors used for // logging. // ostream *statofs = NULL; ostream *errofs = NULL; // // Save standard error's rdbuf here // and restore it when exiting. // static streambuf *errsbuf = NULL; // // Allow faults to be recovered by // jumping back into the main loop. // jmp_buf context; // // Provide operational parameters. // Control *control = NULL; // // Collect and print statistics. // Statistics *statistics = NULL; // // Keep data for X11 authentication. // Auth *auth = NULL; // // This class makes the hard work. // Proxy *proxy = NULL; // // Used to handle memory-to-memory // transport to the X agent. // Agent *agent = NULL; // // The image cache house-keeping class. // Keeper *keeper = NULL; // // Callback set by the child process // to be notified about signals. // int (*handler)(int) = NULL; // // Signal handling functions. // void DisableSignals(); void EnableSignals(); void InstallSignals(); static void RestoreSignals(); static void HandleSignal(int signal); // // Signal handling utilities. // static void InstallSignal(int signal, int action); static void RestoreSignal(int signal); static int HandleChildren(); static int HandleChild(int child); static int CheckChild(int pid, int status); static int WaitChild(int child, const char *label, int force); int CheckParent(const char *name, const char *type, int parent); void RegisterChild(int child); static int CheckAbort(); // // Timer handling utilities. // void SetTimer(int timeout); void ResetTimer(); static void HandleTimer(int signal); // // Kill or check a running child. // static int KillProcess(int pid, const char *label, int signal, int wait); static int CheckProcess(int pid, const char *label); // // Macros used to test the pid of a child. // #define IsFailed(pid) ((pid) < 0) #define IsRunning(pid) ((pid) > 1) #define IsNotRunning(pid) ((pid) == 0) #define IsRestarting(pid) ((pid) == 1) #define SetNotRunning(pid) ((pid) = 0) #define SetRestarting(pid) ((pid) = 1) // // Start or restart the house-keeper process. // static int StartKeeper(); // // Cleanup functions. // void CleanupConnections(); void CleanupListeners(); void CleanupSockets(); void CleanupGlobal(); static void CleanupChildren(); static void CleanupLocal(); static void CleanupKeeper(); static void CleanupStreams(); // // Loop forever until the connections // to the peer proxy is dropped. // static void WaitCleanup(); // // Initialization functions. // static int InitBeforeNegotiation(); static int SetupProxyConnection(); static int InitAfterNegotiation(); static int SetupProxyInstance(); static int SetupAuthInstance(); static int SetupAgentInstance(); static int SetupTcpSocket(); static int SetupUnixSocket(); static int SetupServiceSockets(); static int SetupDisplaySocket(int &xServerAddrFamily, sockaddr *&xServerAddr, unsigned int &xServerAddrLength); // // Setup a listening socket and accept // a new connection. // static int ListenConnection(int port, const char *label); static int AcceptConnection(int fd, int domain, const char *label); // // Other convenience functions. // static int WaitForRemote(int portNum); static int ConnectToRemote(const char *const hostName, int portNum); static int SendProxyOptions(int fd); static int SendProxyCaches(int fd); static int ReadProxyVersion(int fd); static int ReadProxyOptions(int fd); static int ReadProxyCaches(int fd); static int ReadForwarderVersion(int fd); static int ReadForwarderOptions(int fd); static int ReadRemoteData(int fd, char *buffer, int size, char stop); static int WriteLocalData(int fd, const char *buffer, int size); static void PrintVersionInfo(); static void PrintProcessInfo(); static void PrintConnectionInfo(); static void PrintUsageInfo(const char *option, const int error); static void PrintOptionIgnored(const char *type, const char *name, const char *value); // // This is not static to avoid a warning. // void PrintCopyrightInfo(); static const char *GetOptions(const char *options); static const char *GetArg(int &argi, int argc, const char **argv); static int CheckArg(const char *type, const char *name, const char *value); static int ParseArg(const char *type, const char *name, const char *value); static int ValidateArg(const char *type, const char *name, const char *value); static int LowercaseArg(const char *type, const char *name, char *value); static int CheckSignal(int signal); extern "C" { int ParseCommandLineOptions(int argc, const char **argv); int ParseEnvironmentOptions(const char *env, int force); int ParseBindOptions(char **host, int *port); } static int ParseFileOptions(const char *file); static int ParseRemoteOptions(char *opts); static int ParseForwarderOptions(char *opts); // // These functions are used to parse literal // values provided by the user and set the // control parameters accordingly. // static int ParseLinkOption(const char *opt); static int ParseBitrateOption(const char *opt); static int ParseCacheOption(const char *opt); static int ParseShmemOption(const char *opt); static int ParseImagesOption(const char *opt); static int ParsePackOption(const char *opt); // // Set host and port where NX proxy is supposed // to be listening in case such parameters are // given on the command line. // static int ParseHostOption(const char *opt, char *host, int &port); // // Translate a font server port specification // to the corresponding Unix socket path. // static int ParseFontPath(char *path); // // Determine the interface where to listen for // the remote proxy connection or the local // forwarder. // static int ParseListenOption(int &interface); // // Translate a pack method id in a literal. // static int ParsePackMethod(const int method, const int quality); // // Try to increase the size of the allowed // core dumps. // static int SetCore(); // // Set the proxy mode to either client or // server. // static int SetMode(int mode); // // Determine the path of the NX_* directories // from the environment. // static int SetDirectories(); // // Set the defaults used for the log file and // statistics. // static int SetLogs(); // // Check if local and remote protocol versions // are compatible and, eventually, downgrade // local version to the minimum level that is // known to work. // static int SetVersion(); // // Setup the listening TCP ports used for the // additional channels according to user's // wishes. // static int SetPorts(); // // Set the maximum number of open descriptors. // static int SetDescriptors(); // // Set the path used for choosing the cache. // It must be selected after determining the // session type. // static int SetCaches(); // // Initialize, one after the other, all the // configuration parameters. // static int SetParameters(); // // Set the specific configuration parameter. // static int SetSession(); static int SetStorage(); static int SetShmem(); static int SetPack(); static int SetImages(); static int SetLimits(); // // Set up the control parameters based on // the link speed negotiated between the // proxies. // static int SetLink(); static int SetLinkModem(); static int SetLinkIsdn(); static int SetLinkAdsl(); static int SetLinkWan(); static int SetLinkLan(); // // Adjust the compression parameters. // static int SetCompression(); static int SetCompressionModem(); static int SetCompressionIsdn(); static int SetCompressionAdsl(); static int SetCompressionWan(); static int SetCompressionLan(); // // Determine the NX paths based on the // user's parameters or the environment. // char *GetClientPath(); static char *GetSystemPath(); static char *GetHomePath(); static char *GetTempPath(); static char *GetRootPath(); static char *GetCachePath(); static char *GetImagesPath(); static char *GetSessionPath(); static char *GetLastCache(char *list, const char *path); static int OpenLogFile(char *name, ostream *&stream); static int ReopenLogFile(char *name, ostream *&stream, int limit); // // Perform operations on the managed // descriptors in the main loop. // static void handleCheckSessionInLoop(); static void handleCheckBitrateInLoop(); #if defined(TEST) || defined(INFO) static void handleCheckSelectInLoop(int &setFDs, fd_set &readSet, fd_set &writeSet, T_timestamp selectTs); static void handleCheckResultInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, fd_set &writeSet, struct timeval &selectTs, struct timeval &startTs); static void handleCheckStateInLoop(int &setFDs); #endif static void handleCheckSessionInConnect(); static inline void handleSetReadInLoop(fd_set &readSet, int &setFDs, struct timeval &selectTs); static inline void handleSetWriteInLoop(fd_set &writeSet, int &setFDs, struct timeval &selectTs); static inline void handleSetListenersInLoop(fd_set &writeSet, int &setFDs); static inline void handleSetAgentInLoop(int &setFDs, fd_set &readSet, fd_set &writeSet, struct timeval &selectTs); static void handleAlertInLoop(); static void handleStatisticsInLoop(); static inline void handleAgentInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, fd_set &writeSet, struct timeval &selectTs); static inline void handleAgentLateInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, fd_set &writeSet, struct timeval &selectTs); static inline void handleReadableInLoop(int &resultFDs, fd_set &readSet); static inline void handleWritableInLoop(int &resultFDs, fd_set &writeSet); static inline void handleRotateInLoop(); static inline void handleEventsInLoop(); static inline void handleFlushInLoop(); // // Manage the proxy link during the negotiation // phase. // static void handleNegotiationInLoop(int &setFDs, fd_set &readSet, fd_set &writeSet, T_timestamp &selectTs); // // Print the 'terminating' messages in the // session log. // static inline void handleTerminatingInLoop(); static inline void handleTerminatedInLoop(); // // Monitor the size of the log file. // static void handleLogReopenInLoop(T_timestamp &logsTs, T_timestamp &nowTs); // // Directory where the NX binaries and libraries reside. // static char systemDir[DEFAULT_STRING_LENGTH] = { 0 }; // // Directory used for temporary files. // static char tempDir[DEFAULT_STRING_LENGTH] = { 0 }; // // Actually the full path to the client. // static char clientDir[DEFAULT_STRING_LENGTH] = { 0 }; // // User's home directory. // static char homeDir[DEFAULT_STRING_LENGTH] = { 0 }; // // Root of directory structure to be created by proxy. // static char rootDir[DEFAULT_STRING_LENGTH] = { 0 }; // // Root of statistics and log files to be created by proxy. // static char sessionDir[DEFAULT_STRING_LENGTH] = { 0 }; // // Log files for errors and statistics. Error log is // the place where we print also debug informations. // Both files are closed, deleted and reopened as // their size exceed the limit set in control class. // The session log is not reopened, as it is used by // the NX client and server to track the advance of // the session. // static char errorsFileName[DEFAULT_STRING_LENGTH] = { 0 }; static char statsFileName[DEFAULT_STRING_LENGTH] = { 0 }; static char sessionFileName[DEFAULT_STRING_LENGTH] = { 0 }; static char optionsFileName[DEFAULT_STRING_LENGTH] = { 0 }; // // String literal representing selected link speed // parameter. Value is translated in control values // used by proxies to stay synchronized. // static char linkSpeedName[DEFAULT_STRING_LENGTH] = { 0 }; // // String literal representing selected // cache size. // static char cacheSizeName[DEFAULT_STRING_LENGTH] = { 0 }; // // String literal representing selected // shared memory segment size. // static char shsegSizeName[DEFAULT_STRING_LENGTH] = { 0 }; // // String literal of images cache size. // static char imagesSizeName[DEFAULT_STRING_LENGTH] = { 0 }; // // String literal for bandwidth limit. // static char bitrateLimitName[DEFAULT_STRING_LENGTH] = { 0 }; // // String literal for image packing method. // static char packMethodName[DEFAULT_STRING_LENGTH] = { 0 }; // // Product name provided by the server or client. // static char productName[DEFAULT_STRING_LENGTH] = { 0 }; // // Its corresponding value from NXpack.h. // static int packMethod = -1; static int packQuality = -1; // // String literal for session type. Persistent caches // are searched in directory whose name matches this // parameter. // static char sessionType[DEFAULT_STRING_LENGTH] = { 0 }; // // Unique id assigned to session. It is used as // name of directory where all files are placed. // static char sessionId[DEFAULT_STRING_LENGTH] = { 0 }; // // Set if we already parsed the options. // static int parsedOptions = 0; static int parsedCommand = 0; // // Buffer data received from the remote proxy at // session negotiation. // static char remoteData[MAXIMUM_REMOTE_OPTIONS_LENGTH] = { 0 }; static int remotePosition = 0; // // Main loop file descriptors. // static int tcpFD = -1; static int unixFD = -1; static int cupsFD = -1; static int auxFD = -1; static int smbFD = -1; static int mediaFD = -1; static int httpFD = -1; static int fontFD = -1; static int slaveFD = -1; static int proxyFD = -1; // // Used for internal communication // with the X agent. // static int agentFD[2] = { -1, -1 }; // // Flags determining which protocols and // ports are forwarded. // int useUnixSocket = 1; static int useTcpSocket = 1; static int useCupsSocket = 0; static int useAuxSocket = 0; static int useSmbSocket = 0; static int useMediaSocket = 0; static int useHttpSocket = 0; static int useFontSocket = 0; static int useSlaveSocket = 0; static int useAgentSocket = 0; // // Set if the launchd service is running // and its socket must be used as X socket. // static int useLaunchdSocket = 0; // // Set by user if he/she wants to modify // the default TCP_NODELAY option as set // in control. // static int useNoDelay = -1; // // Set if user wants to override default // flush timeout set according to link. // static int usePolicy = -1; // // Set if user wants to hide the RENDER // extension or wants to short-circuit // some simple replies at client side. // static int useRender = -1; static int useTaint = -1; // // Set if the user wants to reduce the // nominal size of the token messages // exchanged between the proxies. // static int useStrict = -1; // // Set if the proxy is running as part // of SSH on the client. // static int useEncryption = -1; // // Name of Unix socket created by the client proxy to // accept client connections. File must be unlinked // by cleanup function. // static char unixSocketName[DEFAULT_STRING_LENGTH] = { 0 }; // // Other parameters. // static char connectHost[DEFAULT_STRING_LENGTH] = { 0 }; static char acceptHost[DEFAULT_STRING_LENGTH] = { 0 }; static char listenHost[DEFAULT_STRING_LENGTH] = { 0 }; static char displayHost[DEFAULT_STRING_LENGTH] = { 0 }; static char authCookie[DEFAULT_STRING_LENGTH] = { 0 }; static int proxyPort = DEFAULT_NX_PROXY_PORT; static int xPort = DEFAULT_NX_X_PORT; // // Used to setup the connection the real // X display socket. // static int xServerAddrFamily = -1; static sockaddr *xServerAddr = NULL; static unsigned int xServerAddrLength = 0; // // The port where the local proxy will await // the peer connection or where the remote // proxy will be contacted. // static int listenPort = -1; static int connectPort = -1; // // Helper channels are disabled by default. // static int cupsPort = -1; static int auxPort = -1; static int smbPort = -1; static int mediaPort = -1; static int httpPort = -1; static int slavePort = -1; // // Can be either a port number or a Unix // socket. // static char fontPort[DEFAULT_STRING_LENGTH] = { 0 }; // // Host and port where the existing proxy // is running. // static char bindHost[DEFAULT_STRING_LENGTH] = { 0 }; static int bindPort = -1; // // Pointers to the callback functions and // parameter set by the agent // static void (*flushCallback)(void *, int) = NULL; static void *flushParameter = NULL; static void (*statisticsCallback)(void *, int) = NULL; static void *statisticsParameter = NULL; // // State variables shared between the init // function and the main loop. // T_timestamp initTs; T_timestamp startTs; T_timestamp logsTs; T_timestamp nowTs; int diffTs; // // This is set to the main proxy process id. // int lastProxy = 0; // // Set to last dialog process launched by proxy. // int lastDialog = 0; // // Set to watchdog process launched by proxy. // int lastWatchdog = 0; // // Set if a cache house-keeper process is running. // int lastKeeper = 0; // // Let an inner routine register the pid of a slave // process. // static int lastChild = 0; // // Exit code of the last child process exited. // static int lastStatus = 0; // // Set if shutdown was requested through a signal. // static int lastKill = 0; // // Set if the agent confirmed the destruction of // the NX transport. // static int lastDestroy = 0; // // This is set to the code and local flag of the // last requested alert. // static struct { int code; int local; } lastAlert; // // Manage the current signal masks. // static struct { sigset_t saved; int blocked; int installed; int enabled[32]; int forward[32]; struct sigaction action[32]; } lastMasks; // // Manage the current timer. // static struct { struct sigaction action; struct itimerval value; struct timeval start; struct timeval next; } lastTimer; // // This is set to last signal received in handler. // static int lastSignal = 0; // // Set to the last time bytes readable were queried // by the agent. // static T_timestamp lastReadableTs = nullTimestamp(); // // Here are interfaces declared in NX.h. // int NXTransProxy(int fd, int mode, const char* options) { // // Let the log temporarily go to the standard // error. Be also sure we have a jump context, // in the case any subsequent operation will // cause a cleanup. // if (logofs == NULL) { logofs = &cerr; } if (setjmp(context) == 1) { #ifdef TEST *logofs << "NXTransProxy: Out of the long jump with pid '" << lastProxy << "'.\n" << logofs_flush; #endif return -1; } // // Check if have already performed a parsing of // parameters, as in the case we are running as // a stand-alone process. If needed create the // parameters repository // if (control == NULL) { control = new Control(); } lastProxy = getpid(); #ifdef TEST *logofs << "NXTransProxy: Main process started with pid '" << lastProxy << "'.\n" << logofs_flush; #endif SetMode(mode); if (mode == NX_MODE_CLIENT) { if (fd != NX_FD_ANY) { #ifdef TEST *logofs << "NXTransProxy: Agent descriptor for X client connections is FD#" << fd << ".\n" << logofs_flush; *logofs << "NXTransProxy: Disabling listening on further X client connections.\n" << logofs_flush; #endif useTcpSocket = 0; useUnixSocket = 0; useAgentSocket = 1; agentFD[1] = fd; } } else if (mode == NX_MODE_SERVER) { if (fd != NX_FD_ANY) { #ifdef TEST *logofs << "NXTransProxy: PANIC! Agent descriptor for X server connections " << "not supported yet.\n" << logofs_flush; #endif cerr << "Error" << ": Agent descriptor for X server connections " << "not supported yet.\n"; return -1; } } const char *env = GetOptions(options); if (ParseEnvironmentOptions(env, 0) < 0) { cerr << "Error" << ": Parsing of NX transport options failed.\n"; return -1; } // // Set the path of the NX directories. // SetDirectories(); // // Open the log files. // SetLogs(); #ifdef TEST *logofs << "NXTransProxy: Going to run the NX transport loop.\n" << logofs_flush; #endif WaitCleanup(); // // This function should never return. // exit(0); } void NXTransExit(int code) { if (logofs == NULL) { logofs = &cerr; } static int recurse; if (++recurse > 1) { #ifdef TEST *logofs << "NXTransExit: Aborting process with pid '" << getpid() << "' due to recursion through " << "exit.\n" << logofs_flush; #endif abort(); } #ifdef TEST *logofs << "NXTransExit: Process with pid '" << getpid() << "' called exit with code '" << code << "'.\n" << logofs_flush; #endif if (control != NULL) { // // Be sure that there we can detect the // termination of the watchdog. // EnableSignals(); // // Close the NX transport if it was not // shut down already. // NXTransDestroy(NX_FD_ANY); } exit(code); } int NXTransParseCommandLine(int argc, const char **argv) { return ParseCommandLineOptions(argc, argv); } int NXTransParseEnvironment(const char *env, int force) { return ParseEnvironmentOptions(env, force); } void NXTransCleanup() { HandleCleanup(); } // // Check the parameters for subsequent // initialization of the NX transport. // int NXTransCreate(int fd, int mode, const char* options) { if (logofs == NULL) { logofs = &cerr; } // // Be sure we have a jump context, in the // case a subsequent operation will cause // a cleanup. // if (setjmp(context) == 1) { return -1; } // // Create the parameters repository // if (control != NULL) { #ifdef PANIC *logofs << "NXTransCreate: PANIC! The NX transport seems " << "to be already running.\n" << logofs_flush; #endif cerr << "Error" << ": The NX transport seems " << "to be already running.\n"; return -1; } control = new Control(); if (control == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Error creating the NX transport.\n" << logofs_flush; #endif cerr << "Error" << ": Error creating the NX transport.\n"; return -1; } lastProxy = getpid(); #ifdef TEST *logofs << "NXTransCreate: Caller process running with pid '" << lastProxy << "'.\n" << logofs_flush; #endif // // Set the local proxy mode an parse the // display NX options. // SetMode(mode); const char *env = GetOptions(options); if (ParseEnvironmentOptions(env, 0) < 0) { cerr << "Error" << ": Parsing of NX transport options failed.\n"; return -1; } // // Set the path of the NX directories. // SetDirectories(); // // Open the log files. // SetLogs(); // // Use the provided descriptor. // proxyFD = fd; #ifdef TEST *logofs << "NXTransCreate: Called with NX proxy descriptor '" << proxyFD << "'.\n" << logofs_flush; #endif #ifdef TEST *logofs << "NXTransCreate: Creation of the NX transport completed.\n" << logofs_flush; #endif return 1; } // // Tell the proxy to use the descriptor as the internal // connection to the X client side NX agent. This will // have the side effect of disabling listening for add- // itional X client connections. // int NXTransAgent(int fd[2]) { // // Be sure we have a jump context, in the // case a subsequent operation will cause // a cleanup. // if (logofs == NULL) { logofs = &cerr; } if (setjmp(context) == 1) { return -1; } if (control == NULL) { cerr << "Error" << ": Can't set the NX agent without a NX transport.\n"; return -1; } else if (control -> ProxyMode != proxy_client) { #ifdef PANIC *logofs << "NXTransAgent: Invalid mode while setting the NX agent.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid mode while setting the NX agent.\n\n"; return -1; } useTcpSocket = 0; useUnixSocket = 0; useAgentSocket = 1; agentFD[0] = fd[0]; agentFD[1] = fd[1]; #ifdef TEST *logofs << "NXTransAgent: Internal descriptors for agent are FD#" << agentFD[0] << " and FD#" << agentFD[1] << ".\n" << logofs_flush; *logofs << "NXTransAgent: Disabling listening for further X client " << "connections.\n" << logofs_flush; #endif agent = new Agent(agentFD); if (agent == NULL || agent -> isValid() != 1) { #ifdef PANIC *logofs << "Loop: PANIC! Error creating the NX memory transport .\n" << logofs_flush; #endif cerr << "Error" << ": Error creating the NX memory transport.\n"; HandleCleanup(); } #ifdef TEST *logofs << "NXTransAgent: Enabling memory-to-memory transport.\n" << logofs_flush; #endif return 1; } int NXTransClose(int fd) { if (logofs == NULL) { logofs = &cerr; } /* * Only handle the proxy connection. The X * transport will take care of closing its * end of the socket pair. */ if (control != NULL && ((agent != NULL && (fd == agentFD[0] || fd == NX_FD_ANY)) || (fd == proxyFD || fd == NX_FD_ANY))) { if (proxy != NULL) { #ifdef TEST *logofs << "NXTransClose: Closing down all the X connections.\n" << logofs_flush; #endif CleanupConnections(); } } #ifdef TEST else { *logofs << "NXTransClose: The NX transport is not running.\n" << logofs_flush; } #endif return 1; } // // Close down the transport and free the // allocated NX resources. // int NXTransDestroy(int fd) { if (logofs == NULL) { logofs = &cerr; } if (control != NULL && ((agent != NULL && (fd == agentFD[0] || fd == NX_FD_ANY)) || (fd == proxyFD || fd == NX_FD_ANY))) { // // Shut down the X connections and // wait the cleanup to complete. // if (proxy != NULL) { #ifdef TEST *logofs << "NXTransDestroy: Closing down all the X connections.\n" << logofs_flush; #endif CleanupConnections(); } #ifdef TEST *logofs << "NXTransDestroy: Waiting for the NX transport to terminate.\n" << logofs_flush; #endif lastDestroy = 1; WaitCleanup(); } #ifdef TEST else { *logofs << "NXTransDestroy: The NX transport is not running.\n" << logofs_flush; } #endif return 1; } // // Assume that the NX transport is valid // as long as the control class has not // been destroyed. // int NXTransRunning(int fd) { return (control != NULL); } int NXTransContinue(struct timeval *selectTs) { if (control != NULL) { // // If no timeout is provided use // the default. // T_timestamp newTs; if (selectTs == NULL) { setTimestamp(newTs, control -> PingTimeout); selectTs = &newTs; } // // Use empty masks and only get the // descriptors set by the proxy. // fd_set readSet; fd_set writeSet; int setFDs; int errorFDs; int resultFDs; setFDs = 0; FD_ZERO(&readSet); FD_ZERO(&writeSet); // // Run a new loop. If the transport // is gone avoid sleeping until the // timeout. // if (NXTransPrepare(&setFDs, &readSet, &writeSet, selectTs) != 0) { NXTransSelect(&resultFDs, &errorFDs, &setFDs, &readSet, &writeSet, selectTs); NXTransExecute(&resultFDs, &errorFDs, &setFDs, &readSet, &writeSet, selectTs); } } return (control != NULL); } int NXTransSignal(int signal, int action) { if (logofs == NULL) { logofs = &cerr; } if (control == NULL) { return 0; } if (action == NX_SIGNAL_RAISE) { #ifdef TEST *logofs << "NXTransSignal: Raising signal '" << DumpSignal(signal) << "' in the proxy handler.\n" << logofs_flush; #endif HandleSignal(signal); return 1; } else if (signal == NX_SIGNAL_ANY) { #ifdef TEST *logofs << "NXTransSignal: Setting action of all signals to '" << action << "'.\n" << logofs_flush; #endif for (int i = 0; i < 32; i++) { if (CheckSignal(i) == 1) { NXTransSignal(i, action); } } return 1; } else if (CheckSignal(signal) == 1) { #ifdef TEST *logofs << "NXTransSignal: Setting action of signal '" << DumpSignal(signal) << "' to '" << action << "'.\n" << logofs_flush; #endif if (action == NX_SIGNAL_ENABLE || action == NX_SIGNAL_FORWARD) { InstallSignal(signal, action); return 1; } else if (action == NX_SIGNAL_DISABLE) { RestoreSignal(signal); return 1; } } #ifdef WARNING *logofs << "NXTransSignal: WARNING! Unable to perform action '" << action << "' on signal '" << DumpSignal(signal) << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Unable to perform action '" << action << "' on signal '" << DumpSignal(signal) << "'.\n"; return -1; } int NXTransCongestion(int fd) { if (control != NULL && proxy != NULL) { #ifdef DUMP int congestion = proxy -> getCongestion(proxyFD); *logofs << "NXTransCongestion: Returning " << congestion << " as current congestion level.\n" << logofs_flush; return congestion; #endif return (proxy -> getCongestion(proxyFD)); } return 0; } int NXTransHandler(int fd, int type, void (*handler)(void *parameter, int reason), void *parameter) { if (logofs == NULL) { logofs = &cerr; } switch (type) { case NX_HANDLER_FLUSH: { flushCallback = handler; flushParameter = parameter; break; } case NX_HANDLER_STATISTICS: { // // Reporting of statistics by the agent // still needs to be implemented. // statisticsCallback = handler; statisticsParameter = parameter; break; } default: { #ifdef TEST *logofs << "NXTransHandler: WARNING! Failed to set " << "the NX callback for event '" << type << "' to '" << (void *) handler << "' and parameter '" << parameter << "'.\n" << logofs_flush; #endif return 0; } } #ifdef TEST *logofs << "NXTransHandler: Set the NX " << "callback for event '" << type << "' to '" << (void *) handler << "' and parameter '" << parameter << "'.\n" << logofs_flush; #endif return 1; } int NXTransRead(int fd, char *data, int size) { if (logofs == NULL) { logofs = &cerr; } if (control != NULL && agent != NULL && fd == agentFD[0]) { #ifdef DUMP *logofs << "NXTransRead: Dequeuing " << size << " bytes " << "from FD#" << agentFD[0] << ".\n" << logofs_flush; #endif int result = agent -> dequeueData(data, size); #ifdef DUMP if (result < 0 && EGET() == EAGAIN) { *logofs << "NXTransRead: WARNING! Dequeuing from FD#" << agentFD[0] << " would block.\n" << logofs_flush; } else { *logofs << "NXTransRead: Dequeued " << result << " bytes " << "to FD#" << agentFD[0] << ".\n" << logofs_flush; } #endif return result; } else { #ifdef DUMP *logofs << "NXTransRead: Reading " << size << " bytes " << "from FD#" << fd << ".\n" << logofs_flush; #endif return read(fd, data, size); } } int NXTransReadVector(int fd, struct iovec *iovdata, int iovsize) { if (logofs == NULL) { logofs = &cerr; } if (control != NULL && agent != NULL && fd == agentFD[0]) { #if defined(DUMP) if (control -> ProxyStage >= stage_operational && agent -> localReadable() > 0) { *logofs << "NXTransReadVector: WARNING! Agent has data readable.\n" << logofs_flush; } #endif char *base; int length; int result; struct iovec *vector = iovdata; int count = iovsize; ESET(0); int i = 0; int total = 0; for (; i < count; i++, vector++) { length = vector -> iov_len; base = (char *) vector -> iov_base; while (length > 0) { #ifdef DUMP *logofs << "NXTransReadVector: Dequeuing " << length << " bytes " << "from FD#" << agentFD[0] << ".\n" << logofs_flush; #endif result = agent -> dequeueData(base, length); #ifdef DUMP if (result < 0 && EGET() == EAGAIN) { *logofs << "NXTransReadVector: WARNING! Dequeuing from FD#" << agentFD[0] << " would block.\n" << logofs_flush; } else { *logofs << "NXTransReadVector: Dequeued " << result << " bytes " << "from FD#" << agentFD[0] << ".\n" << logofs_flush; } #endif if (result < 0 && total == 0) { return result; } else if (result <= 0) { return total; } ESET(0); length -= result; total += result; base += result; } } return total; } else { #ifdef DUMP *logofs << "NXTransReadVector: Reading vector with " << iovsize << " elements from FD#" << fd << ".\n" << logofs_flush; #endif return readv(fd, iovdata, iovsize); } } int NXTransReadable(int fd, int *readable) { if (logofs == NULL) { logofs = &cerr; } if (control == NULL || agent == NULL || fd != agentFD[0]) { #ifdef DUMP int result = GetBytesReadable(fd, readable); if (result == -1) { *logofs << "NXTransReadable: Error detected on FD#" << fd << ".\n" << logofs_flush; } else { *logofs << "NXTransReadable: Returning " << *readable << " bytes as readable from FD#" << fd << ".\n" << logofs_flush; } return result; #else return GetBytesReadable(fd, readable); #endif } int result = agent -> dequeuableData(); switch (result) { case 0: { // // The client might have enqueued data to our side // and is now checking for the available events. As // _XEventsQueued() may omit to call _XSelect(), we // handle here the new data that is coming from the // proxy to avoid spinning through this function // again. // if (proxy != NULL && proxy -> canRead() == 1) { #if defined(TEST) || defined(INFO) *logofs << "NXTransReadable: WARNING! Trying to " << "read to generate new agent data.\n" << logofs_flush; #endif // // Set the context as the function // can cause a cleanup. // if (setjmp(context) == 1) { return -1; } if (proxy -> handleRead() < 0) { #if defined(TEST) || defined(INFO) *logofs << "NXTransReadable: Failure reading " << "messages from proxy FD#" << proxyFD << ".\n" << logofs_flush; #endif HandleShutdown(); } // // Call again the routine. By reading // new control messages from the proxy // the agent channel may be gone. // return NXTransReadable(fd, readable); } #ifdef DUMP *logofs << "NXTransReadable: Returning " << 0 << " bytes as readable from FD#" << fd << " with result 0.\n" << logofs_flush; #endif *readable = 0; return 0; } case -1: { #ifdef DUMP *logofs << "NXTransReadable: Returning " << 0 << " bytes as readable from FD#" << fd << " with result -1.\n" << logofs_flush; #endif *readable = 0; return -1; } default: { #ifdef DUMP *logofs << "NXTransReadable: Returning " << result << " bytes as readable from FD#" << fd << " with result 0.\n" << logofs_flush; #endif *readable = result; return 0; } } } int NXTransWrite(int fd, char *data, int size) { // // Be sure we have a valid log file. // if (logofs == NULL) { logofs = &cerr; } if (control != NULL && agent != NULL && fd == agentFD[0]) { int result; if (proxy != NULL) { if (proxy -> canRead(agentFD[1]) == 0) { #if defined(DUMP) || defined(TEST) *logofs << "NXTransWrite: WARNING! Delayed enqueuing to FD#" << agentFD[0] << " with proxy unable to read.\n" << logofs_flush; #endif ESET(EAGAIN); return -1; } // // Set the context as the function // can cause a cleanup. // if (setjmp(context) == 1) { return -1; } // // Don't enqueue the data to the transport // but let the channel borrow the buffer. // #ifdef DUMP *logofs << "NXTransWrite: Letting the channel borrow " << size << " bytes from FD#" << agentFD[0] << ".\n" << logofs_flush; #endif result = proxy -> handleRead(agentFD[1], data, size); if (result == 1) { result = size; } else { if (result == 0) { ESET(EAGAIN); } else { ESET(EPIPE); } result = -1; } } else { // // We don't have a proxy connection, yet. // Enqueue the data to the agent transport. // #ifdef DUMP *logofs << "NXTransWrite: Enqueuing " << size << " bytes " << "to FD#" << agentFD[0] << ".\n" << logofs_flush; #endif result = agent -> enqueueData(data, size); } #ifdef DUMP if (result < 0) { if (EGET() == EAGAIN) { *logofs << "NXTransWrite: WARNING! Enqueuing to FD#" << agentFD[0] << " would block.\n" << logofs_flush; } else { *logofs << "NXTransWrite: WARNING! Error enqueuing to FD#" << agentFD[0] << ".\n" << logofs_flush; } } else { *logofs << "NXTransWrite: Enqueued " << result << " bytes " << "to FD#" << agentFD[0] << ".\n" << logofs_flush; } #endif return result; } else { #ifdef DUMP *logofs << "NXTransWrite: Writing " << size << " bytes " << "to FD#" << fd << ".\n" << logofs_flush; #endif return write(fd, data, size); } } int NXTransWriteVector(int fd, struct iovec *iovdata, int iovsize) { // // Be sure we have a valid log file and a // jump context because we will later call // functions that can perform a cleanup. // if (logofs == NULL) { logofs = &cerr; } int result = 0; if (control != NULL && agent != NULL && fd == agentFD[0]) { // // See the comment in NXTransWrite(). // if (proxy != NULL) { if (proxy -> canRead(agentFD[1]) == 0) { #if defined(DUMP) || defined(TEST) *logofs << "NXTransWriteVector: WARNING! Delayed enqueuing to FD#" << agentFD[0] << " with proxy unable to read.\n" << logofs_flush; #endif ESET(EAGAIN); return -1; } } // // Set the context as the function // can cause a cleanup. // if (setjmp(context) == 1) { return -1; } char *base; int length; struct iovec *vector = iovdata; int count = iovsize; ESET(0); int i = 0; int total = 0; for (; i < count; i++, vector++) { length = vector -> iov_len; base = (char *) vector -> iov_base; while (length > 0) { if (proxy != NULL) { // // Don't enqueue the data to the transport // but let the channel borrow the buffer. // #ifdef DUMP *logofs << "NXTransWriteVector: Letting the channel borrow " << length << " bytes from FD#" << agentFD[0] << ".\n" << logofs_flush; #endif result = proxy -> handleRead(agentFD[1], base, length); if (result == 1) { result = length; } else { if (result == 0) { ESET(EAGAIN); } else { ESET(EPIPE); } result = -1; } } else { // // We don't have a proxy connection, yet. // Enqueue the data to the agent transport. // #ifdef DUMP *logofs << "NXTransWriteVector: Enqueuing " << length << " bytes " << "to FD#" << agentFD[0] << ".\n" << logofs_flush; #endif result = agent -> enqueueData(base, length); } #ifdef DUMP if (result < 0) { if (EGET() == EAGAIN) { *logofs << "NXTransWriteVector: WARNING! Enqueuing to FD#" << agentFD[0] << " would block.\n" << logofs_flush; } else { *logofs << "NXTransWriteVector: WARNING! Error enqueuing to FD#" << agentFD[0] << ".\n" << logofs_flush; } } else { *logofs << "NXTransWriteVector: Enqueued " << result << " bytes " << "to FD#" << agentFD[0] << ".\n" << logofs_flush; } #endif if (result < 0 && total == 0) { return result; } else if (result <= 0) { return total; } ESET(0); length -= result; total += result; base += result; } } return total; } else { #ifdef DUMP *logofs << "NXTransWriteVector: Writing vector with " << iovsize << " elements to FD#" << fd << ".\n" << logofs_flush; #endif return writev(fd, iovdata, iovsize); } } int NXTransPolicy(int fd, int type) { if (control != NULL) { if (usePolicy == -1) { #if defined(TEST) || defined(INFO) *logofs << "NXTransPolicy: Setting flush policy on " << "proxy FD#" << proxyFD << " to '" << DumpPolicy(type == NX_POLICY_DEFERRED ? policy_deferred : policy_immediate) << "'.\n" << logofs_flush; #endif control -> FlushPolicy = (type == NX_POLICY_DEFERRED ? policy_deferred : policy_immediate); if (proxy != NULL) { proxy -> handleFlush(); } return 1; } else { #if defined(TEST) || defined(INFO) *logofs << "NXTransPolicy: Ignoring the agent " << "setting with user policy set to '" << DumpPolicy(control -> FlushPolicy) << "'.\n" << logofs_flush; #endif return 0; } } return 0; } int NXTransFlushable(int fd) { if (proxy == NULL || agent == NULL || fd != agentFD[0]) { #ifdef DUMP *logofs << "NXTransFlushable: Returning 0 bytes as " << "flushable for unrecognized FD#" << fd << ".\n" << logofs_flush; #endif return 0; } else { #if defined(DUMP) || defined(INFO) *logofs << "NXTransFlushable: Returning " << proxy -> getFlushable(proxyFD) << " as bytes flushable on " << "proxy FD#" << proxyFD << ".\n" << logofs_flush; #endif return proxy -> getFlushable(proxyFD); } } int NXTransFlush(int fd) { if (proxy != NULL) { #if defined(TEST) || defined(INFO) *logofs << "NXTransFlush: Requesting an immediate flush of " << "proxy FD#" << proxyFD << ".\n" << logofs_flush; #endif return proxy -> handleFlush(); } return 0; } int NXTransChannel(int fd, int channelFd, int type) { if (proxy != NULL) { // // Set the context as the function // can cause a cleanup. // if (setjmp(context) == 1) { return -1; } #if defined(TEST) || defined(INFO) *logofs << "NXTransChannel: Going to create a new channel " << "with type '" << type << "' on FD#" << channelFd << ".\n" << logofs_flush; #endif int result = -1; switch (type) { case NX_CHANNEL_X11: { if (useUnixSocket == 1 || useTcpSocket == 1 || useAgentSocket == 1 || useAuxSocket == 1) { result = proxy -> handleNewConnection(channel_x11, channelFd); } break; } case NX_CHANNEL_CUPS: { if (useCupsSocket == 1) { result = proxy -> handleNewConnection(channel_cups, channelFd); } break; } case NX_CHANNEL_SMB: { if (useSmbSocket == 1) { result = proxy -> handleNewConnection(channel_smb, channelFd); } break; } case NX_CHANNEL_MEDIA: { if (useMediaSocket == 1) { result = proxy -> handleNewConnection(channel_media, channelFd); } break; } case NX_CHANNEL_HTTP: { if (useHttpSocket == 1) { result = proxy -> handleNewConnection(channel_http, channelFd); } break; } case NX_CHANNEL_FONT: { if (useFontSocket == 1) { result = proxy -> handleNewConnection(channel_font, channelFd); } break; } case NX_CHANNEL_SLAVE: { if (useSlaveSocket == 1) { result = proxy -> handleNewConnection(channel_slave, channelFd); } break; } default: { #ifdef WARNING *logofs << "NXTransChannel: WARNING! Unrecognized channel " << "type '" << type << "'.\n" << logofs_flush; #endif break; } } #ifdef WARNING if (result != 1) { *logofs << "NXTransChannel: WARNING! Could not create the " << "new channel with type '" << type << "' on FD#" << channelFd << ".\n" << logofs_flush; } #endif return result; } return 0; } const char *NXTransFile(int type) { char *name = NULL; switch (type) { case NX_FILE_SESSION: { name = sessionFileName; break; } case NX_FILE_ERRORS: { name = errorsFileName; break; } case NX_FILE_OPTIONS: { name = optionsFileName; break; } case NX_FILE_STATS: { name = statsFileName; break; } } if (name != NULL && *name != '\0') { return name; } return NULL; } long NXTransTime() { static T_timestamp last = getTimestamp(); T_timestamp now = getTimestamp(); long diff = diffTimestamp(last, now); last = now; return diff; } int NXTransAlert(int code, int local) { if (proxy != NULL) { #if defined(DUMP) || defined(INFO) *logofs << "NXTransAlert: Requesting a NX dialog with code " << code << " and local " << local << ".\n" << logofs_flush; #endif if (local == 0) { // // Set the context as the function // can cause a cleanup. // if (setjmp(context) == 1) { return -1; } proxy -> handleAlert(code); } else { // // Show the alert at the next loop. // HandleAlert(code, local); } return 1; } #if defined(DUMP) || defined(INFO) else { if (logofs == NULL) { logofs = &cerr; } *logofs << "NXTransAlert: Can't request an alert without " << "a valid NX transport.\n" << logofs_flush; } #endif return 0; } // // Prepare the file sets and the timeout // for a later execution of the select(). // int NXTransPrepare(int *setFDs, fd_set *readSet, fd_set *writeSet, struct timeval *selectTs) { if (logofs == NULL) { logofs = &cerr; } // // Control is NULL if the NX transport was // reset or was never created. If control // is valid then prepare to jump back when // the transport is destroyed. // if (control == NULL || setjmp(context) == 1) { return 0; } #if defined(TEST) || defined(INFO) *logofs << "\nNXTransPrepare: Going to prepare the NX transport.\n" << logofs_flush; #endif if (control -> ProxyStage < stage_operational) { handleNegotiationInLoop(*setFDs, *readSet, *writeSet, *selectTs); } else { #if defined(TEST) || defined(INFO) if (isTimestamp(*selectTs) == 0) { *logofs << "Loop: WARNING! Preparing the select with requested " << "timeout of " << selectTs -> tv_sec << " S and " << (double) selectTs -> tv_usec / 1000 << " Ms.\n" << logofs_flush; } else { *logofs << "Loop: Preparing the select with requested " << "timeout of " << selectTs -> tv_sec << " S and " << (double) selectTs -> tv_usec / 1000 << " Ms.\n" << logofs_flush; } #endif // // Set descriptors of listening sockets. // handleSetListenersInLoop(*readSet, *setFDs); // // Set descriptors of both proxy and X // connections. // handleSetReadInLoop(*readSet, *setFDs, *selectTs); // // Find out which file descriptors have // data to write. // handleSetWriteInLoop(*writeSet, *setFDs, *selectTs); } // // Prepare the masks for handling the memory- // to-memory transport. This is required even // during session negotiation. // if (agent != NULL) { handleSetAgentInLoop(*setFDs, *readSet, *writeSet, *selectTs); } // // Register time spent handling messages. // nowTs = getNewTimestamp(); diffTs = diffTimestamp(startTs, nowTs); #ifdef TEST *logofs << "Loop: Mark - 0 - at " << strMsTimestamp() << " with " << diffTs << " Ms elapsed.\n" << logofs_flush; #endif // // TODO: Should add the read time in two // parts otherwise the limits are checked // before the counters are updated with // time spent in the last loop. // if (control -> ProxyStage >= stage_operational) { statistics -> addReadTime(diffTs); } startTs = nowTs; #ifdef DEBUG *logofs << "Loop: New timestamp is " << strMsTimestamp(startTs) << ".\n" << logofs_flush; #endif return 1; } // // Let's say that we call select() to find out // if any of the handled descriptors has data, // but actually things are a bit more complex // than that. // int NXTransSelect(int *resultFDs, int *errorFDs, int *setFDs, fd_set *readSet, fd_set *writeSet, struct timeval *selectTs) { #ifdef TIME static T_timestamp lastTs; #endif if (logofs == NULL) { logofs = &cerr; } // // Control is NULL if the NX transport was // reset or never created. If control is // valid then prepare for jumping back in // the case of an error. // if (control == NULL || setjmp(context) == 1) { *resultFDs = select(*setFDs, readSet, writeSet, NULL, selectTs); *errorFDs = errno; return 0; } #if defined(TEST) || defined(INFO) *logofs << "\nNXTransSelect: Going to select the NX descriptors.\n" << logofs_flush; #endif #if defined(TEST) || defined(INFO) handleCheckSelectInLoop(*setFDs, *readSet, *writeSet, *selectTs); #endif #ifdef TIME diffTs = diffTimestamp(lastTs, getNewTimestamp()); if (diffTs > 20) { *logofs << "Loop: TIME! Spent " << diffTs << " Ms handling messages for proxy FD#" << proxyFD << ".\n" << logofs_flush; } lastTs = getNewTimestamp(); #endif #if defined(TEST) || defined(INFO) if (isTimestamp(*selectTs) == 0) { *logofs << "Loop: WARNING! Executing the select with requested " << "timeout of " << selectTs -> tv_sec << " S and " << (double) selectTs -> tv_usec / 1000 << " Ms.\n" << logofs_flush; } else if (proxy != NULL && proxy -> getFlushable(proxyFD) > 0) { *logofs << "Loop: WARNING! Proxy FD#" << proxyFD << " has " << proxy -> getFlushable(proxyFD) << " bytes to write but timeout is " << selectTs -> tv_sec << " S and " << selectTs -> tv_usec / 1000 << " Ms.\n" << logofs_flush; } #endif // // Wait for the selected sockets // or the timeout. // ESET(0); *resultFDs = select(*setFDs, readSet, writeSet, NULL, selectTs); *errorFDs = EGET(); #ifdef TIME diffTs = diffTimestamp(lastTs, getNewTimestamp()); if (diffTs > 100) { *logofs << "Loop: TIME! Spent " << diffTs << " Ms waiting for new data for proxy FD#" << proxyFD << ".\n" << logofs_flush; } lastTs = getNewTimestamp(); #endif // // Check the result of the select. // #if defined(TEST) || defined(INFO) handleCheckResultInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs, startTs); #endif // // Get time spent in select. The accouting is done // in milliseconds. This is a real problem on fast // machines where each loop is unlikely to take // more than 500 us, so consider that the results // can be inaccurate. // nowTs = getNewTimestamp(); diffTs = diffTimestamp(startTs, nowTs); #ifdef TEST *logofs << "Loop: Out of select after " << diffTs << " Ms " << "at " << strMsTimestamp(nowTs) << " with result " << *resultFDs << ".\n" << logofs_flush; #endif startTs = nowTs; #ifdef DEBUG *logofs << "Loop: New timestamp is " << strMsTimestamp(startTs) << ".\n" << logofs_flush; #endif if (control -> ProxyStage >= stage_operational) { statistics -> addIdleTime(diffTs); } if (*resultFDs < 0) { // // Check if the call was interrupted or if any of the // managed descriptors has become invalid. This can // happen to the X11 code, before the descriptor is // removed from the managed set. // #ifdef __sun if (*errorFDs == EINTR || *errorFDs == EBADF || *errorFDs == EINVAL) #else if (*errorFDs == EINTR || *errorFDs == EBADF) #endif { #ifdef TEST if (*errorFDs == EINTR) { *logofs << "Loop: Select failed due to EINTR error.\n" << logofs_flush; } else { *logofs << "Loop: WARNING! Call to select failed. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; } #endif } else { #ifdef PANIC *logofs << "Loop: PANIC! Call to select failed. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to select failed. Error is " << EGET() << " '" << ESTR() << "'.\n"; HandleCleanup(); } } return 1; } // // Perform the required actions on all // the descriptors having I/O pending. // int NXTransExecute(int *resultFDs, int *errorFDs, int *setFDs, fd_set *readSet, fd_set *writeSet, struct timeval *selectTs) { if (logofs == NULL) { logofs = &cerr; } // // Control is NULL if the NX transport was // reset or never created. If control is // valid then prepare for jumping back in // the case of an error. // if (control == NULL || setjmp(context) == 1) { return 0; } #if defined(TEST) || defined(INFO) *logofs << "\nNXTransExecute: Going to execute I/O on the NX descriptors.\n" << logofs_flush; #endif if (control -> ProxyStage >= stage_operational) { // // Check if I/O is possible on the proxy and // local agent descriptors. // if (agent != NULL) { handleAgentInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs); } #ifdef TEST *logofs << "Loop: Mark - 1 - at " << strMsTimestamp() << " with " << diffTimestamp(startTs, getTimestamp()) << " Ms elapsed.\n" << logofs_flush; #endif // // Rotate the channel that will be handled // first. // handleRotateInLoop(); // // Flush any data on newly writable sockets. // handleWritableInLoop(*resultFDs, *writeSet); #ifdef TEST *logofs << "Loop: Mark - 2 - at " << strMsTimestamp() << " with " << diffTimestamp(startTs, getTimestamp()) << " Ms elapsed.\n" << logofs_flush; #endif // // Check if any socket has become readable. // handleReadableInLoop(*resultFDs, *readSet); #ifdef TEST *logofs << "Loop: Mark - 3 - at " << strMsTimestamp() << " with " << diffTimestamp(startTs, getTimestamp()) << " Ms elapsed.\n" << logofs_flush; #endif // // Handle the scheduled events on channels. // // - Restart, if possible, any client that was // put to sleep. // // - Check if there are pointer motion events to // flush. This applies only to X server side. // // - Check if any channel has exited the conges- // tion state. // // - Check if there are images that are currently // being streamed. // handleEventsInLoop(); #ifdef TEST *logofs << "Loop: Mark - 4 - at " << strMsTimestamp() << " with " << diffTimestamp(startTs, getTimestamp()) << " Ms elapsed.\n" << logofs_flush; #endif // // Check if user sent a signal to produce // statistics. // handleStatisticsInLoop(); // // We may have flushed the proxy link or // handled data coming from the remote. // Post-process the masks and set the // selected agent descriptors as ready. // if (agent != NULL) { handleAgentLateInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs); } #ifdef TEST *logofs << "Loop: Mark - 5 - at " << strMsTimestamp() << " with " << diffTimestamp(startTs, getTimestamp()) << " Ms elapsed.\n" << logofs_flush; #endif // // Check if there is any data to flush. // Agents should flush the proxy link // explicitly. // handleFlushInLoop(); #ifdef TEST *logofs << "Loop: Mark - 6 - at " << strMsTimestamp() << " with " << diffTimestamp(startTs, getTimestamp()) << " Ms elapsed.\n" << logofs_flush; #endif } // // Check if we have an alert to show. // handleAlertInLoop(); if (control -> ProxyStage >= stage_operational) { // // Check if it's time to give up. // handleCheckSessionInLoop(); // // Check if local proxy is consuming // too many resources. // handleCheckBitrateInLoop(); // // Check coherency of internal state. // #if defined(TEST) || defined(INFO) handleCheckStateInLoop(*setFDs); #endif #ifdef TEST *logofs << "Loop: Mark - 7 - at " << strMsTimestamp() << " with " << diffTimestamp(startTs, getTimestamp()) << " Ms elapsed.\n" << logofs_flush; #endif } // // Truncate the logs if needed. // handleLogReopenInLoop(logsTs, nowTs); return 1; } // // Initialize the connection parameters and // prepare for negotiating the link with the // remote proxy. // int InitBeforeNegotiation() { // // Disable limits on core dumps. // SetCore(); // // Install the signal handlers. // InstallSignals(); // // Track how much time we spent in initialization. // nowTs = getNewTimestamp(); startTs = nowTs; initTs = nowTs; #ifdef TEST *logofs << "Loop: INIT! Taking mark for initialization at " << strMsTimestamp(initTs) << ".\n" << logofs_flush; #endif // // If not explicitly specified, determine if local // mode is client or server according to parameters // provided so far. // if (WE_SET_PROXY_MODE == 0) { cerr << "Error" << ": Please specify either the -C or -S option.\n"; HandleCleanup(); } // // Start a watchdog. If initialization cannot // be completed before timeout, then clean up // everything and exit. // if (control -> ProxyMode == proxy_client) { #ifdef TEST *logofs << "Loop: Starting watchdog process with timeout of " << control -> InitTimeout / 1000 << " seconds.\n" << logofs_flush; #endif lastWatchdog = NXTransWatchdog(control -> InitTimeout); if (IsFailed(lastWatchdog)) { #ifdef PANIC *logofs << "Loop: PANIC! Can't start the NX watchdog process.\n" << logofs_flush; #endif SetNotRunning(lastWatchdog); } #ifdef TEST else { *logofs << "Loop: Watchdog started with pid '" << lastWatchdog << "'.\n" << logofs_flush; } #endif } // // Print preliminary info. // PrintProcessInfo(); // // Set cups, multimedia and other // auxiliary ports. // SetPorts(); // // Increase the number of maximum open // file descriptors for this process. // SetDescriptors(); // // Set local endianess. // unsigned int test = 1; setHostBigEndian(*((unsigned char *) (&test)) == 0); #ifdef TEST *logofs << "Loop: Local host is " << (hostBigEndian() ? "big endian" : "little endian") << ".\n" << logofs_flush; #endif if (control -> ProxyMode == proxy_client) { // // Listen on sockets that mimic an X display to // which X clients will be able to connect (e.g. // unix:8 and/or localhost:8). // if (useTcpSocket == 1) { SetupTcpSocket(); } if (useUnixSocket == 1) { SetupUnixSocket(); } } else { // // Don't listen for X connections. // useUnixSocket = 0; useTcpSocket = 0; useAgentSocket = 0; // // Get ready to open the local display. // SetupDisplaySocket(xServerAddrFamily, xServerAddr, xServerAddrLength); } // // If we are the NX server-side proxy we need to // complete our initializazion. We will mandate // our parameters at the time the NX client will // connect. // if (control -> ProxyMode == proxy_client) { SetParameters(); } return 1; } int SetupProxyConnection() { if (proxyFD == -1) { if (WE_INITIATE_CONNECTION) { if (connectPort < 0) { connectPort = DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort; } #ifdef TEST *logofs << "Loop: Going to connect to " << connectHost << ":" << connectPort << ".\n" << logofs_flush; #endif proxyFD = ConnectToRemote(connectHost, connectPort); #ifdef TEST *logofs << "Loop: Connected to remote proxy on FD#" << proxyFD << ".\n" << logofs_flush; #endif cerr << "Info" << ": Connection to remote proxy '" << connectHost << ":" << connectPort << "' established.\n"; } else { if (listenPort < 0) { listenPort = DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort; } #ifdef TEST *logofs << "Loop: Going to wait for connection on port " << listenPort << ".\n" << logofs_flush; #endif proxyFD = WaitForRemote(listenPort); #ifdef TEST if (WE_LISTEN_FORWARDER) { *logofs << "Loop: Connected to remote forwarder on FD#" << proxyFD << ".\n" << logofs_flush; } else { *logofs << "Loop: Connected to remote proxy on FD#" << proxyFD << ".\n" << logofs_flush; } #endif } } #ifdef TEST else { *logofs << "Loop: Using the inherited connection on FD#" << proxyFD << ".\n" << logofs_flush; } #endif // // Set TCP_NODELAY on proxy descriptor // to reduce startup time. Option will // later be disabled if needed. // SetNoDelay(proxyFD, 1); // // We need non-blocking input since the // negotiation phase. // SetNonBlocking(proxyFD, 1); return 1; } // // Create the required proxy and channel classes // and get ready for handling the encoded traffic. // int InitAfterNegotiation() { #ifdef TEST *logofs << "Loop: Connection with remote proxy completed.\n" << logofs_flush; #endif cerr << "Info" << ": Connection with remote proxy completed.\n" << logofs_flush; // // If we are the server proxy we completed // our initializazion phase according to // the values provided by the client side. // if (control -> ProxyMode == proxy_server) { SetParameters(); } // // Set up the listeners for the additional // services. // SetupServiceSockets(); // // Create the proxy class and the statistics // repository and pass all the configuration // data we negotiated with the remote peer. // SetupProxyInstance(); // // We completed both parsing of user's parameters // and handlshaking with remote proxy. Now print // a brief summary including the most significant // control values. // PrintConnectionInfo(); // // Cancel the initialization watchdog. // if (IsRunning(lastWatchdog)) { KillProcess(lastWatchdog, "watchdog", SIGTERM, 1); SetNotRunning(lastWatchdog); lastSignal = 0; } // // Start the house-keeper process. It will // remove the oldest persistent caches, if // the amount of storage exceed the limits // set by the user. // StartKeeper(); // // Set the log size check timestamp. // nowTs = getNewTimestamp(); logsTs = nowTs; // // TODO: Due to the way the new NX transport is working, // the accounting of time spent handling messages must // be rewritten. In particular, at the moment it only // shows the time spent encoding and decoding messages // in the main loop, after executing a select. It doesn't // take into account the time spent in the NXTrans* calls // where messages can be encoded and decoded implicitly, // on demand of the agent. When the agent transport is // in use, these calls constitute the vast majority of // the encoding activity. The result is that the number // of KB encoded per second shown by the proxy statistics // is actually much lower than the real throughput gene- // rated by the proxy. // #ifdef TEST *logofs << "Loop: INIT! Completed initialization at " << strMsTimestamp(nowTs) << " with " << diffTimestamp(initTs, nowTs) << " Ms " << "since the init mark.\n" << logofs_flush; #endif initTs = getNewTimestamp(); // // We can now start handling binary data from // our peer proxy. // if (agent == NULL) { cerr << "Session" << ": Session started at '" << strTimestamp() << "'.\n"; } return 1; } int SetMode(int mode) { // // Set the local proxy mode. // if (control -> ProxyMode == proxy_undefined) { if (mode == NX_MODE_CLIENT) { #ifdef TEST *logofs << "Loop: INIT! Initializing with mode " << "NX_MODE_CLIENT at " << strMsTimestamp() << ".\n" << logofs_flush; #endif control -> ProxyMode = proxy_client; } else if (mode == NX_MODE_SERVER) { #ifdef TEST *logofs << "Loop: INIT! Initializing with mode " << "NX_MODE_SERVER at " << strMsTimestamp() << ".\n" << logofs_flush; #endif control -> ProxyMode = proxy_server; } else { cerr << "Error" << ": Please specify either " << "the -C or -S option.\n"; HandleCleanup(); } } return 1; } int SetupProxyInstance() { if (control -> ProxyMode == proxy_client) { proxy = new ClientProxy(proxyFD); } else { proxy = new ServerProxy(proxyFD); } if (proxy == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Error creating the NX proxy.\n" << logofs_flush; #endif cerr << "Error" << ": Error creating the NX proxy.\n"; HandleCleanup(); } // // Create the statistics repository. // statistics = new Statistics(proxy); if (statistics == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Error creating the NX statistics.\n" << logofs_flush; #endif cerr << "Error" << ": Error creating the NX statistics.\n"; HandleCleanup(); } // // If user gave us a proxy cookie than create the // X11 authorization repository and find the real // cookie to be used for our X display. // SetupAuthInstance(); // // Reset the static members in channels. // proxy -> handleChannelConfiguration(); // // Inform the proxies about the ports where they // will have to forward the network connections. // proxy -> handleDisplayConfiguration(displayHost, xServerAddrFamily, xServerAddr, xServerAddrLength); proxy -> handlePortConfiguration(cupsPort, smbPort, mediaPort, httpPort, fontPort); // // We handed over the sockaddr structure we // created when we set up the display socket // to the proxy. // xServerAddr = NULL; // // Set socket options on proxy link, then propagate link // configuration to proxy. This includes translating some // control parameters in 'local' and 'remote'. Finally // adjust cache parameters according to pack method and // session type selected by user. // if (proxy -> handleSocketConfiguration() < 0 || proxy -> handleLinkConfiguration() < 0 || proxy -> handleCacheConfiguration() < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Error configuring the NX transport.\n" << logofs_flush; #endif cerr << "Error" << ": Error configuring the NX transport.\n"; HandleCleanup(); } // // Load the message stores from the persistent // cache. // proxy -> handleLoad(load_if_first); // // Inform the proxy that from now on it can // start handling the encoded data. // proxy -> setOperational(); // // Are we going to use an internal IPC connection // with an agent? In this case create the channel // by using the socket descriptor provided by the // caller at the proxy initialization. // SetupAgentInstance(); // // Check if we need to verify the existence of // a matching client cache at shutdown. // #ifdef MATCH control -> PersistentCacheCheckOnShutdown = 1; #endif // // Flush any data produced so far. // proxy -> handleFlush(); #if defined(TEST) || defined(INFO) if (proxy -> getFlushable(proxyFD) > 0) { *logofs << "Loop: WARNING! Proxy FD#" << proxyFD << " has data " << "to flush after setup of the NX transport.\n" << logofs_flush; } #endif return 1; } int SetupAuthInstance() { // // If user gave us a proxy cookie, then create the // X11 authorization repository and find the real // cookie to be used for our X display. // if (control -> ProxyMode == proxy_server) { if (authCookie != NULL && *authCookie != '\0') { if (useLaunchdSocket == 1) { // // If we are going to retrieve the X11 autho- // rization through the launchd service, make // a connection to its socket to trigger the // X server starting. // sockaddr_un launchdAddrUnix; unsigned int launchdAddrLength = sizeof(sockaddr_un); int launchdAddrFamily = AF_UNIX; launchdAddrUnix.sun_family = AF_UNIX; const int launchdAddrNameLength = 108; int success = -1; strncpy(launchdAddrUnix.sun_path, displayHost, launchdAddrNameLength); *(launchdAddrUnix.sun_path + launchdAddrNameLength - 1) = '\0'; #ifdef TEST *logofs << "Loop: Connecting to launchd service " << "on Unix port '" << displayHost << "'.\n" << logofs_flush; #endif int launchdFd = socket(launchdAddrFamily, SOCK_STREAM, PF_UNSPEC); if (launchdFd < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Call to socket failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif } else if ((success = connect(launchdFd, (sockaddr *) &launchdAddrUnix, launchdAddrLength)) < 0) { #ifdef WARNING *logofs << "Loop: WARNING! Connection to launchd service " << "on Unix port '" << displayHost << "' failed " << "with error " << EGET() << ", '" << ESTR() << "'.\n" << logofs_flush; #endif } if (launchdFd >= 0) { close(launchdFd); } // // The real cookie will not be available // until the X server starts. Query for the // cookie in a loop, unless the connection // to the launchd service failed. // int attempts = (success < 0 ? 1 : 10); for (int i = 0; i < attempts; i++) { delete auth; auth = new Auth(displayHost, authCookie); if (auth != NULL && auth -> isFake() == 1) { usleep(200000); continue; } break; } } else { auth = new Auth(displayHost, authCookie); } if (auth == NULL || auth -> isValid() != 1) { #ifdef PANIC *logofs << "Loop: PANIC! Error creating the X authorization.\n" << logofs_flush; #endif cerr << "Error" << ": Error creating the X authorization.\n"; HandleCleanup(); } else if (auth -> isFake() == 1) { #ifdef WARNING *logofs << "Loop: WARNING! Could not retrieve the X server " << "authentication cookie.\n" << logofs_flush; #endif cerr << "Warning" << ": Failed to read data from the X " << "auth command.\n"; cerr << "Warning" << ": Generated a fake cookie for X " << "authentication.\n"; } } else { #ifdef TEST *logofs << "Loop: No proxy cookie was provided for " << "authentication.\n" << logofs_flush; #endif cerr << "Info" << ": No proxy cookie was provided for " << "authentication.\n"; #ifdef TEST *logofs << "Loop: Forwarding the real X authorization " << "cookie.\n" << logofs_flush; #endif cerr << "Info" << ": Forwarding the real X authorization " << "cookie.\n"; } } return 1; } int SetupAgentInstance() { if (control -> ProxyMode == proxy_client && useAgentSocket == 1) { // // This will temporarily disable signals to safely // load the cache, then will send a control packet // to the remote end, telling that cache has to be // loaded, so it's important that proxy is already // set in operational state. // int result; if (agent != NULL) { result = proxy -> handleNewAgentConnection(agent); } else { result = proxy -> handleNewConnection(channel_x11, agentFD[1]); } if (result < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Error creating the NX agent connection.\n" << logofs_flush; #endif cerr << "Error" << ": Error creating the NX agent connection.\n"; HandleCleanup(); } } return 1; } int SetupTcpSocket() { // // Open TCP socket emulating local display. // tcpFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); if (tcpFD == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to socket failed for TCP socket" << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to socket failed for TCP socket" << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; HandleCleanup(); } else if (SetReuseAddress(tcpFD) < 0) { HandleCleanup(); } unsigned int proxyPortTCP = X_TCP_PORT + proxyPort; sockaddr_in tcpAddr; tcpAddr.sin_family = AF_INET; tcpAddr.sin_port = htons(proxyPortTCP); tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(tcpFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to bind failed for TCP port " << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to bind failed for TCP port " << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; HandleCleanup(); } if (listen(tcpFD, 8) == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to listen failed for TCP port " << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to listen failed for TCP port " << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; HandleCleanup(); } return 1; } int SetupUnixSocket() { // // Open UNIX domain socket for display. // unixFD = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); if (unixFD == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to socket failed for UNIX domain" << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to socket failed for UNIX domain" << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; HandleCleanup(); } sockaddr_un unixAddr; unixAddr.sun_family = AF_UNIX; char dirName[DEFAULT_STRING_LENGTH]; snprintf(dirName, DEFAULT_STRING_LENGTH - 1, "%s/.X11-unix", control -> TempPath); *(dirName + DEFAULT_STRING_LENGTH - 1) = '\0'; struct stat dirStat; if ((stat(dirName, &dirStat) == -1) && (EGET() == ENOENT)) { mkdir(dirName, (0777 | S_ISVTX)); chmod(dirName, (0777 | S_ISVTX)); } snprintf(unixSocketName, DEFAULT_STRING_LENGTH - 1, "%s/X%d", dirName, proxyPort); strncpy(unixAddr.sun_path, unixSocketName, 108); #ifdef TEST *logofs << "Loop: Assuming Unix socket with name '" << unixAddr.sun_path << "'.\n" << logofs_flush; #endif *(unixAddr.sun_path + 107) = '\0'; if (bind(unixFD, (sockaddr *) &unixAddr, sizeof(unixAddr)) == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to bind failed for UNIX domain socket " << unixSocketName << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to bind failed for UNIX domain socket " << unixSocketName << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; HandleCleanup(); } if (listen(unixFD, 8) == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to listen failed for UNIX domain socket " << unixSocketName << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to listen failed for UNIX domain socket " << unixSocketName << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; HandleCleanup(); } // // Let any local user to gain access to socket. // chmod(unixSocketName, 0777); return 1; } // // The following is a dumb copy-paste. The // nxcompsh library should offer a better // implementation. // int SetupDisplaySocket(int &xServerAddrFamily, sockaddr *&xServerAddr, unsigned int &xServerAddrLength) { xServerAddrFamily = AF_INET; xServerAddr = NULL; xServerAddrLength = 0; char *display; if (*displayHost == '\0') { // // Assume DISPLAY as the X server to which // we will forward the proxied connections. // This means that NX parameters have been // passed through other means. // display = getenv("DISPLAY"); if (display == NULL || *display == '\0') { #ifdef PANIC *logofs << "Loop: PANIC! Host X server DISPLAY is not set.\n" << logofs_flush; #endif cerr << "Error" << ": Host X server DISPLAY is not set.\n"; HandleCleanup(); } else if (strncasecmp(display, "nx/nx,", 6) == 0 || strncasecmp(display, "nx,", 3) == 0 || strncasecmp(display, "nx:", 3) == 0) { #ifdef PANIC *logofs << "Loop: PANIC! NX transport on host X server '" << display << "' not supported.\n" << logofs_flush; #endif cerr << "Error" << ": NX transport on host X server '" << display << "' not supported.\n"; cerr << "Error" << ": Please run the local proxy specifying " << "the host X server to connect to.\n"; HandleCleanup(); } else if (strlen(display) >= DEFAULT_STRING_LENGTH) { #ifdef PANIC *logofs << "Loop: PANIC! Host X server DISPLAY cannot exceed " << DEFAULT_STRING_LENGTH << " characters.\n" << logofs_flush; #endif cerr << "Error" << ": Host X server DISPLAY cannot exceed " << DEFAULT_STRING_LENGTH << " characters.\n"; HandleCleanup(); } strcpy(displayHost, display); } display = new char[strlen(displayHost) + 1]; if (display == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Out of memory handling DISPLAY variable.\n" << logofs_flush; #endif cerr << "Error" << ": Out of memory handling DISPLAY variable.\n"; HandleCleanup(); } strcpy(display, displayHost); #ifdef __APPLE__ if (strncasecmp(display, "/tmp/launch", 11) == 0) { #ifdef TEST *logofs << "Loop: Using launchd service on socket '" << display << "'.\n" << logofs_flush; #endif useLaunchdSocket = 1; } #endif char *separator = rindex(display, ':'); if ((separator == NULL) || !isdigit(*(separator + 1))) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid display '" << display << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid display '" << display << "'.\n"; HandleCleanup(); } *separator = '\0'; xPort = atoi(separator + 1); #ifdef TEST *logofs << "Loop: Using local X display '" << displayHost << "' with host '" << display << "' and port '" << xPort << "'.\n" << logofs_flush; #endif #ifdef __APPLE__ if (separator == display || strcmp(display, "unix") == 0 || useLaunchdSocket == 1) #else if (separator == display || strcmp(display, "unix") == 0) #endif { // // UNIX domain port. // #ifdef TEST *logofs << "Loop: Using real X server on UNIX domain socket.\n" << logofs_flush; #endif sockaddr_un *xServerAddrUNIX = new sockaddr_un; xServerAddrFamily = AF_UNIX; xServerAddrUNIX -> sun_family = AF_UNIX; // // The scope of this function is to fill either the sockaddr_un // (when the display is set to the Unix Domain socket) or the // sockaddr_in structure (when connecting by TCP) only once, so // that the structure will be later used at the time the server // proxy will try to forward the connection to the X server. We // don't need to verify that the socket does exist at the pre- // sent moment. The method that forwards the connection will // perform the required checks and will retry, if needed. Anyway // we need to select the name of the socket, so we check if the // well-known directory exists and take that as an indication of // where the socket will be created. // struct stat statInfo; char unixSocketDir[DEFAULT_STRING_LENGTH]; snprintf(unixSocketDir, DEFAULT_STRING_LENGTH - 1, "%s/.X11-unix", control -> TempPath); #ifdef __APPLE__ if (useLaunchdSocket == 1) { char *slash = rindex(display, '/'); if (slash != NULL) { *slash = '\0'; } snprintf(unixSocketDir, DEFAULT_STRING_LENGTH - 1, "%s", display); } #endif *(unixSocketDir + DEFAULT_STRING_LENGTH - 1) = '\0'; #ifdef TEST *logofs << "Loop: Assuming X socket in directory '" << unixSocketDir << "'.\n" << logofs_flush; #endif if (stat(unixSocketDir, &statInfo) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't determine the location of " << "the X display socket.\n" << logofs_flush; #endif cerr << "Error" << ": Can't determine the location of " << "the X display socket.\n"; #ifdef PANIC *logofs << "Loop: PANIC! Error " << EGET() << " '" << ESTR() << "' checking '" << unixSocketDir << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Error " << EGET() << " '" << ESTR() << "' checking '" << unixSocketDir << "'.\n"; HandleCleanup(); } sprintf(unixSocketName, "%s/X%d", unixSocketDir, xPort); #ifdef __APPLE__ if (useLaunchdSocket == 1) { strncpy(unixSocketName, displayHost, DEFAULT_STRING_LENGTH - 1); } #endif #ifdef TEST *logofs << "Loop: Assuming X socket name '" << unixSocketName << "'.\n" << logofs_flush; #endif strcpy(xServerAddrUNIX -> sun_path, unixSocketName); xServerAddr = (sockaddr *) xServerAddrUNIX; xServerAddrLength = sizeof(sockaddr_un); } else { // // TCP port. // #ifdef TEST *logofs << "Loop: Using real X server on TCP port.\n" << logofs_flush; #endif xServerAddrFamily = AF_INET; int xServerIPAddr = GetHostAddress(display); if (xServerIPAddr == 0) { #ifdef PANIC *logofs << "Loop: PANIC! Unknown display host '" << display << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Unknown display host '" << display << "'.\n"; HandleCleanup(); } sockaddr_in *xServerAddrTCP = new sockaddr_in; xServerAddrTCP -> sin_family = AF_INET; xServerAddrTCP -> sin_port = htons(X_TCP_PORT + xPort); xServerAddrTCP -> sin_addr.s_addr = xServerIPAddr; xServerAddr = (sockaddr *) xServerAddrTCP; xServerAddrLength = sizeof(sockaddr_in); } delete [] display; return 1; } int SetupServiceSockets() { if (control -> ProxyMode == proxy_client) { if (useCupsSocket) { if ((cupsFD = ListenConnection(cupsPort, "CUPS")) < 0) { useCupsSocket = 0; } } if (useAuxSocket) { if ((auxFD = ListenConnection(auxPort, "auxiliary X11")) < 0) { useAuxSocket = 0; } } if (useSmbSocket) { if ((smbFD = ListenConnection(smbPort, "SMB")) < 0) { useSmbSocket = 0; } } if (useMediaSocket) { if ((mediaFD = ListenConnection(mediaPort, "media")) < 0) { useMediaSocket = 0; } } if (useHttpSocket) { if ((httpFD = ListenConnection(httpPort, "http")) < 0) { useHttpSocket = 0; } } useFontSocket = 0; } else { // // Disable the font server connections if // they are not supported by the remote // proxy. // if (useFontSocket) { if (control -> isProtoStep7() == 1) { int port = atoi(fontPort); if ((fontFD = ListenConnection(port, "font")) < 0) { useFontSocket = 0; } } else { #ifdef WARNING *logofs << "Loop: WARNING! Font server connections not supported " << "by the remote proxy.\n" << logofs_flush; #endif cerr << "Warning" << ": Font server connections not supported " << "by the remote proxy.\n"; useFontSocket = 0; } } useCupsSocket = 0; useAuxSocket = 0; useSmbSocket = 0; useMediaSocket = 0; useHttpSocket = 0; } // // Slave channels can be originated // by both sides. // if (useSlaveSocket) { if (control -> isProtoStep7() == 1) { if ((slaveFD = ListenConnection(slavePort, "slave")) < 0) { useSlaveSocket = 0; } } else { #ifdef WARNING *logofs << "Loop: WARNING! Slave connections not supported " << "by the remote proxy.\n" << logofs_flush; #endif cerr << "Warning" << ": Slave connections not supported " << "by the remote proxy.\n"; useSlaveSocket = 0; } } return 1; } int ListenConnection(int port, const char *label) { sockaddr_in tcpAddr; unsigned int portTCP = port; int newFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); if (newFD == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to socket failed for " << label << " TCP socket. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to socket failed for " << label << " TCP socket. Error is " << EGET() << " '" << ESTR() << "'.\n"; goto SetupSocketError; } else if (SetReuseAddress(newFD) < 0) { goto SetupSocketError; } tcpAddr.sin_family = AF_INET; tcpAddr.sin_port = htons(portTCP); tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(newFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to bind failed for " << label << " TCP port " << port << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to bind failed for " << label << " TCP port " << port << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; goto SetupSocketError; } if (listen(newFD, 8) == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to bind failed for " << label << " TCP port " << port << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to bind failed for " << label << " TCP port " << port << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; goto SetupSocketError; } return newFD; SetupSocketError: if (newFD != -1) { close(newFD); } // // May optionally return. The session would // continue without the service. The problem // with this approach is that it would make // harder to track problems with allocation // of ports in clients and server. // HandleCleanup(); } static int AcceptConnection(int fd, int domain, const char *label) { struct sockaddr newAddr; socklen_t addrLen = sizeof(newAddr); #ifdef TEST if (domain == AF_UNIX) { *logofs << "Loop: Going to accept new Unix " << label << " connection on FD#" << fd << ".\n" << logofs_flush; } else { *logofs << "Loop: Going to accept new TCP " << label << " connection on FD#" << fd << ".\n" << logofs_flush; } #endif int newFD = accept(fd, &newAddr, &addrLen); if (newFD < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Call to accept failed for " << label << " connection. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to accept failed for " << label << " connection. Error is " << EGET() << " '" << ESTR() << "'.\n"; } return newFD; } void HandleShutdown() { if (proxy -> getShutdown() == 0) { #ifdef PANIC *logofs << "Loop: PANIC! No shutdown of proxy link " << "performed by remote proxy.\n" << logofs_flush; #endif // // Close the socket before showing the alert. // It seems that the closure of the socket can // sometimes take several seconds, even after // the connection is broken. The result is that // the dialog can be shown long after the user // has gone after the failed session. Note that // disabling the linger timeout does not seem // to make any difference. // CleanupSockets(); cerr << "Error" << ": Connection with remote peer broken.\n"; #ifdef TEST *logofs << "Loop: Bytes received so far are " << (unsigned long long) statistics -> getBytesIn() << ".\n" << logofs_flush; #endif cerr << "Error" << ": Please check the state of your " << "network and retry.\n"; handleTerminatingInLoop(); if (control -> ProxyMode == proxy_server) { #ifdef TEST *logofs << "Loop: Showing the proxy abort dialog.\n" << logofs_flush; #endif HandleAlert(ABORT_PROXY_CONNECTION_ALERT, 1); handleAlertInLoop(); } } #ifdef TEST else { *logofs << "Loop: Finalized the remote proxy shutdown.\n" << logofs_flush; } #endif HandleCleanup(); } void WaitCleanup() { T_timestamp selectTs; while (NXTransRunning(NX_FD_ANY)) { setTimestamp(selectTs, control -> PingTimeout); NXTransContinue(&selectTs); } } int KillProcess(int pid, const char *label, int signal, int wait) { if (pid > 0) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Killing the " << label << " process '" << pid << "' from process with pid '" << getpid() << "' with signal '" << DumpSignal(signal) << "'.\n" << logofs_flush; #endif signal = (signal == 0 ? SIGTERM : signal); if (kill(pid, signal) < 0 && EGET() != ESRCH) { #ifdef PANIC *logofs << "Loop: PANIC! Couldn't kill the " << label << " process with pid '" << pid << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Couldn't kill the " << label << " process with pid '" << pid << "'.\n"; } if (wait == 1) { WaitChild(pid, label, 1); } return 1; } else { #ifdef TEST *logofs << "Loop: No " << label << " process " << "to kill with pid '" << pid << "'.\n" << logofs_flush; #endif return 0; } } int CheckProcess(int pid, const char *label) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Checking the " << label << " process '" << pid << "' from process with pid '" << getpid() << "'.\n" << logofs_flush; #endif if (kill(pid, SIGCONT) < 0 && EGET() == ESRCH) { #ifdef WARNING *logofs << "Loop: WARNING! The " << label << " process " << "with pid '" << pid << "' has exited.\n" << logofs_flush; #endif cerr << "Warning" << ": The " << label << " process " << "with pid '" << pid << "' has exited.\n"; return 0; } return 1; } int StartKeeper() { #if defined(TEST) || defined(INFO) if (IsRunning(lastKeeper) == 1 || IsRestarting(lastKeeper) == 1) { #ifdef PANIC *logofs << "Loop: PANIC! The house-keeping process is " << "alreay running with pid '" << lastKeeper << "'.\n" << logofs_flush; #endif HandleCleanup(); } #endif // // Don't care harvesting the persistent caches if // the memory cache is not enabled. If the memory // cache is not enabled neither we produced per- // sistent caches. The user can still delete any // persistent cache produced by the previous runs // by using the client GUI. // // TODO: At the moment the user doesn't have a way // to specify the amount of disk space to use for // the persistent caches, but only the amount of // space to use for images. // if (control -> LocalTotalStorageSize > 0) { #ifdef TEST *logofs << "Loop: Starting the house-keeping process with " << "storage size " << control -> PersistentCacheDiskLimit << ".\n" << logofs_flush; #endif lastKeeper = NXTransKeeper(control -> PersistentCacheDiskLimit, 0, control -> RootPath); if (IsFailed(lastKeeper)) { #ifdef WARNING *logofs << "Loop: WARNING! Failed to start the NX keeper process.\n" << logofs_flush; #endif cerr << "Warning" << ": Failed to start the NX keeper process.\n"; SetNotRunning(lastKeeper); } #ifdef TEST else { *logofs << "Loop: Keeper started with pid '" << lastKeeper << "'.\n" << logofs_flush; } #endif } #ifdef TEST else { *logofs << "Loop: Nothing to do for the keeper process " << "with persistent cache not enabled.\n" << logofs_flush; } #endif return 1; } void HandleCleanup(int code) { #ifdef TEST *logofs << "Loop: Going to clean up system resources " << "in process '" << getpid() << "'.\n" << logofs_flush; #endif handleTerminatedInLoop(); // // Suspend any signal while cleaning up. // DisableSignals(); if (getpid() == lastProxy) { // // Terminate all the children. // CleanupChildren(); // // Close all listeners. // CleanupListeners(); // // Close all sockets. // CleanupSockets(); // // Release the global objects. // CleanupGlobal(); // // Restore the original signal handlers. // RestoreSignals(); } // // This is our last chance to print a message. If this // is the process which created the transport we will // jump back into the loop, letting the caller find out // that the connection is broken, otherwise we assume // that this is a child of the proxy and so we will // safely exit. // #ifdef TEST if (getpid() == lastProxy) { *logofs << "Loop: Reverting to loop context in process with " << "pid '" << getpid() << "' at " << strMsTimestamp() << ".\n" << logofs_flush; } else { *logofs << "Loop: Exiting from child process with pid '" << getpid() << "' at " << strMsTimestamp() << ".\n" << logofs_flush; } #endif if (getpid() == lastProxy) { // // Reset all values to their default. // CleanupLocal(); CleanupStreams(); longjmp(context, 1); } else { // // Give a last chance to the process // to cleanup the ancillary classes. // CleanupKeeper(); CleanupStreams(); exit(code); } } void CleanupKeeper() { if (keeper != NULL) { #ifdef TEST *logofs << "Loop: Freeing up keeper in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif delete keeper; keeper = NULL; } } void CleanupStreams() { // // TODO: The cleanup procedure skips deletion of // the I/O streams under Windows. This is intended // to avoid a strange segfault occurring randomly, // at the time the proxy is being shut down. // #ifndef __CYGWIN32__ #ifdef TEST *logofs << "Loop: Freeing up streams in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif if (logofs != NULL && logofs != &cerr && *errorsFileName != '\0') { *logofs << flush; delete logofs; // // Let the log go again to the standard // error. // logofs = &cerr; } if (statofs != NULL && statofs != &cerr && *statsFileName != '\0') { *statofs << flush; delete statofs; statofs = NULL; } if (errofs != NULL) { *errofs << flush; if (errofs == &cerr) { errofs = NULL; } else if (errsbuf != NULL) { cerr.rdbuf(errsbuf); errsbuf = NULL; delete errofs; } errofs = NULL; } #endif /* #ifndef __CYGWIN32__ */ // // Reset these as they can't be reset // in CleanupLocal(). // *sessionFileName = '\0'; *errorsFileName = '\0'; *optionsFileName = '\0'; *statsFileName = '\0'; } void CleanupChildren() { // // Remove any watchdog. // if (IsRunning(lastWatchdog)) { KillProcess(lastWatchdog, "watchdog", SIGTERM, 1); SetNotRunning(lastWatchdog); lastSignal = 0; } // // Kill the cache house-keeping process. // if (IsRunning(lastKeeper)) { KillProcess(lastKeeper, "house-keeping", SIGTERM, 1); SetNotRunning(lastKeeper); } // // Let any running dialog to continue until it is // closed by the user. In general this is the exp- // ected behaviour, as for example when we are // exiting because the link was abrouptedly shut // down. // if (IsRunning(lastDialog)) { #if defined(TEST) || defined(INFO) *logofs << "Loop: WARNING! Leaving the dialog process '" << lastDialog << "' running in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif SetNotRunning(lastDialog); } // // Give user a chance to start a new session. // if (control -> EnableRestartOnShutdown == 1) { #ifdef WARNING *logofs << "Loop: WARNING! Respawning the NX client " << "on display '" << displayHost << "'.\n" << logofs_flush; #endif NXTransClient(displayHost); } for (int i = 0; i < control -> KillDaemonOnShutdownNumber; i++) { #ifdef WARNING *logofs << "Loop: WARNING! Killing the NX daemon with " << "pid '" << control -> KillDaemonOnShutdown[i] << "'.\n" << logofs_flush; #endif KillProcess(control -> KillDaemonOnShutdown[i], "daemon", SIGTERM, 0); } } void CleanupGlobal() { if (proxy != NULL) { #ifdef TEST *logofs << "Loop: Freeing up proxy in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif delete proxy; proxy = NULL; } if (agent != NULL) { #ifdef TEST *logofs << "Loop: Freeing up agent in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif delete agent; agent = NULL; } if (auth != NULL) { #ifdef TEST *logofs << "Loop: Freeing up auth data in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif delete auth; auth = NULL; } if (statistics != NULL) { #ifdef TEST *logofs << "Loop: Freeing up statistics in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif delete statistics; statistics = NULL; } if (control != NULL) { #ifdef TEST *logofs << "Loop: Freeing up control in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif delete control; control = NULL; } } void CleanupConnections() { if (proxy -> getChannels(channel_x11) != 0) { #ifdef TEST *logofs << "Loop: Closing any remaining X connections.\n" << logofs_flush; #endif proxy -> handleCloseAllXConnections(); #ifdef TEST *logofs << "Loop: Closing any remaining listener.\n" << logofs_flush; #endif proxy -> handleCloseAllListeners(); } proxy -> handleFinish(); } void CleanupListeners() { if (useTcpSocket == 1) { if (tcpFD != -1) { #ifdef TEST *logofs << "Loop: Closing TCP listener in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(tcpFD); tcpFD = -1; } useTcpSocket = 0; } if (useUnixSocket == 1) { if (unixFD != -1) { #ifdef TEST *logofs << "Loop: Closing UNIX listener in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(unixFD); unixFD = -1; } if (*unixSocketName != '\0') { #ifdef TEST *logofs << "Loop: Going to remove the Unix domain socket '" << unixSocketName << "' in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif unlink(unixSocketName); } useUnixSocket = 0; } if (useAgentSocket == 1) { // // There is no listener for the // agent descriptor. // useAgentSocket = 0; } if (useCupsSocket == 1) { if (cupsFD != -1) { #ifdef TEST *logofs << "Loop: Closing CUPS listener in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(cupsFD); cupsFD = -1; } useCupsSocket = 0; } if (useAuxSocket == 1) { if (auxFD != -1) { #ifdef TEST *logofs << "Loop: Closing auxiliary X11 listener " << "in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(auxFD); auxFD = -1; } useAuxSocket = 0; } if (useSmbSocket == 1) { if (smbFD != -1) { #ifdef TEST *logofs << "Loop: Closing SMB listener in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(smbFD); smbFD = -1; } useSmbSocket = 0; } if (useMediaSocket == 1) { if (mediaFD != -1) { #ifdef TEST *logofs << "Loop: Closing multimedia listener in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(mediaFD); mediaFD = -1; } useMediaSocket = 0; } if (useHttpSocket == 1) { if (httpFD != -1) { #ifdef TEST *logofs << "Loop: Closing http listener in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(httpFD); httpFD = -1; } useHttpSocket = 0; } if (useFontSocket == 1) { if (fontFD != -1) { #ifdef TEST *logofs << "Loop: Closing font server listener in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(fontFD); fontFD = -1; } useFontSocket = 0; } if (useSlaveSocket == 1) { if (slaveFD != -1) { #ifdef TEST *logofs << "Loop: Closing slave listener in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(slaveFD); slaveFD = -1; } useSlaveSocket = 0; } } void CleanupSockets() { if (proxyFD != -1) { #ifdef TEST *logofs << "Loop: Closing proxy FD in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(proxyFD); proxyFD = -1; } if (agentFD[1] != -1) { #ifdef TEST *logofs << "Loop: Closing agent FD in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif close(agentFD[1]); agentFD[0] = -1; agentFD[1] = -1; } } void CleanupLocal() { *homeDir = '\0'; *rootDir = '\0'; *tempDir = '\0'; *systemDir = '\0'; *sessionDir = '\0'; *linkSpeedName = '\0'; *cacheSizeName = '\0'; *shsegSizeName = '\0'; *imagesSizeName = '\0'; *bitrateLimitName = '\0'; *packMethodName = '\0'; *productName = '\0'; packMethod = -1; packQuality = -1; *sessionType = '\0'; *sessionId = '\0'; parsedOptions = 0; parsedCommand = 0; *remoteData = '\0'; remotePosition = 0; tcpFD = -1; unixFD = -1; cupsFD = -1; auxFD = -1; smbFD = -1; mediaFD = -1; httpFD = -1; fontFD = -1; slaveFD = -1; proxyFD = -1; agentFD[0] = -1; agentFD[1] = -1; useUnixSocket = 1; useTcpSocket = 1; useCupsSocket = 0; useAuxSocket = 0; useSmbSocket = 0; useMediaSocket = 0; useHttpSocket = 0; useFontSocket = 0; useSlaveSocket = 0; useAgentSocket = 0; useNoDelay = -1; usePolicy = -1; useRender = -1; useTaint = -1; *unixSocketName = '\0'; *connectHost = '\0'; *acceptHost = '\0'; *listenHost = '\0'; *displayHost = '\0'; *authCookie = '\0'; proxyPort = DEFAULT_NX_PROXY_PORT; xPort = DEFAULT_NX_X_PORT; xServerAddrFamily = -1; xServerAddrLength = 0; delete xServerAddr; xServerAddr = NULL; listenPort = -1; connectPort = -1; cupsPort = -1; auxPort = -1; smbPort = -1; mediaPort = -1; httpPort = -1; slavePort = -1; *fontPort = '\0'; *bindHost = '\0'; bindPort = -1; initTs = nullTimestamp(); startTs = nullTimestamp(); logsTs = nullTimestamp(); nowTs = nullTimestamp(); diffTs = 0; lastProxy = 0; lastDialog = 0; lastWatchdog = 0; lastKeeper = 0; lastStatus = 0; lastKill = 0; lastDestroy = 0; lastReadableTs = nullTimestamp(); lastAlert.code = 0; lastAlert.local = 0; lastMasks.blocked = 0; lastMasks.installed = 0; memset(&lastMasks.saved, 0, sizeof(sigset_t)); for (int i = 0; i < 32; i++) { lastMasks.enabled[i] = 0; lastMasks.forward[i] = 0; memset(&lastMasks.action[i], 0, sizeof(struct sigaction)); } lastSignal = 0; memset(&lastTimer.action, 0, sizeof(struct sigaction)); memset(&lastTimer.value, 0, sizeof(struct itimerval)); lastTimer.start = nullTimestamp(); lastTimer.next = nullTimestamp(); } int CheckAbort() { if (lastSignal != 0) { #ifdef TEST *logofs << "Loop: Aborting the procedure due to signal '" << lastSignal << "', '" << DumpSignal(lastSignal) << "'.\n" << logofs_flush; #endif cerr << "Info" << ": Aborting the procedure due to signal '" << lastSignal << "'.\n"; lastSignal = 0; return 1; } return 0; } void HandleAbort() { if (logofs == NULL) { logofs = &cerr; } *logofs << flush; handleTerminatingInLoop(); if (lastSignal == SIGHUP) { lastSignal = 0; } // // The current default is to just quit the program. // Code has not been updated to deal with the new // NX transport loop. // if (control -> EnableCoreDumpOnAbort == 1) { if (agent != NULL) { cerr << "Session" << ": Terminating session at '" << strTimestamp() << "'.\n"; } cerr << "Error" << ": Generating a core file to help " << "the investigations.\n"; cerr << "Session" << ": Session terminated at '" << strTimestamp() << "'.\n"; cerr << flush; signal(SIGABRT, SIG_DFL); raise(SIGABRT); } #ifdef TEST *logofs << "Loop: Showing the proxy abort dialog.\n" << logofs_flush; #endif if (control -> ProxyMode == proxy_server) { // // Close the socket before showing the alert. // It seems that the closure of the socket can // sometimes take several seconds, even after // the connection is broken. // CleanupSockets(); if (lastKill == 0) { HandleAlert(ABORT_PROXY_CONNECTION_ALERT, 1); } else { HandleAlert(ABORT_PROXY_SHUTDOWN_ALERT, 1); } handleAlertInLoop(); } HandleCleanup(); } void HandleAlert(int code, int local) { if (lastAlert.code == 0) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Requesting an alert dialog with code " << code << " and local " << local << ".\n" << logofs_flush; #endif lastAlert.code = code; lastAlert.local = local; } #if defined(TEST) || defined(INFO) else { *logofs << "Loop: WARNING! Alert dialog already requested " << "with code " << lastAlert.code << ".\n" << logofs_flush; } #endif return; } void FlushCallback(int length) { if (flushCallback != NULL) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Reporting a flush request at " << strMsTimestamp() << " with " << length << " bytes written.\n" << logofs_flush; #endif (*flushCallback)(flushParameter, length); } #if defined(TEST) || defined(INFO) else if (control -> ProxyMode == proxy_client) { *logofs << "Loop: WARNING! Can't find a flush " << "callback in process with pid '" << getpid() << "'.\n" << logofs_flush; } #endif } void KeeperCallback() { if (IsRunning(lastKeeper) == 0) { // // Let the house-keeping process take care // of the persistent image cache. // if (control -> ImageCacheEnableLoad == 1 || control -> ImageCacheEnableSave == 1) { #ifdef TEST *logofs << "Loop: Starting the house-keeping process with " << "image storage size " << control -> ImageCacheDiskLimit << ".\n" << logofs_flush; #endif lastKeeper = NXTransKeeper(0, control -> ImageCacheDiskLimit, control -> RootPath); if (IsFailed(lastKeeper)) { #ifdef WARNING *logofs << "Loop: WARNING! Can't start the NX keeper process.\n" << logofs_flush; #endif SetNotRunning(lastKeeper); } #ifdef TEST else { *logofs << "Loop: Keeper started with pid '" << lastKeeper << "'.\n" << logofs_flush; } #endif } #ifdef TEST else { *logofs << "Loop: Nothing to do for the keeper process " << "with image cache not enabled.\n" << logofs_flush; } #endif } #ifdef TEST else { *logofs << "Loop: Nothing to do with the keeper process " << "already running.\n" << logofs_flush; } #endif } void InstallSignals() { #ifdef TEST *logofs << "Loop: Installing signals in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif for (int i = 0; i < 32; i++) { if (CheckSignal(i) == 1 && lastMasks.enabled[i] == 0) { InstallSignal(i, NX_SIGNAL_ENABLE); } } lastMasks.installed = 1; } void RestoreSignals() { #ifdef TEST *logofs << "Loop: Restoring signals in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif if (lastMasks.installed == 1) { // // Need to keep monitoring the children. // for (int i = 0; i < 32; i++) { if (lastMasks.enabled[i] == 1) { RestoreSignal(i); } } } lastMasks.installed = 0; if (lastMasks.blocked == 1) { EnableSignals(); } } void DisableSignals() { if (lastMasks.blocked == 0) { sigset_t newMask; sigemptyset(&newMask); // // Block also the other signals that may be // installed by the agent, that are those // signals for which the function returns 2. // for (int i = 0; i < 32; i++) { if (CheckSignal(i) > 0) { #ifdef DUMP *logofs << "Loop: Disabling signal " << i << " '" << DumpSignal(i) << "' in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif sigaddset(&newMask, i); } } sigprocmask(SIG_BLOCK, &newMask, &lastMasks.saved); lastMasks.blocked++; } #ifdef TEST else { *logofs << "Loop: WARNING! Signals were already blocked in " << "process with pid '" << getpid() << "'.\n" << logofs_flush; } #endif } void EnableSignals() { if (lastMasks.blocked == 1) { #ifdef TEST *logofs << "Loop: Enabling signals in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif sigprocmask(SIG_SETMASK, &lastMasks.saved, NULL); lastMasks.blocked = 0; } else { #ifdef WARNING *logofs << "Loop: WARNING! Signals were not blocked in " << "process with pid '" << getpid() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Signals were not blocked in " << "process with pid '" << getpid() << "'.\n"; } } void InstallSignal(int signal, int action) { if (lastMasks.enabled[signal] == 1) { if (action == NX_SIGNAL_FORWARD) { #ifdef TEST *logofs << "Loop: Forwarding handler for signal " << signal << " '" << DumpSignal(signal) << "' in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif lastMasks.forward[signal] = 1; return; } #ifdef TEST else { *logofs << "Loop: Reinstalling handler for signal " << signal << " '" << DumpSignal(signal) << "' in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; } #endif } #ifdef TEST else { *logofs << "Loop: Installing handler for signal " << signal << " '" << DumpSignal(signal) << "' in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; } #endif if (signal == SIGALRM && isTimestamp(lastTimer.start)) { ResetTimer(); } struct sigaction newAction; newAction.sa_handler = HandleSignal; // // This field doesn't exist on most OSes except // Linux. We keep setting the field to NULL to // avoid side-effects in the case the field is // a value return. // #if defined(__linux__) newAction.sa_restorer = NULL; #endif sigemptyset(&(newAction.sa_mask)); if (signal == SIGCHLD) { newAction.sa_flags = SA_NOCLDSTOP; } else { newAction.sa_flags = 0; } sigaction(signal, &newAction, &lastMasks.action[signal]); lastMasks.enabled[signal] = 1; if (action == NX_SIGNAL_FORWARD) { lastMasks.forward[signal] = 1; } } void RestoreSignal(int signal) { if (lastMasks.enabled[signal] == 0) { #ifdef WARNING *logofs << "Loop: WARNING! Signal '" << DumpSignal(signal) << " not installed in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Signal '" << DumpSignal(signal) << " not installed in process with pid '" << getpid() << "'.\n"; return; } #ifdef TEST *logofs << "Loop: Restoring handler for signal " << signal << " '" << DumpSignal(signal) << "' in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif if (signal == SIGALRM && isTimestamp(lastTimer.start)) { ResetTimer(); } sigaction(signal, &lastMasks.action[signal], NULL); lastMasks.enabled[signal] = 0; lastMasks.forward[signal] = 0; } void HandleSignal(int signal) { if (logofs == NULL) { logofs = &cerr; } #if defined(UNSAFE) && (defined(TEST) || defined(INFO)) if (lastSignal != 0) { *logofs << "Loop: WARNING! Last signal is '" << lastSignal << "', '" << DumpSignal(signal) << "' and not zero " << "in process with pid '" << getpid() << "'.\n" << logofs_flush; } *logofs << "Loop: Signal '" << signal << "', '" << DumpSignal(signal) << "' received in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif if (getpid() != lastProxy && handler != NULL) { #if defined(UNSAFE) && (defined(TEST) || defined(INFO)) *logofs << "Loop: Calling slave handler in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif if ((*handler)(signal) == 0) { return; } } switch (signal) { case SIGUSR1: { if (proxy != NULL && lastSignal == 0) { lastSignal = SIGUSR1; } break; } case SIGUSR2: { if (proxy != NULL && lastSignal == 0) { lastSignal = SIGUSR2; } break; } case SIGPIPE: { // // It can happen that SIGPIPE is delivered // to the proxy even in the case some other // descriptor is unexpectedly closed. // // if (agentFD[1] != -1) // { // cerr << "Info" << ": Received signal 'SIGPIPE'. " // << "Closing agent conection.\n"; // // shutdown(agentFD[1], SHUT_RDWR); // } // break; } case SIGALRM: { // // Nothing to do. Just wake up the // process on blocking operations. // break; } case SIGCHLD: { // // Check if any of our children has exited. // if (HandleChildren() != 0) { signal = 0; } // // Don't save this signal or it will override // any previous signal sent by child before // exiting. // break; } #ifdef __CYGWIN32__ case 12: { // // Nothing to do. This signal is what is delivered // by the Cygwin library when trying use a shared // memory function if the daemon is not running. // #ifdef TEST *logofs << "Loop: WARNING! Received signal '12' in " << "process with pid '" << getpid() << "'.\n" << logofs_flush; *logofs << "Loop: WARNING! Please check that the " << "cygserver daemon is running.\n" << logofs_flush; #endif break; } #endif default: { // // Register the signal so we can handle it // inside the main loop. We will probably // dispose any resource and exit. // if (getpid() == lastProxy) { #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Registering end of session request " << "due to signal '" << signal << "', '" << DumpSignal(signal) << "'.\n" << logofs_flush; #endif lastSignal = signal; } else { // // This is a child, so exit immediately. // HandleCleanup(); } } } if (signal != 0 && lastMasks.forward[signal] == 1) { if (lastMasks.action[signal].sa_handler != NULL && lastMasks.action[signal].sa_handler != HandleSignal) { #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Forwarding signal '" << signal << "', '" << DumpSignal(signal) << "' to previous handler.\n" << logofs_flush; #endif lastMasks.action[signal].sa_handler(signal); } #ifdef WARNING else if (lastMasks.action[signal].sa_handler == NULL) { *logofs << "Loop: WARNING! Parent requested to forward " << "signal '" << signal << "', '" << DumpSignal(signal) << "' but didn't set a handler.\n" << logofs_flush; } #endif } } int HandleChildren() { // // Try to waitpid() for each child because the // call might have return ECHILD and so we may // have lost any of the processes. // if (IsRunning(lastDialog) && HandleChild(lastDialog) == 1) { #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Resetting pid of last dialog process " << "in handler.\n" << logofs_flush; #endif SetNotRunning(lastDialog); if (proxy != NULL) { proxy -> handleResetAlert(); } return 1; } if (IsRunning(lastWatchdog) && HandleChild(lastWatchdog) == 1) { #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Watchdog is gone. Setting the last " << "signal to SIGHUP.\n" << logofs_flush; #endif lastSignal = SIGHUP; #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Resetting pid of last watchdog process " << "in handler.\n" << logofs_flush; #endif SetNotRunning(lastWatchdog); return 1; } // // The house-keeping process exits after a // number of iterations to keep the memory // pollution low. It is restarted on demand // by the lower layers, using the callback // function. // if (IsRunning(lastKeeper) && HandleChild(lastKeeper) == 1) { #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Resetting pid of last house-keeping " << "process in handler.\n" << logofs_flush; #endif SetNotRunning(lastKeeper); return 1; } // // The pid will be checked by the code // that registered the child. // if (IsRunning(lastChild)) { #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Resetting pid of last child process " << "in handler.\n" << logofs_flush; #endif SetNotRunning(lastChild); return 1; } // // This can actually happen either because we // reset the pid of the child process as soon // as we kill it, or because of a child process // of our parent. // #if defined(UNSAFE) && (defined(TEST) || defined(INFO)) *logofs << "Loop: Ignoring signal received for the " << "unregistered child.\n" << logofs_flush; #endif return 0; } int HandleChild(int child) { int pid; int status = 0; int options = WNOHANG | WUNTRACED; while ((pid = waitpid(child, &status, options)) && pid == -1 && EGET() == EINTR); return CheckChild(pid, status); } int WaitChild(int child, const char* label, int force) { int pid; int status = 0; int options = WUNTRACED; for (;;) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Waiting for the " << label << " process '" << child << "' to die.\n" << logofs_flush; #endif pid = waitpid(child, &status, options); if (pid == -1 && EGET() == EINTR) { if (force == 0) { return 0; } #ifdef WARNING *logofs << "Loop: WARNING! Ignoring signal while " << "waiting for the " << label << " process '" << child << "' to die.\n" << logofs_flush; #endif continue; } break; } return (EGET() == ECHILD ? 1 : CheckChild(pid, status)); } int CheckChild(int pid, int status) { lastStatus = 0; if (pid > 0) { if (WIFSTOPPED(status)) { #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Child process '" << pid << "' was stopped " << "with signal " << (WSTOPSIG(status)) << ".\n" << logofs_flush; #endif return 0; } else { if (WIFEXITED(status)) { #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Child process '" << pid << "' exited " << "with status '" << (WEXITSTATUS(status)) << "'.\n" << logofs_flush; #endif lastStatus = WEXITSTATUS(status); } else if (WIFSIGNALED(status)) { if (CheckSignal(WTERMSIG(status)) != 1) { #ifdef WARNING *logofs << "Loop: WARNING! Child process '" << pid << "' died because of signal " << (WTERMSIG(status)) << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Child process '" << pid << "' died because of signal " << (WTERMSIG(status)) << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n"; } #if defined(UNSAFE) && defined(TEST) else { *logofs << "Loop: Child process '" << pid << "' died because of signal " << (WTERMSIG(status)) << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n" << logofs_flush; } #endif lastStatus = 1; } return 1; } } else if (pid < 0) { if (EGET() != ECHILD) { #ifdef PANIC *logofs << "Loop: PANIC! Call to waitpid failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to waitpid failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; HandleCleanup(); } // // This can happen when the waitpid() is // blocking, as the SIGCHLD is received // within the call. // #ifdef TEST *logofs << "Loop: No more children processes running.\n" << logofs_flush; #endif return 1; } return 0; } void RegisterChild(int child) { #if defined(TEST) || defined(INFO) if (IsNotRunning(lastChild)) { *logofs << "Loop: Registering child process '" << child << "' in process with pid '" << getpid() << "'.\n" << logofs_flush; } else { *logofs << "Loop: WARNING! Overriding registered child '" << lastChild << "' with new child '" << child << "' in process with pid '" << getpid() << "'.\n" << logofs_flush; } #endif lastChild = child; } int CheckParent(const char *name, const char *type, int parent) { if (parent != getppid() || parent == 1) { #ifdef WARNING *logofs << name << ": WARNING! Parent process appears " << "to be dead. Exiting " << type << ".\n" << logofs_flush; #endif cerr << "Warning" << ": Parent process appears " << "to be dead. Exiting " << type << ".\n"; return 0; } return 1; } void HandleTimer(int signal) { if (signal == SIGALRM) { if (isTimestamp(lastTimer.start)) { #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Timer expired at " << strMsTimestamp() << " in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif if (proxy != NULL) { proxy -> handleTimer(); } ResetTimer(); } else { #ifdef PANIC *logofs << "Loop: PANIC! Inconsistent timer state " << " in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Inconsistent timer state " << " in process with pid '" << getpid() << "'.\n"; } } else { #ifdef PANIC *logofs << "Loop: PANIC! Inconsistent signal '" << signal << "', '" << DumpSignal(signal) << "' received in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Inconsistent signal '" << signal << "', '" << DumpSignal(signal) << "' received in process with pid '" << getpid() << "'.\n"; } } void SetTimer(int value) { getNewTimestamp(); if (isTimestamp(lastTimer.start)) { int diffTs = diffTimestamp(lastTimer.start, getTimestamp()); if (diffTs > lastTimer.next.tv_usec / 1000 * 2) { #ifdef WARNING *logofs << "Loop: WARNING! Timer missed to expire at " << strMsTimestamp() << " in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Timer missed to expire at " << strMsTimestamp() << " in process with pid '" << getpid() << "'.\n"; HandleTimer(SIGALRM); } else { #ifdef TEST *logofs << "Loop: Timer already running at " << strMsTimestamp() << " in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif return; } } // // Save the former handler. // struct sigaction action; action.sa_handler = HandleTimer; #if defined(__linux__) action.sa_restorer = NULL; #endif sigemptyset(&action.sa_mask); action.sa_flags = 0; sigaction(SIGALRM, &action, &lastTimer.action); // // Start the timer. // lastTimer.next = getTimestamp(value); struct itimerval timer; timer.it_interval = lastTimer.next; timer.it_value = lastTimer.next; #ifdef TEST *logofs << "Loop: Timer set to " << lastTimer.next.tv_sec << " S and " << lastTimer.next.tv_usec / 1000 << " Ms at " << strMsTimestamp() << " in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif if (setitimer(ITIMER_REAL, &timer, &lastTimer.value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Call to setitimer failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to setitimer failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; lastTimer.next = nullTimestamp(); return; } lastTimer.start = getTimestamp(); } void ResetTimer() { if (isTimestamp(lastTimer.start) == 0) { #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Timer not running in process " << "with pid '" << getpid() << "'.\n" << logofs_flush; #endif return; } #if defined(UNSAFE) && defined(TEST) *logofs << "Loop: Timer reset at " << strMsTimestamp() << " in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif // // Restore the old signal mask and timer. // if (setitimer(ITIMER_REAL, &lastTimer.value, NULL) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Call to setitimer failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to setitimer failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; } if (sigaction(SIGALRM, &lastTimer.action, NULL) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Call to sigaction failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to sigaction failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; } lastTimer.start = lastTimer.next = nullTimestamp(); } // // Open TCP socket to listen for remote proxy and // block until remote connects. If successful close // the listening socket and return FD on which the // other party is connected. // int WaitForRemote(int portNum) { char hostLabel[DEFAULT_STRING_LENGTH] = { 0 }; int retryAccept = -1; int listenIPAddr = -1; int proxyFD = -1; int newFD = -1; // // Get IP address of host to be awaited. // int acceptIPAddr = 0; if (*acceptHost != '\0') { acceptIPAddr = GetHostAddress(acceptHost); if (acceptIPAddr == 0) { #ifdef PANIC *logofs << "Loop: PANIC! Cannot accept connections from unknown host '" << acceptHost << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot accept connections from unknown host '" << acceptHost << "'.\n"; goto WaitForRemoteError; } } proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); if (proxyFD == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to socket failed for TCP socket. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to socket failed for TCP socket. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; goto WaitForRemoteError; } else if (SetReuseAddress(proxyFD) < 0) { goto WaitForRemoteError; } listenIPAddr = 0; ParseListenOption(listenIPAddr); sockaddr_in tcpAddr; tcpAddr.sin_family = AF_INET; tcpAddr.sin_port = htons(portNum); // // Quick patch to run on MacOS/X where inet_addr("127.0.0.1") // alone seems to fail to return a valid interface. It probably // just needs a htonl() or something like that. // // TODO: We have to give another look at inet_addr("127.0.0.1") // on the Mac. // #ifdef __APPLE__ tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY); #else tcpAddr.sin_addr.s_addr = listenIPAddr; #endif if (bind(proxyFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to bind failed for TCP port " << portNum << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to bind failed for TCP port " << portNum << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; goto WaitForRemoteError; } if (listen(proxyFD, 4) == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to listen failed for TCP port " << portNum << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to listen failed for TCP port " << portNum << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; goto WaitForRemoteError; } if (*acceptHost != '\0') { strcat(hostLabel, "'"); strcat(hostLabel, acceptHost); strcat(hostLabel, "'"); } else { strcpy(hostLabel, "any host"); } #ifdef TEST *logofs << "Loop: Waiting for connection from " << hostLabel << " on port '" << portNum << "'.\n" << logofs_flush; #endif cerr << "Info" << ": Waiting for connection from " << hostLabel << " on port '" << portNum << "'.\n"; // // How many times to loop waiting for connections // from the selected host? Each loop wait for at // most 20 seconds so a default value of 3 gives // a timeout of 1 minute. // // TODO: Handling of timeouts and retry attempts // must be rewritten. // retryAccept = control -> OptionProxyRetryAccept; for (;;) { fd_set readSet; FD_ZERO(&readSet); FD_SET(proxyFD, &readSet); T_timestamp selectTs; selectTs.tv_sec = 20; selectTs.tv_usec = 0; int result = select(proxyFD + 1, &readSet, NULL, NULL, &selectTs); getNewTimestamp(); if (result == -1) { if (EGET() == EINTR) { if (CheckAbort() != 0) { goto WaitForRemoteError; } continue; } #ifdef PANIC *logofs << "Loop: PANIC! Call to select failed. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to select failed. Error is " << EGET() << " '" << ESTR() << "'.\n"; goto WaitForRemoteError; } else if (result > 0 && FD_ISSET(proxyFD, &readSet)) { sockaddr_in newAddr; size_t addrLen = sizeof(sockaddr_in); newFD = accept(proxyFD, (sockaddr *) &newAddr, (socklen_t *) &addrLen); if (newFD == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to accept failed. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to accept failed. Error is " << EGET() << " '" << ESTR() << "'.\n"; goto WaitForRemoteError; } char *connectedHost = inet_ntoa(newAddr.sin_addr); if (*acceptHost == '\0' || (int) newAddr.sin_addr.s_addr == acceptIPAddr) { #ifdef TEST unsigned int connectedPort = ntohs(newAddr.sin_port); *logofs << "Loop: Accepted connection from '" << connectedHost << "' with port '" << connectedPort << "'.\n" << logofs_flush; #endif cerr << "Info" << ": Accepted connection from '" << connectedHost << "'.\n"; break; } else { #ifdef PANIC *logofs << "Loop: WARNING! Refusing connection from '" << connectedHost << "' on port '" << portNum << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Refusing connection from '" << connectedHost << "'.\n"; } // // Not the best way to elude a DOS attack... // sleep(5); close(newFD); } if (--retryAccept == 0) { if (*acceptHost == '\0') { #ifdef PANIC *logofs << "Loop: PANIC! Connection with remote host " << "could not be established.\n" << logofs_flush; #endif cerr << "Error" << ": Connection with remote host " << "could not be established.\n"; } else { #ifdef PANIC *logofs << "Loop: PANIC! Connection with remote host '" << acceptHost << "' could not be established.\n" << logofs_flush; #endif cerr << "Error" << ": Connection with remote host '" << acceptHost << "' could not be established.\n"; } goto WaitForRemoteError; } else { handleCheckSessionInConnect(); } } close(proxyFD); return newFD; WaitForRemoteError: close(proxyFD); HandleCleanup(); } // // Connect to remote proxy. If successful // return FD of connection, else return -1. // int ConnectToRemote(const char *const hostName, int portNum) { int proxyFD = -1; int remoteIPAddr = GetHostAddress(hostName); if (remoteIPAddr == 0) { #ifdef PANIC *logofs << "Loop: PANIC! Unknown remote host '" << hostName << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Unknown remote host '" << hostName << "'.\n"; HandleCleanup(); } #ifdef TEST *logofs << "Loop: Connecting to remote host '" << hostName << ":" << portNum << "'.\n" << logofs_flush; #endif cerr << "Info" << ": Connecting to remote host '" << hostName << ":" << portNum << "'.\n" << logofs_flush; // // How many times we retry to connect to remote // host in case of failure? // int retryConnect = control -> OptionProxyRetryConnect; // // Show an alert after 20 seconds and use the // same timeout to interrupt the connect. The // retry timeout is incremental, starting from // 100 miliseconds up to 1 second. // int alertTimeout = 20000; int connectTimeout = 20000; int retryTimeout = 100; T_timestamp lastRetry = getNewTimestamp(); sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(portNum); addr.sin_addr.s_addr = remoteIPAddr; for (;;) { proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); if (proxyFD == -1) { #ifdef PANIC *logofs << "Loop: PANIC! Call to socket failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Call to socket failed. " << "Error is " << EGET() << " '" << ESTR() << "'.\n"; goto ConnectToRemoteError; } else if (SetReuseAddress(proxyFD) < 0) { goto ConnectToRemoteError; } // // Ensure operation is timed out // if there is a network problem. // #ifdef DEBUG *logofs << "Loop: Timer set to " << connectTimeout / 1000 << " S " << "with retry set to " << retryConnect << " in process with pid '" << getpid() << "'.\n" << logofs_flush; #endif SetTimer(connectTimeout); int result = connect(proxyFD, (sockaddr *) &addr, sizeof(sockaddr_in)); int reason = EGET(); ResetTimer(); if (result < 0) { close(proxyFD); if (CheckAbort() != 0) { goto ConnectToRemoteError; } else if (--retryConnect == 0) { ESET(reason); #ifdef PANIC *logofs << "Loop: PANIC! Connection to '" << hostName << ":" << portNum << "' failed. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Connection to '" << hostName << ":" << portNum << "' failed. Error is " << EGET() << " '" << ESTR() << "'.\n"; goto ConnectToRemoteError; } else { #ifdef TEST *logofs << "Loop: Sleeping " << retryTimeout << " Ms before retrying.\n" << logofs_flush; #endif usleep(retryTimeout * 1000); retryTimeout <<= 1; if (retryTimeout > 1000 * 1000) { retryTimeout = 1000 * 1000; } } // // Check if it is time to show an alert dialog. // if (diffTimestamp(lastRetry, getNewTimestamp()) >= (alertTimeout - control -> LatencyTimeout)) { if (IsNotRunning(lastDialog)) { handleCheckSessionInConnect(); // // Wait for the dialog process to die // unless a signal is received. // while (IsRunning(lastDialog)) { WaitChild(lastDialog, "dialog", 0); if (CheckAbort() != 0) { // // The client ignores the TERM signal // on Windows. // #ifdef __CYGWIN32__ KillProcess(lastDialog, "dialog", SIGKILL, 1); #else KillProcess(lastDialog, "dialog", SIGTERM, 1); #endif goto ConnectToRemoteError; } } lastRetry = getTimestamp(); } } #ifdef TEST { *logofs << "Loop: Not showing the dialog with " << (diffTimestamp(lastRetry, getTimestamp()) / 1000) << " seconds elapsed.\n" << logofs_flush; } #endif ESET(reason); #ifdef TEST *logofs << "Loop: Connection to '" << hostName << ":" << portNum << "' failed with error '" << ESTR() << "'. Retrying.\n" << logofs_flush; #endif } else { // // Connection was successful. // break; } } return proxyFD; ConnectToRemoteError: if (proxyFD != -1) { close(proxyFD); } HandleCleanup(); } // // Make a string of options for the remote // proxy and write it to the descriptor. // The string includes the local version. // int SendProxyOptions(int fd) { char options[DEFAULT_REMOTE_OPTIONS_LENGTH]; // // Send the "compatibility" version first, then our // actual version. Old proxies will take the first // value and ignore the second. // sprintf(options, "NXPROXY-1.5.0-%i.%i.%i", control -> LocalVersionMajor, control -> LocalVersionMinor, control -> LocalVersionPatch); // // If you want to send options from proxy // initiating the connection use something // like this: // // if (WE_PROVIDE_CREDENTIALS) // { // sprintf(options + strlen(options), "%s=%s", option, value); // } // // If you want to send options according to // local proxy mode use something like this: // // if (control -> ProxyMode == proxy_client) // { // sprintf(options + strlen(options), "%s=%s", option, value); // } // // // Send the authorization cookie if any. We assume // user can choose to not provide any auth cookie // and allow any connection to be accepted. // if (WE_PROVIDE_CREDENTIALS && *authCookie != '\0') { sprintf(options + strlen(options), " cookie=%s,", authCookie); } else { sprintf(options + strlen(options), " "); } // // Now link characteristics and compression // options. Delta compression, as well as // preferred pack method, are imposed by // client proxy. // if (control -> ProxyMode == proxy_client) { sprintf(options + strlen(options), "link=%s,pack=%s,cache=%s,", linkSpeedName, packMethodName, cacheSizeName); if (*bitrateLimitName != '\0') { sprintf(options + strlen(options), "limit=%s,", bitrateLimitName); } // // Let the user disable the render extension // and let the X client proxy know if it can // short-circuit the X replies. Also pass // along the session type to ensure that the // remote proxy gets the right value. // sprintf(options + strlen(options), "render=%d,taint=%d,", (control -> HideRender == 0), control -> TaintReplies); if (*sessionType != '\0') { sprintf(options + strlen(options), "type=%s,", sessionType); } else { sprintf(options + strlen(options), "type=default,"); } // // Add the 'strict' option, if needed. // if (control -> isProtoStep7() == 1 && useStrict != -1) { sprintf(options + strlen(options), "strict=%d,", useStrict); } // // Tell the remote the size of the shared // memory segment. // if (control -> isProtoStep7() == 1 && *shsegSizeName != '\0') { sprintf(options + strlen(options), "shseg=%s,", shsegSizeName); } // // Send image cache parameters. // sprintf(options + strlen(options), "images=%s,", imagesSizeName); sprintf(options + strlen(options), "delta=%d,stream=%d,data=%d ", control -> LocalDeltaCompression, control -> LocalStreamCompressionLevel, control -> LocalDataCompressionLevel); } else { // // If no special compression level was selected, // server side will use compression levels set // by client. // if (control -> LocalStreamCompressionLevel < 0) { sprintf(options + strlen(options), "stream=default,"); } else { sprintf(options + strlen(options), "stream=%d,", control -> LocalStreamCompressionLevel); } if (control -> LocalDataCompressionLevel < 0) { sprintf(options + strlen(options), "data=default "); } else { sprintf(options + strlen(options), "data=%d ", control -> LocalDataCompressionLevel); } } #ifdef TEST *logofs << "Loop: Sending remote options '" << options << "'.\n" << logofs_flush; #endif return WriteLocalData(fd, options, strlen(options)); } int ReadProxyVersion(int fd) { #ifdef TEST *logofs << "Loop: Going to read the remote proxy version " << "from FD#" << fd << ".\n" << logofs_flush; #endif // // Read until the first space in string. // We expect the remote version number. // char options[DEFAULT_REMOTE_OPTIONS_LENGTH]; int result = ReadRemoteData(fd, options, sizeof(options), ' '); if (result <= 0) { if (result < 0) { if (control -> ProxyMode == proxy_server) { HandleAlert(ABORT_PROXY_NEGOTIATION_ALERT, 1); } handleAlertInLoop(); } return result; } #ifdef TEST *logofs << "Loop: Received remote version string '" << options << "' from FD#" << fd << ".\n" << logofs_flush; #endif if (strncmp(options, "NXPROXY-", strlen("NXPROXY-")) != 0) { #ifdef PANIC *logofs << "Loop: PANIC! Parse error in remote options string '" << options << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Parse error in remote options string '" << options << "'.\n"; return -1; } // // Try to determine if this is a pre-2.0.0 // version advertising itself as compatible // with the 1.2.2. // int major = -1; int minor = -1; int patch = -1; sscanf(options, "NXPROXY-%i.%i.%i-%i.%i.%i", &(control -> RemoteVersionMajor), &(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch), &major, &minor, &patch); if (control -> RemoteVersionMajor == 1 && control -> RemoteVersionMinor == 2 && control -> RemoteVersionPatch == 2 && major != -1 && minor != -1 && patch != -1) { #ifdef TEST *logofs << "Loop: Read trailing remote version '" << major << "." << minor << "." << patch << "'.\n" << logofs_flush; #endif control -> CompatVersionMajor = major; control -> CompatVersionMinor = minor; control -> CompatVersionPatch = patch; control -> RemoteVersionMajor = major; control -> RemoteVersionMinor = minor; control -> RemoteVersionPatch = patch; } else { // // We read the remote version at the first // round. If the second version is missing, // we will retain the values read before. // sscanf(options, "NXPROXY-%i.%i.%i-%i.%i.%i", &(control -> CompatVersionMajor), &(control -> CompatVersionMinor), &(control -> CompatVersionPatch), &(control -> RemoteVersionMajor), &(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch)); } #ifdef TEST *logofs << "Loop: Identified remote version '" << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch << "'.\n" << logofs_flush; *logofs << "Loop: Remote compatibility version '" << control -> CompatVersionMajor << "." << control -> CompatVersionMinor << "." << control -> CompatVersionPatch << "'.\n" << logofs_flush; *logofs << "Loop: Local version '" << control -> LocalVersionMajor << "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch << "'.\n" << logofs_flush; #endif if (SetVersion() < 0) { if (control -> ProxyMode == proxy_server) { HandleAlert(WRONG_PROXY_VERSION_ALERT, 1); } handleAlertInLoop(); return -1; } return 1; } int ReadProxyOptions(int fd) { #ifdef TEST *logofs << "Loop: Going to read the remote proxy options " << "from FD#" << fd << ".\n" << logofs_flush; #endif char options[DEFAULT_REMOTE_OPTIONS_LENGTH]; int result = ReadRemoteData(fd, options, sizeof(options), ' '); if (result <= 0) { return result; } #ifdef TEST *logofs << "Loop: Received remote options string '" << options << "' from FD#" << fd << ".\n" << logofs_flush; #endif // // Get the remote options, delimited by a space character. // Note that there will be a further initialization phase // at the time proxies negotiate cache file to restore. // if (ParseRemoteOptions(options) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Couldn't negotiate a valid " << "session with remote NX proxy.\n" << logofs_flush; #endif cerr << "Error" << ": Couldn't negotiate a valid " << "session with remote NX proxy.\n"; return -1; } return 1; } int SendProxyCaches(int fd) { #ifdef TEST *logofs << "Loop: Synchronizing local and remote caches.\n" << logofs_flush; #endif if (control -> ProxyMode == proxy_client) { // // Prepare a list of caches matching this // session type and send it to the remote. // #ifdef TEST *logofs << "Loop: Going to send the list of local caches.\n" << logofs_flush; #endif SetCaches(); int entries = DEFAULT_REMOTE_CACHE_ENTRIES; const char prefix = 'C'; if (control -> LocalDeltaCompression == 0 || control -> PersistentCacheEnableLoad == 0) { #ifdef TEST *logofs << "Loop: Writing an empty list to FD#" << fd << ".\n" << logofs_flush; #endif return WriteLocalData(fd, "cachelist=none ", strlen("cachelist=none ")); } int count = 0; #ifdef TEST *logofs << "Loop: Looking for cache files in directory '" << control -> PersistentCachePath << "'.\n" << logofs_flush; #endif DIR *cacheDir = opendir(control -> PersistentCachePath); if (cacheDir != NULL) { dirent *dirEntry; int prologue = 0; while (((dirEntry = readdir(cacheDir)) != NULL) && (count < entries)) { if (*dirEntry -> d_name == prefix && strlen(dirEntry -> d_name) == (MD5_LENGTH * 2 + 2)) { if (prologue == 0) { WriteLocalData(fd, "cachelist=", strlen("cachelist=")); prologue = 1; } else { WriteLocalData(fd, ",", strlen(",")); } #ifdef TEST *logofs << "Loop: Writing entry '" << control -> PersistentCachePath << "/" << dirEntry -> d_name << "' to FD#" << fd << ".\n" << logofs_flush; #endif // // Write cache file name to the socket, // including leading 'C-' or 'S-'. // WriteLocalData(fd, dirEntry -> d_name, MD5_LENGTH * 2 + 2); count++; } } closedir(cacheDir); } if (count == 0) { #ifdef TEST *logofs << "Loop: Writing an empty list to FD#" << fd << ".\n" << logofs_flush; #endif return WriteLocalData(fd, "cachelist=none ", strlen("cachelist=none ")); } else { return WriteLocalData(fd, " ", 1); } } else { // // Send back the selected cache name. // #ifdef TEST *logofs << "Loop: Going to send the selected cache.\n" << logofs_flush; #endif char buffer[DEFAULT_STRING_LENGTH]; if (control -> PersistentCacheName != NULL) { #ifdef TEST *logofs << "Loop: Name of selected cache file is '" << control -> PersistentCacheName << "'.\n" << logofs_flush; #endif sprintf(buffer, "cachefile=%s%s ", *(control -> PersistentCacheName) == 'C' ? "S-" : "C-", control -> PersistentCacheName + 2); } else { #ifdef TEST *logofs << "Loop: No valid cache file was selected.\n" << logofs_flush; #endif sprintf(buffer, "cachefile=none "); } #ifdef TEST *logofs << "Loop: Sending string '" << buffer << "' as selected cache file.\n" << logofs_flush; #endif return WriteLocalData(fd, buffer, strlen(buffer)); } } int ReadProxyCaches(int fd) { if (control -> ProxyMode == proxy_client) { #ifdef TEST *logofs << "Loop: Going to receive the selected proxy cache.\n" << logofs_flush; #endif // // We will read the name of cache plus the stop character. // char buffer[DEFAULT_STRING_LENGTH]; // // Leave space for a trailing null. // int result = ReadRemoteData(fd, buffer, sizeof("cachefile=") + MD5_LENGTH * 2 + 3, ' '); if (result <= 0) { return result; } char *cacheName = strstr(buffer, "cachefile="); if (cacheName == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid cache file option '" << buffer << "' provided by remote proxy.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid cache file option '" << buffer << "' provided by remote proxy.\n"; HandleCleanup(); } cacheName += strlen("cachefile="); if (control -> PersistentCacheName != NULL) { delete [] control -> PersistentCacheName; } control -> PersistentCacheName = NULL; if (strncasecmp(cacheName, "none", strlen("none")) == 0) { #ifdef TEST *logofs << "Loop: No cache file selected by remote proxy.\n" << logofs_flush; #endif } else if (strlen(cacheName) != MD5_LENGTH * 2 + 3 || *(cacheName + MD5_LENGTH * 2 + 2) != ' ') { #ifdef PANIC *logofs << "Loop: PANIC! Invalid cache file name '" << cacheName << "' provided by remote proxy.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid cache file name '" << cacheName << "' provided by remote proxy.\n"; HandleCleanup(); } else { // // It is "C-" + 32 + "\0". // control -> PersistentCacheName = new char[MD5_LENGTH * 2 + 3]; *(cacheName + MD5_LENGTH * 2 + 2) = '\0'; strcpy(control -> PersistentCacheName, cacheName); #ifdef TEST *logofs << "Loop: Cache file '" << control -> PersistentCacheName << "' selected by remote proxy.\n" << logofs_flush; #endif } } else { #ifdef TEST *logofs << "Loop: Going to receive the list of remote caches.\n" << logofs_flush; #endif SetCaches(); int size = ((MD5_LENGTH * 2 + 2) + strlen(",")) * DEFAULT_REMOTE_CACHE_ENTRIES + strlen("cachelist=") + strlen(" ") + 1; char *buffer = new char[size]; int result = ReadRemoteData(fd, buffer, size - 1, ' '); if (result <= 0) { delete [] buffer; return result; } #ifdef TEST *logofs << "Loop: Read list of caches from remote side as '" << buffer << "'.\n" << logofs_flush; #endif // // Prepare the buffer. What we want is a list // like "cache1,cache2,cache2" terminated by // null. // *(buffer + strlen(buffer) - 1) = '\0'; if (strncasecmp(buffer, "cachelist=", strlen("cachelist=")) != 0) { #ifdef PANIC *logofs << "Loop: Wrong format for list of cache files " << "read from FD#" << fd << ".\n" << logofs_flush; #endif cerr << "Error" << ": Wrong format for list of cache files.\n"; delete [] buffer; return -1; } control -> PersistentCacheName = GetLastCache(buffer, control -> PersistentCachePath); // // Get rid of list of caches. // delete [] buffer; } return 1; } int ReadForwarderVersion(int fd) { #ifdef TEST *logofs << "Loop: Going to negotiate the forwarder version.\n" << logofs_flush; #endif // // Check if we actually expect the session cookie. // if (*authCookie == '\0') { #ifdef TEST *logofs << "Loop: No authentication cookie required " << "from FD#" << fd << ".\n" << logofs_flush; #endif return 1; } char options[DEFAULT_REMOTE_OPTIONS_LENGTH]; int result = ReadRemoteData(fd, options, sizeof(options), ' '); if (result <= 0) { return result; } #ifdef TEST *logofs << "Loop: Received forwarder version string '" << options << "' from FD#" << fd << ".\n" << logofs_flush; #endif if (strncmp(options, "NXSSH-", strlen("NXSSH-")) != 0) { #ifdef PANIC *logofs << "Loop: PANIC! Parse error in forwarder options string '" << options << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Parse error in forwarder options string '" << options << "'.\n"; return -1; } // // Accept whatever forwarder version. // sscanf(options, "NXSSH-%i.%i.%i", &(control -> RemoteVersionMajor), &(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch)); #ifdef TEST *logofs << "Loop: Read forwarder version '" << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch << "'.\n" << logofs_flush; #endif return 1; } int ReadForwarderOptions(int fd) { // // Get the forwarder cookie. // if (*authCookie == '\0') { #ifdef TEST *logofs << "Loop: No authentication cookie required " << "from FD#" << fd << ".\n" << logofs_flush; #endif return 1; } char options[DEFAULT_REMOTE_OPTIONS_LENGTH]; int result = ReadRemoteData(fd, options, sizeof(options), ' '); if (result <= 0) { return result; } #ifdef TEST *logofs << "Loop: Received forwarder options string '" << options << "' from FD#" << fd << ".\n" << logofs_flush; #endif if (ParseForwarderOptions(options) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Couldn't negotiate a valid " << "cookie with the NX forwarder.\n" << logofs_flush; #endif cerr << "Error" << ": Couldn't negotiate a valid " << "cookie with the NX forwarder.\n"; return -1; } return 1; } int ReadRemoteData(int fd, char *buffer, int size, char stop) { #ifdef TEST *logofs << "Loop: Going to read remote data from FD#" << fd << ".\n" << logofs_flush; #endif if (size >= MAXIMUM_REMOTE_OPTIONS_LENGTH) { #ifdef PANIC *logofs << "Loop: PANIC! Maximum remote options buffer " << "limit exceeded.\n" << logofs_flush; #endif cerr << "Error" << ": Maximum remote options buffer " << "limit exceeded.\n"; HandleCleanup(); } while (remotePosition < (size - 1)) { int result = read(fd, remoteData + remotePosition, 1); getNewTimestamp(); if (result <= 0) { if (result == -1) { if (EGET() == EAGAIN) { #ifdef TEST *logofs << "Loop: Reading data from FD#" << fd << " would block.\n" << logofs_flush; #endif return 0; } else if (EGET() == EINTR) { if (CheckAbort() != 0) { return -1; } continue; } } #ifdef PANIC *logofs << "Loop: PANIC! The remote NX proxy closed " << "the connection.\n" << logofs_flush; #endif cerr << "Error" << ": The remote NX proxy closed " << "the connection.\n"; return -1; } else if (*(remoteData + remotePosition) == stop) { #ifdef TEST *logofs << "Loop: Read stop character from FD#" << fd << ".\n" << logofs_flush; #endif remotePosition++; // // Copy the fake terminating null // in the buffer. // *(remoteData + remotePosition) = '\0'; memcpy(buffer, remoteData, remotePosition + 1); #ifdef TEST *logofs << "Loop: Remote string '" << remoteData << "' read from FD#" << fd << ".\n" << logofs_flush; #endif int t = remotePosition; remotePosition = 0; return t; } else { // // Make sure string received // from far end is printable. // if (isgraph(*(remoteData + remotePosition)) == 0) { #ifdef WARNING *logofs << "Loop: WARNING! Non printable character decimal '" << (unsigned int) *(remoteData + remotePosition) << "' received in remote data from FD#" << fd << ".\n" << logofs_flush; #endif cerr << "Warning" << ": Non printable character decimal '" << (unsigned int) *(remoteData + remotePosition) << "' received in remote data from FD#" << fd << ".\n" << logofs_flush; *(remoteData + remotePosition) = ' '; } #ifdef DEBUG *logofs << "Loop: Read a further character " << "from FD#" << fd << ".\n" << logofs_flush; #endif remotePosition++; } } *(remoteData + remotePosition) = '\0'; #ifdef PANIC *logofs << "Loop: PANIC! Stop character missing " << "from FD#" << fd << " after " << remotePosition << " characters read in string '" << remoteData << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Stop character missing " << "from FD#" << fd << " after " << remotePosition << " characters read in string '" << remoteData << "'.\n"; memcpy(buffer, remoteData, remotePosition); remotePosition = 0; return -1; } int WriteLocalData(int fd, const char *buffer, int size) { int position = 0; while (position < size) { int result = write(fd, buffer + position, size - position); getNewTimestamp(); if (result <= 0) { if (result < 0 && EGET() == EINTR) { continue; } #ifdef TEST *logofs << "Loop: Error writing data to FD#" << fd << ".\n" << logofs_flush; #endif return -1; } position += result; } return position; } // // Parse the string passed by calling process in // the environment. This is not necessarily the // content of DISPLAY variable, but can be the // parameters passed when creating the process // or thread. // int ParseEnvironmentOptions(const char *env, int force) { // // Be sure log file is valid. // if (logofs == NULL) { logofs = &cerr; } // // Be sure we have a parameters repository // and a context to jump into because this // can be called before creating the proxy. // if (control == NULL) { control = new Control(); } if (setjmp(context) == 1) { #ifdef TEST *logofs << "Loop: Out of the long jump while parsing " << "the environment options.\n" << logofs_flush; #endif return -1; } if (force == 0 && parsedOptions == 1) { #ifdef TEST *logofs << "Loop: Skipping a further parse of environment " << "options string '" << (env != NULL ? env : "") << "'.\n" << logofs_flush; #endif return 1; } if (env == NULL || *env == '\0') { #ifdef TEST *logofs << "Loop: Nothing to do with empty environment " << "options string '" << (env != NULL ? env : "") << "'.\n" << logofs_flush; #endif return 0; } #ifdef TEST *logofs << "Loop: Going to parse the environment options " << "string '" << env << "'.\n" << logofs_flush; #endif parsedOptions = 1; // // Copy the string passed as parameter // because we need to modify it. // char opts[DEFAULT_DISPLAY_OPTIONS_LENGTH]; #ifdef VALGRIND memset(opts, '\0', DEFAULT_DISPLAY_OPTIONS_LENGTH); #endif if (strlen(env) >= DEFAULT_DISPLAY_OPTIONS_LENGTH) { #ifdef PANIC *logofs << "Loop: PANIC! Environment options string '" << env << "' exceeds length of " << DEFAULT_DISPLAY_OPTIONS_LENGTH << " characters.\n" << logofs_flush; #endif cerr << "Error" << ": Environment options string '" << env << "' exceeds length of " << DEFAULT_DISPLAY_OPTIONS_LENGTH << " characters.\n"; return -1; } strcpy(opts, env); char *nextOpts = opts; // // Ensure that DISPLAY environment variable // (roughly) follows the X convention for // transport notation. // if (strncasecmp(opts, "nx/nx,:", 7) == 0 || strncasecmp(opts, "nx,:", 4) == 0) { #ifdef PANIC *logofs << "Loop: PANIC! Parse error in options string '" << opts << "' at 'nx,:'.\n" << logofs_flush; #endif cerr << "Error" << ": Parse error in options string '" << opts << "' at 'nx,:'.\n"; return -1; } else if (strncasecmp(opts, "nx/nx,", 6) == 0) { nextOpts += 6; } else if (strncasecmp(opts, "nx,", 3) == 0) { nextOpts += 3; } else if (strncasecmp(opts, "nx:", 3) == 0) { nextOpts += 3; } else if (force == 0) { #ifdef TEST *logofs << "Loop: Ignoring host X server display string '" << opts << "'.\n" << logofs_flush; #endif return 0; } // // Save here the name of the options file and // parse it after all the other options. // char fileOptions[DEFAULT_STRING_LENGTH] = { 0 }; // // The options string is intended to be a series // of name/value tuples in the form name=value // separated by the ',' character ended by a ':' // followed by remote NX proxy port. // char *name; char *value; value = rindex(nextOpts, ':'); if (value != NULL) { char *check = value + 1; if (*check == '\0' || isdigit(*check) == 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify NX port in string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify NX port in string '" << value << "'.\n"; return -1; } proxyPort = atoi(check); // // Get rid of the port specification. // *value = '\0'; } else if (proxyPort == DEFAULT_NX_PROXY_PORT && force == 0) { // // Complain only if user didn't specify // the port on the command line. // #ifdef PANIC *logofs << "Loop: PANIC! Can't identify NX port in string '" << opts << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify NX port in string '" << opts << "'.\n"; return -1; } #ifdef TEST *logofs << "Loop: Parsing options string '" << nextOpts << "'.\n" << logofs_flush; #endif // // Now all the other optional parameters. // name = strtok(nextOpts, "="); while (name) { value = strtok(NULL, ","); if (CheckArg("environment", name, value) < 0) { return -1; } if (strcasecmp(name, "options") == 0) { strncpy(fileOptions, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "display") == 0) { strncpy(displayHost, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "link") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else if (ParseLinkOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify 'link' option in string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify 'link' option in string '" << value << "'.\n"; return -1; } } else if (strcasecmp(name, "limit") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else if (ParseBitrateOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify option 'limit' in string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify option 'limit' in string '" << value << "'.\n"; return -1; } } else if (strcasecmp(name, "type") == 0) { // // Type of session, for example "desktop", // "application", "windows", etc. // if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else { if (strcasecmp(value, "default") == 0) { *sessionType = '\0'; } else { strncpy(sessionType, value, DEFAULT_STRING_LENGTH - 1); } } } else if (strcasecmp(name, "listen") == 0) { if (*connectHost != '\0') { #ifdef PANIC *logofs << "Loop: PANIC! Can't handle 'listen' and 'connect' parameters " << "at the same time.\n" << logofs_flush; *logofs << "Loop: PANIC! Refusing 'listen' parameter with 'connect' being '" << connectHost << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't handle 'listen' and 'connect' parameters " << "at the same time.\n"; cerr << "Error" << ": Refusing 'listen' parameter with 'connect' being '" << connectHost << "'.\n"; return -1; } listenPort = ValidateArg("local", name, value); } else if (strcasecmp(name, "accept") == 0) { if (*connectHost != '\0') { #ifdef PANIC *logofs << "Loop: PANIC! Can't handle 'accept' and 'connect' parameters " << "at the same time.\n" << logofs_flush; *logofs << "Loop: PANIC! Refusing 'accept' parameter with 'connect' being '" << connectHost << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't handle 'accept' and 'connect' parameters " << "at the same time.\n"; cerr << "Error" << ": Refusing 'accept' parameter with 'connect' being '" << connectHost << "'.\n"; return -1; } strncpy(acceptHost, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "connect") == 0) { if (*acceptHost != '\0') { #ifdef PANIC *logofs << "Loop: PANIC! Can't handle 'connect' and 'accept' parameters " << "at the same time.\n" << logofs_flush; *logofs << "Loop: PANIC! Refusing 'connect' parameter with 'accept' being '" << acceptHost << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't handle 'connect' and 'accept' parameters " << "at the same time.\n"; cerr << "Error" << ": Refusing 'connect' parameter with 'accept' being '" << acceptHost << "'.\n"; return -1; } strncpy(connectHost, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "port") == 0) { connectPort = ValidateArg("local", name, value); } else if (strcasecmp(name, "retry") == 0) { control -> OptionProxyRetryConnect = ValidateArg("local", name, value); control -> OptionServerRetryConnect = ValidateArg("local", name, value); } else if (strcasecmp(name, "session") == 0) { strncpy(sessionFileName, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "errors") == 0) { // // The old name of the parameter was 'log' // but the default name for the file is // 'errors' so it is more logical to use // the same name. // strncpy(errorsFileName, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "root") == 0) { strncpy(rootDir, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "id") == 0) { strncpy(sessionId, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "stats") == 0) { control -> EnableStatistics = 1; strncpy(statsFileName, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "cookie") == 0) { LowercaseArg("local", name, value); strncpy(authCookie, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "nodelay") == 0) { useNoDelay = ValidateArg("local", name, value); } else if (strcasecmp(name, "policy") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else { usePolicy = ValidateArg("local", name, value); } } else if (strcasecmp(name, "render") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else { useRender = ValidateArg("local", name, value); } } else if (strcasecmp(name, "taint") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else { useTaint = ValidateArg("local", name, value); } } else if (strcasecmp(name, "delta") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else { control -> LocalDeltaCompression = ValidateArg("local", name, value); } } else if (strcasecmp(name, "data") == 0) { control -> LocalDataCompressionLevel = ValidateArg("local", name, value); if (control -> LocalDataCompressionLevel == 0) { control -> LocalDataCompression = 0; } else { control -> LocalDataCompression = 1; } } else if (strcasecmp(name, "stream") == 0) { control -> LocalStreamCompressionLevel = ValidateArg("local", name, value); if (control -> LocalStreamCompressionLevel == 0) { control -> LocalStreamCompression = 0; } else { control -> LocalStreamCompression = 1; } } else if (strcasecmp(name, "memory") == 0) { control -> LocalMemoryLevel = ValidateArg("local", name, value); } else if (strcasecmp(name, "cache") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else if (ParseCacheOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify cache size for string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify cache size for string '" << value << "'.\n"; return -1; } } else if (strcasecmp(name, "images") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else if (ParseImagesOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify images cache size for string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify images cache size for string '" << value << "'.\n"; return -1; } } else if (strcasecmp(name, "shseg") == 0) { // // The 'shmem' option is used by the agent, together // with 'shpix' literal. We make the 'shseg' option // specific to the proxy and use it to determine the // size of the shared memory segment, or otherwise 0, // if the use of the shared memory extension should // not be enabled on the real X server. // if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else if (ParseShmemOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify size of shared memory " << "segment in string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify size of shared memory " << "segment in string '" << value << "'.\n"; return -1; } } else if (strcasecmp(name, "load") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else { control -> PersistentCacheEnableLoad = ValidateArg("local", name, value); if (control -> PersistentCacheEnableLoad > 0) { control -> PersistentCacheEnableLoad = 1; } else { if (control -> PersistentCacheName != NULL) { delete [] control -> PersistentCacheName; } control -> PersistentCacheName = NULL; control -> PersistentCacheEnableLoad = 0; } } } else if (strcasecmp(name, "save") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else { control -> PersistentCacheEnableSave = ValidateArg("local", name, value); if (control -> PersistentCacheEnableSave > 0) { control -> PersistentCacheEnableSave = 1; } else { if (control -> PersistentCacheName != NULL) { delete [] control -> PersistentCacheName; } control -> PersistentCacheName = NULL; control -> PersistentCacheEnableSave = 0; } } } else if (strcasecmp(name, "cups") == 0) { cupsPort = ValidateArg("local", name, value); } else if (strcasecmp(name, "sync") == 0) { #ifdef WARNING *logofs << "Loop: WARNING! No 'sync' channel in current version. " << "Assuming 'cups' channel.\n" << logofs_flush; #endif cerr << "Warning" << ": No 'sync' channel in current version. " << "Assuming 'cups' channel.\n"; cupsPort = ValidateArg("local", name, value); } else if (strcasecmp(name, "keybd") == 0 || strcasecmp(name, "aux") == 0) { auxPort = ValidateArg("local", name, value); } else if (strcasecmp(name, "samba") == 0 || strcasecmp(name, "smb") == 0) { smbPort = ValidateArg("local", name, value); } else if (strcasecmp(name, "media") == 0) { mediaPort = ValidateArg("local", name, value); } else if (strcasecmp(name, "http") == 0) { httpPort = ValidateArg("local", name, value); } else if (strcasecmp(name, "font") == 0) { strncpy(fontPort, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "slave") == 0) { slavePort = ValidateArg("local", name, value); } else if (strcasecmp(name, "mask") == 0) { control -> ChannelMask = ValidateArg("local", name, value); } else if (strcasecmp(name, "timeout") == 0) { int timeout = ValidateArg("local", name, value); if (timeout == 0) { #ifdef TEST *logofs << "Loop: Disabling timeout on broken " << "proxy connection.\n" << logofs_flush; #endif control -> ProxyTimeout = 0; } else { control -> ProxyTimeout = timeout * 1000; } } else if (strcasecmp(name, "cleanup") == 0) { int cleanup = ValidateArg("local", name, value); if (cleanup == 0) { #ifdef TEST *logofs << "Loop: Disabling grace timeout on " << "proxy shutdown.\n" << logofs_flush; #endif control -> CleanupTimeout = 0; } else { control -> CleanupTimeout = cleanup * 1000; } } else if (strcasecmp(name, "pack") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else if (ParsePackOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify pack method for string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify pack method for string '" << value << "'.\n"; return -1; } } else if (strcasecmp(name, "core") == 0) { control -> EnableCoreDumpOnAbort = ValidateArg("local", name, value); } else if (strcasecmp(name, "kill") == 0) { if (control -> KillDaemonOnShutdownNumber < control -> KillDaemonOnShutdownLimit) { #ifdef TEST *logofs << "Loop: WARNING! Adding process with pid '" << ValidateArg("local", name, value) << " to the " << "daemons to kill at shutdown.\n" << logofs_flush; #endif control -> KillDaemonOnShutdown[control -> KillDaemonOnShutdownNumber] = ValidateArg("local", name, value); control -> KillDaemonOnShutdownNumber++; } else { #ifdef WARNING *logofs << "Loop: WARNING! Number of daemons to kill " << "at shutdown exceeded.\n" << logofs_flush; #endif cerr << "Warning" << ": Number of daemons to kill " << "at shutdown exceeded.\n"; } } else if (strcasecmp(name, "strict") == 0) { if (control -> ProxyMode == proxy_server) { PrintOptionIgnored("local", name, value); } else { useStrict = ValidateArg("local", name, value); } } else if (strcasecmp(name, "encryption") == 0) { useEncryption = ValidateArg("local", name, value); } else if (strcasecmp(name, "product") == 0) { strncpy(productName, value, DEFAULT_STRING_LENGTH - 1); } else if (strcasecmp(name, "rootless") == 0 || strcasecmp(name, "geometry") == 0 || strcasecmp(name, "resize") == 0 || strcasecmp(name, "fullscreen") == 0 || strcasecmp(name, "keyboard") == 0 || strcasecmp(name, "clipboard") == 0 || strcasecmp(name, "streaming") == 0 || strcasecmp(name, "backingstore") == 0) { #ifdef DEBUG *logofs << "Loop: Ignoring agent option '" << name << "' with value '" << value << "'.\n" << logofs_flush; #endif } else if (strcasecmp(name, "composite") == 0 || strcasecmp(name, "shmem") == 0 || strcasecmp(name, "shpix") == 0 || strcasecmp(name, "kbtype") == 0 || strcasecmp(name, "client") == 0 || strcasecmp(name, "shadow") == 0 || strcasecmp(name, "shadowuid") == 0 || strcasecmp(name, "shadowmode") == 0 || strcasecmp(name, "clients") == 0) { #ifdef DEBUG *logofs << "Loop: Ignoring agent option '" << name << "' with value '" << value << "'.\n" << logofs_flush; #endif } else if (strcasecmp(name, "defer") == 0 || strcasecmp(name, "tile") == 0 || strcasecmp(name, "menu") == 0) { #ifdef DEBUG *logofs << "Loop: Ignoring agent option '" << name << "' with value '" << value << "'.\n" << logofs_flush; #endif } else { #ifdef WARNING *logofs << "Loop: WARNING! Ignoring unknown option '" << name << "' with value '" << value << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Ignoring unknown option '" << name << "' with value '" << value << "'.\n"; } name = strtok(NULL, "="); } // End of while (name) ... #ifdef TEST *logofs << "Loop: Completed parsing of string '" << env << "'.\n" << logofs_flush; #endif if (*fileOptions != '\0') { if (strcmp(fileOptions, optionsFileName) != 0) { #ifdef TEST *logofs << "Loop: Reading options from '" << fileOptions << "'.\n" << logofs_flush; #endif if (ParseFileOptions(fileOptions) < 0) { return -1; } } #ifdef WARNING else { *logofs << "Loop: WARNING! Name of the options file " << "specified multiple times. Not parsing " << "again.\n" << logofs_flush; } #endif if (*optionsFileName == '\0') { strncpy(optionsFileName, value, DEFAULT_STRING_LENGTH - 1); #ifdef TEST *logofs << "Loop: Assuming name of options file '" << optionsFileName << "'.\n" << logofs_flush; #endif } } // // If port where proxy is acting as an X server // was not specified assume the same port where // proxy is listening for the remote peer. // if (xPort == DEFAULT_NX_X_PORT) { xPort = proxyPort; } return 1; } // // Parse the command line options passed by user when // running proxy in stand alone mode. Note that passing // parameters this way is strongly discouraged. These // command line switch can change (and they do often). // Please, use the form "option=value" instead and set // the DISPLAY environment variable. // int ParseCommandLineOptions(int argc, const char **argv) { // // Be sure log file is valid. // if (logofs == NULL) { logofs = &cerr; } if (setjmp(context) == 1) { #ifdef TEST *logofs << "Loop: Out of the long jump while parsing " << "the command line options.\n" << logofs_flush; #endif return -1; } // // Be sure we have a parameters repository // if (control == NULL) { control = new Control(); } if (parsedCommand == 1) { #ifdef TEST *logofs << "Loop: Skipping a further parse of command line options.\n" << logofs_flush; #endif return 1; } #ifdef TEST *logofs << "Loop: Going to parse the command line options.\n" << logofs_flush; #endif parsedCommand = 1; // // Print out arguments. // #ifdef TEST *logofs << "Loop: Argc is " << argc << ".\n" << logofs_flush; for (int argi = 0; argi < argc; argi++) { *logofs << "Loop: Argv[" << argi << "] is " << argv[argi] << ".\n" << logofs_flush; } #endif // // Shall use getopt here. // for (int argi = 1; argi < argc; argi++) { const char *nextArg = argv[argi]; if (*nextArg == '-') { switch (*(nextArg + 1)) { case 'h': { PrintUsageInfo(nextArg, 0); return -1; } case 'C': { // // Start proxy in CLIENT mode. // if (WE_SET_PROXY_MODE == 0) { #ifdef TEST *logofs << "Loop: Setting local proxy mode to proxy_client.\n" << logofs_flush; #endif control -> ProxyMode = proxy_client; } else if (control -> ProxyMode != proxy_client) { #ifdef PANIC *logofs << "Loop: PANIC! Can't redefine local proxy to " << "client mode.\n" << logofs_flush; #endif cerr << "Error" << ": Can't redefine local proxy to " << "client mode.\n"; return -1; } break; } case 'S': { // // Start proxy in SERVER mode. // if (WE_SET_PROXY_MODE == 0) { #ifdef TEST *logofs << "Loop: Setting local proxy mode to proxy_server.\n" << logofs_flush; #endif control -> ProxyMode = proxy_server; } else if (control -> ProxyMode != proxy_server) { #ifdef PANIC *logofs << "Loop: PANIC! Can't redefine local proxy to " << "server mode.\n" << logofs_flush; #endif cerr << "Error" << ": Can't redefine local proxy to " << "server mode.\n"; return -1; } break; } case 'v': { PrintVersionInfo(); return -1; } default: { PrintUsageInfo(nextArg, 1); // // Function GetArg() is not used anymore. // Add a dummy call to avoid the warning. // if (0) { GetArg(argi, argc, argv); } return -1; } } } else { if (nextArg) { // // Try to parse the option as a remote host:port // specification as in 'localhost:8'. Such a // parameter can be specified at the end of the // command line at the connecting side. // if (ParseHostOption(nextArg, connectHost, connectPort) > 0) { // // Assume port is at a proxied display offset. // proxyPort = connectPort; connectPort += DEFAULT_NX_PROXY_PORT_OFFSET; } else if (ParseEnvironmentOptions(nextArg, 1) < 0) { return -1; } } } } return 1; } // // Set the variable to the values of host and // port where this proxy is going to hook to // an existing proxy. // int ParseBindOptions(char **host, int *port) { if (*bindHost != '\0') { *host = bindHost; *port = bindPort; return 1; } else { return 0; } } // // Read options from file and merge with environment. // int ParseFileOptions(const char *file) { char *fileName; if (*file != '/' && *file != '.') { char *filePath = GetSessionPath(); if (filePath == NULL) { cerr << "Error" << ": Cannot determine directory for NX option file.\n"; HandleCleanup(); } fileName = new char[strlen(filePath) + strlen("/") + strlen(file) + 1]; strcpy(fileName, filePath); strcat(fileName, "/"); strcat(fileName, file); delete [] filePath; } else { fileName = new char[strlen(file) + 1]; strcpy(fileName, file); } #ifdef TEST *logofs << "Loop: Going to read options from file '" << fileName << "'.\n" << logofs_flush; #endif FILE *filePtr = fopen(fileName, "r"); if (filePtr == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Can't open options file '" << fileName << "'. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't open options file '" << fileName << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"; delete [] fileName; return -1; } char options[DEFAULT_DISPLAY_OPTIONS_LENGTH]; #ifdef VALGRIND memset(options, '\0', DEFAULT_DISPLAY_OPTIONS_LENGTH); #endif if (fgets(options, DEFAULT_DISPLAY_OPTIONS_LENGTH, filePtr) == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Can't read options from file '" << fileName << "'. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't read options from file '" << fileName << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"; fclose(filePtr); delete [] fileName; return -1; } fclose(filePtr); // // Purge the newline and the other non- // printable characters in the string. // char *next = options; while (*next != '\0') { if (isprint(*next) == 0) { *next = '\0'; } next++; } #ifdef TEST *logofs << "Loop: Read options '" << options << "' from file '" << fileName << "'.\n" << logofs_flush; #endif if (ParseEnvironmentOptions(options, 1) < 0) { delete [] fileName; return -1; } delete [] fileName; return 1; } // // Parse the option string passed from the // remote proxy at startup. // int ParseRemoteOptions(char *opts) { #ifdef TEST *logofs << "Loop: Going to parse the remote options " << "string '" << opts << "'.\n" << logofs_flush; #endif char *name; char *value; // // The options string is intended to be a series // of name/value tuples in the form name=value // separated by the ',' character. // int hasCookie = 0; int hasLink = 0; int hasPack = 0; int hasCache = 0; int hasImages = 0; int hasDelta = 0; int hasStream = 0; int hasData = 0; int hasLimit = 0; int hasRender = 0; int hasTaint = 0; int hasType = 0; int hasStrict = 0; int hasShseg = 0; // // Get rid of the terminating space. // if (*(opts + strlen(opts) - 1) == ' ') { *(opts + strlen(opts) - 1) = '\0'; } name = strtok(opts, "="); while (name) { value = strtok(NULL, ","); if (CheckArg("remote", name, value) < 0) { return -1; } if (strcasecmp(name, "cookie") == 0) { if (WE_PROVIDE_CREDENTIALS) { #ifdef WARNING *logofs << "Loop: WARNING! Ignoring remote option 'cookie' " << "with value '" << value << "' when initiating " << "connection.\n" << logofs_flush; #endif cerr << "Warning" << ": Ignoring remote option 'cookie' " << "with value '" << value << "' when initiating " << "connection.\n"; } else if (strncasecmp(authCookie, value, strlen(authCookie)) != 0) { #ifdef PANIC *logofs << "Loop: PANIC! Authentication cookie '" << value << "' doesn't match '" << authCookie << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Authentication cookie '" << value << "' doesn't match '" << authCookie << "'.\n"; return -1; } hasCookie = 1; } else if (strcasecmp(name, "link") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else { if (*linkSpeedName != '\0' && strcasecmp(linkSpeedName, value) != 0) { #ifdef WARNING *logofs << "Loop: WARNING! Overriding option 'link' " << "with new value '" << value << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Overriding option 'link' " << "with new value '" << value << "'.\n"; } if (ParseLinkOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify remote 'link' " << "option in string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify remote 'link' " << "option in string '" << value << "'.\n"; return -1; } } hasLink = 1; } else if (strcasecmp(name, "pack") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else { if (*packMethodName != '\0' && strcasecmp(packMethodName, value) != 0) { #ifdef WARNING *logofs << "Loop: WARNING! Overriding option 'pack' " << "with remote value '" << value << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Overriding option 'pack' " << "with remote value '" << value << "'.\n"; } if (ParsePackOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid pack option '" << value << "' requested by remote.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid pack option '" << value << "' requested by remote.\n"; return -1; } } hasPack = 1; } else if (strcasecmp(name, "cache") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else { // // Cache size is sent as a hint of how much memory // the remote proxy is going to consume. A very low // powered thin client could choose to refuse the // connection. // if (ParseCacheOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify remote 'cache' " << "option in string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify remote 'cache' " << "option in string '" << value << "'.\n"; return -1; } } hasCache = 1; } else if (strcasecmp(name, "images") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else { // // Images cache size is sent as a hint. // There is no obbligation for the local // proxy to use the persistent cache. // if (ParseImagesOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify remote 'images' " << "option in string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify remote 'images' " << "option in string '" << value << "'.\n"; return -1; } } hasImages = 1; } else if (strcasecmp(name, "limit") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else { if (*bitrateLimitName != '\0' && strcasecmp(bitrateLimitName, value) != 0) { #ifdef WARNING *logofs << "Loop: WARNING! Overriding option 'limit' " << "with new value '" << value << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Overriding option 'limit' " << "with new value '" << value << "'.\n"; } if (ParseBitrateOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify 'limit' " << "option in string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify 'limit' " << "option in string '" << value << "'.\n"; return -1; } } hasLimit = 1; } else if (strcasecmp(name, "render") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else { useRender = ValidateArg("remote", name, value); } hasRender = 1; } else if (strcasecmp(name, "taint") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else { useTaint = ValidateArg("remote", name, value); } hasTaint = 1; } else if (strcasecmp(name, "type") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else { if (strcasecmp(value, "default") == 0) { *sessionType = '\0'; } else { strncpy(sessionType, value, DEFAULT_STRING_LENGTH - 1); } } hasType = 1; } else if (strcasecmp(name, "strict") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else { useStrict = ValidateArg("remote", name, value); } hasStrict = 1; } else if (strcasecmp(name, "shseg") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else if (ParseShmemOption(value) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Can't identify size of shared memory " << "segment in string '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't identify size of shared memory " << "segment in string '" << value << "'.\n"; return -1; } hasShseg = 1; } else if (strcasecmp(name, "delta") == 0) { if (control -> ProxyMode == proxy_client) { PrintOptionIgnored("remote", name, value); } else { control -> RemoteDeltaCompression = ValidateArg("remote", name, value); // // Follow for delta compression the // same settings as the client proxy. // control -> LocalDeltaCompression = control -> RemoteDeltaCompression; } hasDelta = 1; } else if (strcasecmp(name, "stream") == 0) { // // If remote side didn't choose its own // stream compression level then assume // local settings. // if (strcasecmp(value, "default") == 0) { // // This applies only at client side. // control -> RemoteStreamCompression = control -> LocalStreamCompression; control -> RemoteStreamCompressionLevel = control -> LocalStreamCompressionLevel; } else { control -> RemoteStreamCompressionLevel = ValidateArg("remote", name, value); if (control -> RemoteStreamCompressionLevel > 0) { control -> RemoteStreamCompression = 1; } else { control -> RemoteStreamCompression = 0; } if (control -> LocalStreamCompressionLevel < 0) { control -> LocalStreamCompressionLevel = ValidateArg("remote", name, value); if (control -> LocalStreamCompressionLevel > 0) { control -> LocalStreamCompression = 1; } else { control -> LocalStreamCompression = 0; } } } hasStream = 1; } else if (strcasecmp(name, "data") == 0) { // // Apply the same to data compression level. // if (strcasecmp(value, "default") == 0) { control -> RemoteDataCompression = control -> LocalDataCompression; control -> RemoteDataCompressionLevel = control -> LocalDataCompressionLevel; } else { control -> RemoteDataCompressionLevel = ValidateArg("remote", name, value); if (control -> RemoteDataCompressionLevel > 0) { control -> RemoteDataCompression = 1; } else { control -> RemoteDataCompression = 0; } if (control -> LocalDataCompressionLevel < 0) { control -> LocalDataCompressionLevel = ValidateArg("remote", name, value); if (control -> LocalDataCompressionLevel > 0) { control -> LocalDataCompression = 1; } else { control -> LocalDataCompression = 0; } } } hasData = 1; } else if (strcasecmp(name, "flush") == 0) { // // This option has no effect in recent // versions. // #ifdef DEBUG *logofs << "Loop: Ignoring obsolete remote option '" << name << "' with value '" << value << "'.\n" << logofs_flush; #endif } else { #ifdef WARNING *logofs << "Loop: WARNING! Ignoring unknown remote option '" << name << "' with value '" << value << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Ignoring unknown remote option '" << name << "' with value '" << value << "'.\n"; } name = strtok(NULL, "="); } // End of while (name) ... // // If we are client side, we need remote 'stream' // and 'data' options. If we are server, we need // all the above plus 'link' and some others. // char missing[DEFAULT_STRING_LENGTH]; *missing = '\0'; if (control -> ProxyMode == proxy_client) { if (hasStream == 0) { strcpy(missing, "stream"); } else if (hasData == 0) { strcpy(missing, "data"); } } else { // // Don't complain if the optional 'flush', // 'render' and 'taint' options are not // provided. // if (hasLink == 0) { strcpy(missing, "link"); } else if (hasCache == 0) { strcpy(missing, "cache"); } else if (hasPack == 0) { strcpy(missing, "pack"); } else if (hasDelta == 0) { strcpy(missing, "delta"); } else if (hasStream == 0) { strcpy(missing, "stream"); } else if (hasData == 0) { strcpy(missing, "data"); } else if (hasType == 0) { strcpy(missing, "type"); } else if (hasImages == 0) { strcpy(missing, "images"); } } if (WE_PROVIDE_CREDENTIALS == 0) { // // Can be that user doesn't have requested to // check the authorization cookie provided by // the connecting peer. // if (hasCookie == 0 && *authCookie != '\0') { strcpy(missing, "cookie"); } } if (*missing != '\0') { #ifdef PANIC *logofs << "Loop: PANIC! The remote peer didn't specify the option '" << missing << "'.\n" << logofs_flush; #endif cerr << "Error" << ": The remote peer didn't specify the option '" << missing << "'.\n"; return -1; } return 1; } // // Parse the cookie provided by the NX proxy // connection forwarder. // int ParseForwarderOptions(char *opts) { #ifdef TEST *logofs << "Loop: Going to parse the forwarder options " << "string '" << opts << "'.\n" << logofs_flush; #endif char *name; char *value; int hasCookie = 0; // // Get rid of the terminating space. // if (*(opts + strlen(opts) - 1) == ' ') { *(opts + strlen(opts) - 1) = '\0'; } name = strtok(opts, "="); while (name) { value = strtok(NULL, ","); if (CheckArg("forwarder", name, value) < 0) { return -1; } if (strcasecmp(name, "cookie") == 0) { if (strncasecmp(authCookie, value, strlen(authCookie)) != 0) { #ifdef PANIC *logofs << "Loop: PANIC! The NX forwarder cookie '" << value << "' doesn't match '" << authCookie << "'.\n" << logofs_flush; #endif cerr << "Error" << ": The NX forwarder cookie '" << value << "' doesn't match '" << authCookie << "'.\n"; return -1; } hasCookie = 1; } else { #ifdef WARNING *logofs << "Loop: WARNING! Ignoring unknown forwarder option '" << name << "' with value '" << value << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Ignoring unknown forwarder option '" << name << "' with value '" << value << "'.\n"; } name = strtok(NULL, "="); } // End of while (name) ... if (hasCookie == 0) { #ifdef PANIC *logofs << "Loop: PANIC! The NX forwarder didn't provide " << "the authentication cookie.\n" << logofs_flush; #endif cerr << "Error" << ": The NX forwarder didn't provide " << "the authentication cookie.\n"; return -1; } return 1; } int SetCore() { #ifdef COREDUMPS rlimit rlim; if (getrlimit(RLIMIT_CORE, &rlim)) { #ifdef TEST *logofs << "Cannot read RLIMIT_CORE. Error is '" << ESTR() << "'.\n" << logofs_flush; #endif return -1; } if (rlim.rlim_cur < rlim.rlim_max) { rlim.rlim_cur = rlim.rlim_max; if (setrlimit(RLIMIT_CORE, &rlim)) { #ifdef TEST *logofs << "Loop: Cannot read RLIMIT_CORE. Error is '" << ESTR() << "'.\n" << logofs_flush; #endif return -2; } } #ifdef TEST *logofs << "Loop: Set RLIMIT_CORE to "<< rlim.rlim_max << ".\n" << logofs_flush; #endif #endif // #ifdef COREDUMPS return 1; } char *GetLastCache(char *listBuffer, const char *searchPath) { if (listBuffer == NULL || searchPath == NULL || strncmp(listBuffer, "cachelist=", strlen("cachelist=")) != 0) { #ifdef TEST *logofs << "Loop: Invalid parameters '" << listBuffer << "' and '" << (searchPath != NULL ? searchPath : "") << "'. Can't select any cache.\n" << logofs_flush; #endif return NULL; } char *selectedName = new char[MD5_LENGTH * 2 + 3]; *selectedName = '\0'; const char *localPrefix; const char *remotePrefix; if (control -> ProxyMode == proxy_client) { localPrefix = "C-"; remotePrefix = "S-"; } else { localPrefix = "S-"; remotePrefix = "C-"; } // // Get rid of prefix. // listBuffer += strlen("cachelist="); char *fileName; fileName = strtok(listBuffer, ","); // // It is "/path/to/file" + "/" + "C-" + 32 + "\0". // char fullPath[strlen(searchPath) + MD5_LENGTH * 2 + 4]; time_t selectedTime = 0; struct stat fileStat; while (fileName) { if (strncmp(fileName, "none", strlen("none")) == 0) { #ifdef TEST *logofs << "Loop: No cache files seem to be available.\n" << logofs_flush; #endif delete [] selectedName; return NULL; } else if (strlen(fileName) != MD5_LENGTH * 2 + 2 || strncmp(fileName, remotePrefix, 2) != 0) { #ifdef PANIC *logofs << "Loop: PANIC! Bad cache file name '" << fileName << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Bad cache file name '" << fileName << "'.\n"; delete [] selectedName; HandleCleanup(); } #ifdef TEST *logofs << "Loop: Parsing remote cache name '" << fileName << "'.\n" << logofs_flush; #endif // // Prefix, received as "S-", becomes // "C-" and viceversa. // *fileName = *localPrefix; strcpy(fullPath, searchPath); strcat(fullPath, "/"); strcat(fullPath, fileName); if (stat(fullPath, &fileStat) == 0) { #ifdef TEST *logofs << "Loop: Found a matching cache '" << fullPath << "'.\n" << logofs_flush; #endif if (fileStat.st_mtime >= selectedTime) { strcpy(selectedName, fileName); selectedTime = fileStat.st_mtime; } } #ifdef TEST else { *logofs << "Loop: Can't get stats of file '" << fullPath << "'.\n" << logofs_flush; } #endif fileName = strtok(NULL, ","); } if (*selectedName != '\0') { return selectedName; } else { delete [] selectedName; return NULL; } } char *GetTempPath() { if (*tempDir == '\0') { // // Check the NX_TEMP environment, first, // then the TEMP variable. // const char *tempEnv = getenv("NX_TEMP"); if (tempEnv == NULL || *tempEnv == '\0') { #ifdef TEST *logofs << "Loop: WARNING! No environment for NX_TEMP.\n" << logofs_flush; #endif tempEnv = getenv("TEMP"); if (tempEnv == NULL || *tempEnv == '\0') { #ifdef TEST *logofs << "Loop: WARNING! No environment for TEMP.\n" << logofs_flush; #endif tempEnv = "/tmp"; } } if (strlen(tempEnv) > DEFAULT_STRING_LENGTH - 1) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid value for the NX " << "temporary directory '" << tempEnv << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid value for the NX " << "temporary directory '" << tempEnv << "'.\n"; HandleCleanup(); } strcpy(tempDir, tempEnv); #ifdef TEST *logofs << "Loop: Assuming temporary NX directory '" << tempDir << "'.\n" << logofs_flush; #endif } char *tempPath = new char[strlen(tempDir) + 1]; if (tempPath == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Can't allocate memory " << "for the temp path.\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory " << "for the temp path.\n"; HandleCleanup(); } strcpy(tempPath, tempDir); return tempPath; } char *GetClientPath() { if (*clientDir == '\0') { // // Check the NX_CLIENT environment. // const char *clientEnv = getenv("NX_CLIENT"); if (clientEnv == NULL || *clientEnv == '\0') { #ifdef TEST *logofs << "Loop: WARNING! No environment for NX_CLIENT.\n" << logofs_flush; #endif // // Try to guess the location of the client. // clientEnv = "/usr/NX/bin/nxclient"; #ifdef __APPLE__ clientEnv = "/Applications/NX Client for OSX.app/Contents/MacOS/nxclient"; #endif #ifdef __CYGWIN32__ clientEnv = "C:\\Program Files\\NX Client for Windows\\nxclient"; #endif } if (strlen(clientEnv) > DEFAULT_STRING_LENGTH - 1) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid value for the NX " << "client directory '" << clientEnv << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid value for the NX " << "client directory '" << clientEnv << "'.\n"; HandleCleanup(); } strcpy(clientDir, clientEnv); #ifdef TEST *logofs << "Loop: Assuming NX client location '" << clientDir << "'.\n" << logofs_flush; #endif } char *clientPath = new char[strlen(clientDir) + 1]; if (clientPath == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Can't allocate memory " << "for the client path.\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory " << "for the client path.\n"; HandleCleanup(); } strcpy(clientPath, clientDir); return clientPath; } char *GetSystemPath() { if (*systemDir == '\0') { // // Check the NX_SYSTEM environment. // const char *systemEnv = getenv("NX_SYSTEM"); if (systemEnv == NULL || *systemEnv == '\0') { #ifdef TEST *logofs << "Loop: WARNING! No environment for NX_SYSTEM.\n" << logofs_flush; #endif systemEnv = "/usr/NX"; } if (strlen(systemEnv) > DEFAULT_STRING_LENGTH - 1) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid value for the NX " << "system directory '" << systemEnv << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid value for the NX " << "system directory '" << systemEnv << "'.\n"; HandleCleanup(); } strcpy(systemDir, systemEnv); #ifdef TEST *logofs << "Loop: Assuming system NX directory '" << systemDir << "'.\n" << logofs_flush; #endif } char *systemPath = new char[strlen(systemDir) + 1]; if (systemPath == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Can't allocate memory " << "for the system path.\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory " << "for the system path.\n"; HandleCleanup(); } strcpy(systemPath, systemDir); return systemPath; } char *GetHomePath() { if (*homeDir == '\0') { // // Check the NX_HOME environment. // const char *homeEnv = getenv("NX_HOME"); if (homeEnv == NULL || *homeEnv == '\0') { #ifdef TEST *logofs << "Loop: WARNING! No environment for NX_HOME.\n" << logofs_flush; #endif homeEnv = getenv("HOME"); if (homeEnv == NULL || *homeEnv == '\0') { #ifdef PANIC *logofs << "Loop: PANIC! No environment for HOME.\n" << logofs_flush; #endif cerr << "Error" << ": No environment for HOME.\n"; HandleCleanup(); } } if (strlen(homeEnv) > DEFAULT_STRING_LENGTH - 1) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid value for the NX " << "home directory '" << homeEnv << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid value for the NX " << "home directory '" << homeEnv << "'.\n"; HandleCleanup(); } strcpy(homeDir, homeEnv); #ifdef TEST *logofs << "Loop: Assuming NX user's home directory '" << homeDir << "'.\n" << logofs_flush; #endif } char *homePath = new char[strlen(homeDir) + 1]; if (homePath == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Can't allocate memory " << "for the home path.\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory " << "for the home path.\n"; HandleCleanup(); } strcpy(homePath, homeDir); return homePath; } char *GetRootPath() { if (*rootDir == '\0') { // // Check the NX_ROOT environment. // const char *rootEnv = getenv("NX_ROOT"); if (rootEnv == NULL || *rootEnv == '\0') { #ifdef TEST *logofs << "Loop: WARNING! No environment for NX_ROOT.\n" << logofs_flush; #endif // // We will determine the root NX directory // based on the NX_HOME or HOME directory // settings. // const char *homeEnv = GetHomePath(); if (strlen(homeEnv) > DEFAULT_STRING_LENGTH - strlen("/.nx") - 1) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid value for the NX " << "home directory '" << homeEnv << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid value for the NX " << "home directory '" << homeEnv << "'.\n"; HandleCleanup(); } #ifdef TEST *logofs << "Loop: Assuming NX root directory in " << "the user's home '" << homeEnv << "'.\n" << logofs_flush; #endif strcpy(rootDir, homeEnv); strcat(rootDir, "/.nx"); delete [] homeEnv; // // Create the NX root directory. // struct stat dirStat; if ((stat(rootDir, &dirStat) == -1) && (EGET() == ENOENT)) { if (mkdir(rootDir, 0700) < 0 && (EGET() != EEXIST)) { #ifdef PANIC *logofs << "Loop: PANIC! Can't create directory '" << rootDir << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't create directory '" << rootDir << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; HandleCleanup(); } } } else { if (strlen(rootEnv) > DEFAULT_STRING_LENGTH - 1) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid value for the NX " << "root directory '" << rootEnv << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid value for the NX " << "root directory '" << rootEnv << "'.\n"; HandleCleanup(); } strcpy(rootDir, rootEnv); } #ifdef TEST *logofs << "Loop: Assuming NX root directory '" << rootDir << "'.\n" << logofs_flush; #endif } char *rootPath = new char[strlen(rootDir) + 1]; if (rootPath == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Can't allocate memory " << "for the root path.\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory " << "for the root path.\n"; HandleCleanup(); } strcpy(rootPath, rootDir); return rootPath; } char *GetCachePath() { char *rootPath = GetRootPath(); char *cachePath; if (*sessionType != '\0') { cachePath = new char[strlen(rootPath) + strlen("/cache-") + strlen(sessionType) + 1]; } else { cachePath = new char[strlen(rootPath) + strlen("/cache") + 1]; } strcpy(cachePath, rootPath); if (*sessionType != '\0') { strcat(cachePath, "/cache-"); strcat(cachePath, sessionType); } else { strcat(cachePath, "/cache"); } // // Create the cache directory if needed. // struct stat dirStat; if ((stat(cachePath, &dirStat) == -1) && (EGET() == ENOENT)) { if (mkdir(cachePath, 0700) < 0 && (EGET() != EEXIST)) { #ifdef PANIC *logofs << "Loop: PANIC! Can't create directory '" << cachePath << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't create directory '" << cachePath << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; delete [] rootPath; delete [] cachePath; return NULL; } } delete [] rootPath; return cachePath; } char *GetImagesPath() { char *rootPath = GetRootPath(); char *imagesPath = new char[strlen(rootPath) + strlen("/images") + 1]; strcpy(imagesPath, rootPath); strcat(imagesPath, "/images"); // // Create the cache directory if needed. // struct stat dirStat; if ((stat(imagesPath, &dirStat) == -1) && (EGET() == ENOENT)) { if (mkdir(imagesPath, 0700) < 0 && (EGET() != EEXIST)) { #ifdef PANIC *logofs << "Loop: PANIC! Can't create directory '" << imagesPath << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't create directory '" << imagesPath << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; delete [] rootPath; delete [] imagesPath; return NULL; } } // // Create 16 directories in the path to // hold the images whose name begins with // the corresponding hexadecimal digit. // char *digitPath = new char[strlen(imagesPath) + 5]; strcpy(digitPath, imagesPath); // // Image paths have format "[path][/I-c][\0]", // where c is the first digit of the checksum. // for (char digit = 0; digit < 16; digit++) { sprintf(digitPath + strlen(imagesPath), "/I-%01X", digit); if ((stat(digitPath, &dirStat) == -1) && (EGET() == ENOENT)) { if (mkdir(digitPath, 0700) < 0 && (EGET() != EEXIST)) { #ifdef PANIC *logofs << "Loop: PANIC! Can't create directory '" << digitPath << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't create directory '" << digitPath << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; delete [] rootPath; delete [] imagesPath; delete [] digitPath; return NULL; } } } delete [] rootPath; delete [] digitPath; return imagesPath; } char *GetSessionPath() { if (*sessionDir == '\0') { char *rootPath = GetRootPath(); strcpy(sessionDir, rootPath); if (control -> ProxyMode == proxy_client) { strcat(sessionDir, "/C-"); } else { strcat(sessionDir, "/S-"); } if (*sessionId == '\0') { char port[DEFAULT_STRING_LENGTH]; sprintf(port, "%d", proxyPort); strcpy(sessionId, port); } strcat(sessionDir, sessionId); struct stat dirStat; if ((stat(sessionDir, &dirStat) == -1) && (EGET() == ENOENT)) { if (mkdir(sessionDir, 0700) < 0 && (EGET() != EEXIST)) { #ifdef PANIC *logofs << "Loop: PANIC! Can't create directory '" << sessionDir << ". Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't create directory '" << sessionDir << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; delete [] rootPath; return NULL; } } #ifdef TEST *logofs << "Loop: Root of NX session is '" << sessionDir << "'.\n" << logofs_flush; #endif delete [] rootPath; } char *sessionPath = new char[strlen(sessionDir) + 1]; strcpy(sessionPath, sessionDir); return sessionPath; } // // Identify requested link characteristics // and set control parameters accordingly. // int ParseLinkOption(const char *opt) { // // Normalize the user input. // if (strcasecmp(opt, "modem") == 0 || strcasecmp(opt, "33k") == 0 || strcasecmp(opt, "56k") == 0) { strcpy(linkSpeedName, "MODEM"); } else if (strcasecmp(opt, "isdn") == 0 || strcasecmp(opt, "64k") == 0 || strcasecmp(opt, "128k") == 0) { strcpy(linkSpeedName, "ISDN"); } else if (strcasecmp(opt, "adsl") == 0 || strcasecmp(opt, "256k") == 0 || strcasecmp(opt, "640k") == 0) { strcpy(linkSpeedName, "ADSL"); } else if (strcasecmp(opt, "wan") == 0 || strcasecmp(opt, "1m") == 0 || strcasecmp(opt, "2m") == 0 || strcasecmp(opt, "34m") == 0) { strcpy(linkSpeedName, "WAN"); } else if (strcasecmp(opt, "lan") == 0 || strcasecmp(opt, "10m") == 0 || strcasecmp(opt, "100m") == 0 || strcasecmp(opt, "local") == 0) { strcpy(linkSpeedName, "LAN"); } if (strcasecmp(linkSpeedName, "modem") != 0 && strcasecmp(linkSpeedName, "isdn") != 0 && strcasecmp(linkSpeedName, "adsl") != 0 && strcasecmp(linkSpeedName, "wan") != 0 && strcasecmp(linkSpeedName, "lan") != 0) { return -1; } return 1; } int ParsePackOption(const char *opt) { #ifdef DEBUG *logofs << "Loop: Pack method is " << packMethod << " quality is " << packQuality << ".\n" << logofs_flush; #endif #ifdef DEBUG *logofs << "Loop: Parsing pack method '" << opt << "'.\n" << logofs_flush; #endif if (strcasecmp(opt, "0") == 0 || strcasecmp(opt, "none") == 0 || strcasecmp(opt, "nopack") == 0 || strcasecmp(opt, "no-pack") == 0) { packMethod = PACK_NONE; } else if (strcasecmp(opt, "8") == 0) { packMethod = PACK_MASKED_8_COLORS; } else if (strcasecmp(opt, "64") == 0) { packMethod = PACK_MASKED_64_COLORS; } else if (strcasecmp(opt, "256") == 0) { packMethod = PACK_MASKED_256_COLORS; } else if (strcasecmp(opt, "512") == 0) { packMethod = PACK_MASKED_512_COLORS; } else if (strcasecmp(opt, "4k") == 0) { packMethod = PACK_MASKED_4K_COLORS; } else if (strcasecmp(opt, "32k") == 0) { packMethod = PACK_MASKED_32K_COLORS; } else if (strcasecmp(opt, "64k") == 0) { packMethod = PACK_MASKED_64K_COLORS; } else if (strcasecmp(opt, "256k") == 0) { packMethod = PACK_MASKED_256K_COLORS; } else if (strcasecmp(opt, "2m") == 0) { packMethod = PACK_MASKED_2M_COLORS; } else if (strcasecmp(opt, "16m") == 0) { packMethod = PACK_MASKED_16M_COLORS; } else if (strncasecmp(opt, "8-jpeg", strlen("8-jpeg")) == 0) { packMethod = PACK_JPEG_8_COLORS; } else if (strncasecmp(opt, "64-jpeg", strlen("64-jpeg")) == 0) { packMethod = PACK_JPEG_64_COLORS; } else if (strncasecmp(opt, "256-jpeg", strlen("256-jpeg")) == 0) { packMethod = PACK_JPEG_256_COLORS; } else if (strncasecmp(opt, "512-jpeg", strlen("512-jpeg")) == 0) { packMethod = PACK_JPEG_512_COLORS; } else if (strncasecmp(opt, "4k-jpeg", strlen("4k-jpeg")) == 0) { packMethod = PACK_JPEG_4K_COLORS; } else if (strncasecmp(opt, "32k-jpeg", strlen("32k-jpeg")) == 0) { packMethod = PACK_JPEG_32K_COLORS; } else if (strncasecmp(opt, "64k-jpeg", strlen("64k-jpeg")) == 0) { packMethod = PACK_JPEG_64K_COLORS; } else if (strncasecmp(opt, "256k-jpeg", strlen("256k-jpeg")) == 0) { packMethod = PACK_JPEG_256K_COLORS; } else if (strncasecmp(opt, "2m-jpeg", strlen("2m-jpeg")) == 0) { packMethod = PACK_JPEG_2M_COLORS; } else if (strncasecmp(opt, "16m-jpeg", strlen("16m-jpeg")) == 0) { packMethod = PACK_JPEG_16M_COLORS; } else if (strncasecmp(opt, "8-png", strlen("8-png")) == 0) { packMethod = PACK_PNG_8_COLORS; } else if (strncasecmp(opt, "64-png", strlen("64-png")) == 0) { packMethod = PACK_PNG_64_COLORS; } else if (strncasecmp(opt, "256-png", strlen("256-png")) == 0) { packMethod = PACK_PNG_256_COLORS; } else if (strncasecmp(opt, "512-png", strlen("512-png")) == 0) { packMethod = PACK_PNG_512_COLORS; } else if (strncasecmp(opt, "4k-png", strlen("4k-png")) == 0) { packMethod = PACK_PNG_4K_COLORS; } else if (strncasecmp(opt, "32k-png", strlen("32k-png")) == 0) { packMethod = PACK_PNG_32K_COLORS; } else if (strncasecmp(opt, "64k-png", strlen("64k-png")) == 0) { packMethod = PACK_PNG_64K_COLORS; } else if (strncasecmp(opt, "256k-png", strlen("256k-png")) == 0) { packMethod = PACK_PNG_256K_COLORS; } else if (strncasecmp(opt, "2m-png", strlen("2m-png")) == 0) { packMethod = PACK_PNG_2M_COLORS; } else if (strncasecmp(opt, "16m-png", strlen("16m-png")) == 0) { packMethod = PACK_PNG_16M_COLORS; } else if (strncasecmp(opt, "16m-rgb", strlen("16m-rgb")) == 0 || strncasecmp(opt, "rgb", strlen("rgb")) == 0) { packMethod = PACK_RGB_16M_COLORS; } else if (strncasecmp(opt, "16m-rle", strlen("16m-rle")) == 0 || strncasecmp(opt, "rle", strlen("rle")) == 0) { packMethod = PACK_RLE_16M_COLORS; } else if (strncasecmp(opt, "16m-bitmap", strlen("16m-bitmap")) == 0 || strncasecmp(opt, "bitmap", strlen("bitmap")) == 0) { packMethod = PACK_BITMAP_16M_COLORS; } else if (strncasecmp(opt, "lossy", strlen("lossy")) == 0) { packMethod = PACK_LOSSY; } else if (strncasecmp(opt, "lossless", strlen("lossless")) == 0) { packMethod = PACK_LOSSLESS; } else if (strncasecmp(opt, "adaptive", strlen("adaptive")) == 0) { packMethod = PACK_ADAPTIVE; } else { return -1; } if (packMethod == PACK_NONE) { strcpy(packMethodName, "none"); } else { strcpy(packMethodName, opt); } if (packMethod == PACK_RGB_16M_COLORS || packMethod == PACK_RLE_16M_COLORS || packMethod == PACK_BITMAP_16M_COLORS || (packMethod >= PACK_JPEG_8_COLORS && packMethod <= PACK_JPEG_16M_COLORS) || (packMethod >= PACK_PNG_8_COLORS && packMethod <= PACK_PNG_16M_COLORS) || packMethod == PACK_LOSSY || packMethod == PACK_LOSSLESS || packMethod == PACK_ADAPTIVE) { const char *dash = rindex(opt, '-'); if (dash != NULL && strlen(dash) == 2 && *(dash + 1) >= '0' && *(dash + 1) <= '9') { packQuality = atoi(dash + 1); #ifdef DEBUG *logofs << "Loop: Using pack quality '" << packQuality << "'.\n" << logofs_flush; #endif } } else { packQuality = 0; } return 1; } int ParsePackMethod(const int method, const int quality) { switch (method) { case PACK_NONE: { strcpy(packMethodName, "none"); break; } case PACK_MASKED_8_COLORS: { strcpy(packMethodName, "8"); break; } case PACK_MASKED_64_COLORS: { strcpy(packMethodName, "64"); break; } case PACK_MASKED_256_COLORS: { strcpy(packMethodName, "256"); break; } case PACK_MASKED_512_COLORS: { strcpy(packMethodName, "512"); break; } case PACK_MASKED_4K_COLORS: { strcpy(packMethodName, "4k"); break; } case PACK_MASKED_32K_COLORS: { strcpy(packMethodName, "32k"); break; } case PACK_MASKED_64K_COLORS: { strcpy(packMethodName, "64k"); break; } case PACK_MASKED_256K_COLORS: { strcpy(packMethodName, "256k"); break; } case PACK_MASKED_2M_COLORS: { strcpy(packMethodName, "2m"); break; } case PACK_MASKED_16M_COLORS: { strcpy(packMethodName, "16m"); break; } case PACK_JPEG_8_COLORS: { strcpy(packMethodName, "8-jpeg"); break; } case PACK_JPEG_64_COLORS: { strcpy(packMethodName, "64-jpeg"); break; } case PACK_JPEG_256_COLORS: { strcpy(packMethodName, "256-jpeg"); break; } case PACK_JPEG_512_COLORS: { strcpy(packMethodName, "512-jpeg"); break; } case PACK_JPEG_4K_COLORS: { strcpy(packMethodName, "4k-jpeg"); break; } case PACK_JPEG_32K_COLORS: { strcpy(packMethodName, "32k-jpeg"); break; } case PACK_JPEG_64K_COLORS: { strcpy(packMethodName, "64k-jpeg"); break; } case PACK_JPEG_256K_COLORS: { strcpy(packMethodName, "256k-jpeg"); break; } case PACK_JPEG_2M_COLORS: { strcpy(packMethodName, "2m-jpeg"); break; } case PACK_JPEG_16M_COLORS: { strcpy(packMethodName, "16m-jpeg"); break; } case PACK_PNG_8_COLORS: { strcpy(packMethodName, "8-png"); break; } case PACK_PNG_64_COLORS: { strcpy(packMethodName, "64-png"); break; } case PACK_PNG_256_COLORS: { strcpy(packMethodName, "256-png"); break; } case PACK_PNG_512_COLORS: { strcpy(packMethodName, "512-png"); break; } case PACK_PNG_4K_COLORS: { strcpy(packMethodName, "4k-png"); break; } case PACK_PNG_32K_COLORS: { strcpy(packMethodName, "32k-png"); break; } case PACK_PNG_64K_COLORS: { strcpy(packMethodName, "64k-png"); break; } case PACK_PNG_256K_COLORS: { strcpy(packMethodName, "256k-png"); break; } case PACK_PNG_2M_COLORS: { strcpy(packMethodName, "2m-png"); break; } case PACK_PNG_16M_COLORS: { strcpy(packMethodName, "16m-png"); break; } case PACK_RGB_16M_COLORS: { strcpy(packMethodName, "16m-rgb"); break; } case PACK_RLE_16M_COLORS: { strcpy(packMethodName, "16m-rle"); break; } case PACK_BITMAP_16M_COLORS: { strcpy(packMethodName, "16m-bitmap"); break; } case PACK_LOSSY: { strcpy(packMethodName, "lossy"); break; } case PACK_LOSSLESS: { strcpy(packMethodName, "lossless"); break; } case PACK_ADAPTIVE: { strcpy(packMethodName, "adaptive"); break; } default: { return -1; } } if (quality < 0 || quality > 9) { return -1; } if (packMethod == PACK_RGB_16M_COLORS || packMethod == PACK_RLE_16M_COLORS || packMethod == PACK_BITMAP_16M_COLORS || (packMethod >= PACK_JPEG_8_COLORS && packMethod <= PACK_JPEG_16M_COLORS) || (packMethod >= PACK_PNG_8_COLORS && packMethod <= PACK_PNG_16M_COLORS) || packMethod == PACK_LOSSY || packMethod == PACK_LOSSLESS || packMethod == PACK_ADAPTIVE) { sprintf(packMethodName + strlen(packMethodName), "-%d", quality); } packMethod = method; packQuality = quality; control -> PackMethod = packMethod; control -> PackQuality = packQuality; return 1; } int SetDirectories() { // // Determine the location of the user's NX // directory and the other relevant paths. // The functions below will check the pa- // rameters passed to the program and will // query the environment, if needed. // control -> HomePath = GetHomePath(); control -> RootPath = GetRootPath(); control -> SystemPath = GetSystemPath(); control -> TempPath = GetTempPath(); control -> ClientPath = GetClientPath(); return 1; } int SetLogs() { // // So far we used stderr (or stdout under // WIN32). Now use the files selected by // the user. // if (*statsFileName == '\0') { strcpy(statsFileName, "stats"); #ifdef TEST *logofs << "Loop: Assuming default statistics file '" << statsFileName << "'.\n" << logofs_flush; #endif } #ifdef TEST else { *logofs << "Loop: Name selected for statistics is '" << statsFileName << "'.\n" << logofs_flush; } #endif if (OpenLogFile(statsFileName, statofs) < 0) { HandleCleanup(); } #ifndef MIXED if (*errorsFileName == '\0') { strcpy(errorsFileName, "errors"); #ifdef TEST *logofs << "Loop: Assuming default log file name '" << errorsFileName << "'.\n" << logofs_flush; #endif } #ifdef TEST else { *logofs << "Loop: Name selected for log file is '" << errorsFileName << "'.\n" << logofs_flush; } #endif // // Share the bebug output with the nxssh binder // process. The file must be made writable by // everybody because the nxssh process is run by // nxserver as the nx user. // #ifdef BINDER strcpy(errorsFileName, "/tmp/errors"); ostream *tmpfs = new ofstream(errorsFileName, ios::out); delete tmpfs; chmod(errorsFileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); #endif if (OpenLogFile(errorsFileName, logofs) < 0) { HandleCleanup(); } // // By default the session log is the standard error // of the process. It is anyway required to set the // option when running inside SSH, otherwise the // output will go to the same file as the SSH log, // depending where the NX client has redirected the // output. // if (*sessionFileName != '\0') { #ifdef TEST *logofs << "Loop: Name selected for session file is '" << sessionFileName << "'.\n" << logofs_flush; #endif if (errofs != NULL) { #ifdef WARNING *logofs << "Loop: WARNING! Unexpected value for stream errofs.\n" << logofs_flush; #endif cerr << "Warning" << ": Unexpected value for stream errofs.\n"; } if (errsbuf != NULL) { #ifdef WARNING *logofs << "Loop: WARNING! Unexpected value for buffer errsbuf.\n" << logofs_flush; #endif cerr << "Warning" << ": Unexpected value for buffer errsbuf.\n"; } errofs = NULL; errsbuf = NULL; if (OpenLogFile(sessionFileName, errofs) < 0) { HandleCleanup(); } // // Redirect the standard error to the file. // errsbuf = cerr.rdbuf(errofs -> rdbuf()); } #endif return 1; } int SetPorts() { // // Depending on the proxy side, we need to determine on which // port to listen for the given protocol or to which port we // will have to forward the connection. Three possibilities // are given for each supported protocol: // // Port <= 0: Disable port forwarding. // Port == 1: Use the default port. // Port > 1: Use the specified port. // // At the connectiong side the user should always explicitly // set the ports where the connections will be forwarded. This // is both for security reasons and because, when running both // proxies on the same host, there is a concrete possibility // that, by using the default ports, the connection will be // forwarded to the same port where the peer proxy is listen- // ing, causing a loop. // if (cupsPort <= 0) { #ifdef TEST *logofs << "Loop: Disabling cups connections.\n" << logofs_flush; #endif cupsPort = 0; useCupsSocket = 0; } else { if (control -> ProxyMode == proxy_client) { if (cupsPort == 1) { cupsPort = DEFAULT_NX_CUPS_PORT_OFFSET + proxyPort; } useCupsSocket = 1; } else { if (cupsPort == 1) { // // Use the well-known 631/tcp port of the // Internet Printing Protocol. // cupsPort = 631; } useCupsSocket = 0; } #ifdef TEST *logofs << "Loop: Using cups port '" << cupsPort << "'.\n" << logofs_flush; #endif } if (auxPort <= 0) { #ifdef TEST *logofs << "Loop: Disabling auxiliary X11 connections.\n" << logofs_flush; #endif auxPort = 0; useAuxSocket = 0; } else { if (control -> ProxyMode == proxy_client) { if (auxPort == 1) { auxPort = DEFAULT_NX_AUX_PORT_OFFSET + proxyPort; } useAuxSocket = 1; } else { // // Auxiliary X connections are always forwarded // to the display where the session is running. // The only value accepted is 1. // if (auxPort != 1) { #ifdef WARNING *logofs << "Loop: WARNING! Overriding auxiliary X11 " << "port with new value '" << 1 << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Overriding auxiliary X11 " << "port with new value '" << 1 << "'.\n"; auxPort = 1; } useAuxSocket = 0; } #ifdef TEST *logofs << "Loop: Using auxiliary X11 port '" << auxPort << "'.\n" << logofs_flush; #endif } if (smbPort <= 0) { #ifdef TEST *logofs << "Loop: Disabling SMB connections.\n" << logofs_flush; #endif smbPort = 0; useSmbSocket = 0; } else { if (control -> ProxyMode == proxy_client) { if (smbPort == 1) { smbPort = DEFAULT_NX_SMB_PORT_OFFSET + proxyPort; } useSmbSocket = 1; } else { if (smbPort == 1) { // // Assume the 139/tcp port used for SMB // over NetBIOS over TCP. // smbPort = 139; } useSmbSocket = 0; } #ifdef TEST *logofs << "Loop: Using SMB port '" << smbPort << "'.\n" << logofs_flush; #endif } if (mediaPort <= 0) { #ifdef TEST *logofs << "Loop: Disabling multimedia connections.\n" << logofs_flush; #endif mediaPort = 0; useMediaSocket = 0; } else { if (control -> ProxyMode == proxy_client) { if (mediaPort == 1) { mediaPort = DEFAULT_NX_MEDIA_PORT_OFFSET + proxyPort; } useMediaSocket = 1; } else { if (mediaPort == 1) { // // We don't have a well-known port to // be used for media connections. // #ifdef PANIC *logofs << "Loop: PANIC! No port specified for multimedia connections.\n" << logofs_flush; #endif cerr << "Error" << ": No port specified for multimedia connections.\n"; HandleCleanup(); } useMediaSocket = 0; } #ifdef TEST *logofs << "Loop: Using multimedia port '" << mediaPort << "'.\n" << logofs_flush; #endif } if (httpPort <= 0) { #ifdef TEST *logofs << "Loop: Disabling HTTP connections.\n" << logofs_flush; #endif httpPort = 0; useHttpSocket = 0; } else { if (control -> ProxyMode == proxy_client) { if (httpPort == 1) { httpPort = DEFAULT_NX_HTTP_PORT_OFFSET + proxyPort; } useHttpSocket = 1; } else { if (httpPort == 1) { // // Use the well-known 80/tcp port. // httpPort = 80; } useHttpSocket = 0; } #ifdef TEST *logofs << "Loop: Using HTTP port '" << httpPort << "'.\n" << logofs_flush; #endif } if (ParseFontPath(fontPort) <= 0) { #ifdef TEST *logofs << "Loop: Disabling font server connections.\n" << logofs_flush; #endif *fontPort = '\0'; useFontSocket = 0; } else { // // We don't know yet if the remote proxy supports // the font server connections. If needed, we will // disable the font server connections at later // time. // if (control -> ProxyMode == proxy_server) { useFontSocket = 1; } else { useFontSocket = 0; } #ifdef TEST *logofs << "Loop: Using font server port '" << fontPort << "'.\n" << logofs_flush; #endif } if (slavePort <= 0) { #ifdef TEST *logofs << "Loop: Disabling slave connections.\n" << logofs_flush; #endif slavePort = 0; useSlaveSocket = 0; } else { // // File transfer connections can // be originated by both sides. // if (slavePort == 1) { if (control -> ProxyMode == proxy_client) { slavePort = DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET + proxyPort; } else { slavePort = DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET + proxyPort; } } useSlaveSocket = 1; #ifdef TEST *logofs << "Loop: Using slave port '" << slavePort << "'.\n" << logofs_flush; #endif } return 1; } int SetDescriptors() { unsigned int limit = 0; #ifdef RLIMIT_NOFILE rlimit limits; if (getrlimit(RLIMIT_NOFILE, &limits) == 0) { if (limits.rlim_max == RLIM_INFINITY) { limit = 0; } else { limit = (unsigned int) limits.rlim_max; } } #endif #ifdef _SC_OPEN_MAX if (limit == 0) { limit = sysconf(_SC_OPEN_MAX); } #endif #ifdef FD_SETSIZE if (limit > FD_SETSIZE) { limit = FD_SETSIZE; } #endif #ifdef RLIMIT_NOFILE if (limits.rlim_cur < limit) { limits.rlim_cur = limit; setrlimit(RLIMIT_NOFILE, &limits); } #endif if (limit == 0) { #ifdef PANIC *logofs << "Loop: PANIC! Cannot determine number of available " << "file descriptors.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot determine number of available " << "file descriptors.\n"; return -1; } return 1; } // // Find the directory containing the caches // matching the session type. // int SetCaches() { if ((control -> PersistentCachePath = GetCachePath()) == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Error getting or creating the cache path.\n" << logofs_flush; #endif cerr << "Error" << ": Error getting or creating the cache path.\n"; HandleCleanup(); } #ifdef TEST *logofs << "Loop: Path of cache files is '" << control -> PersistentCachePath << "'.\n" << logofs_flush; #endif return 1; } // // Initialize all configuration parameters. // int SetParameters() { // // Find out the type of session. // SetSession(); // // Initialize the network and compression // parameters according to the settings // suggested by the user. // SetLink(); // // Set compression according to link speed. // SetCompression(); // // Be sure that we have a literal for current // cache size. Value will reflect control's // default unless we already parsed a 'cache' // option. Server side has no control on size // of cache but is informed at session nego- // tiation about how much memory is going to // be used. // SetStorage(); // // Set size of shared memory segments. // SetShmem(); // // Make adjustments to cache based // on the pack method. // SetPack(); // // Set disk-based image cache. // SetImages(); // // Set CPU and bandwidth limits. // SetLimits(); return 1; } // // According to session literal determine // the type of traffic that is going to be // transported. Literals should be better // standardized in future NX versions. // int SetSession() { if (strncmp(sessionType, "agent", strlen("agent")) == 0 || strncmp(sessionType, "desktop", strlen("desktop")) == 0 || strncmp(sessionType, "rootless", strlen("rootless")) == 0 || strncmp(sessionType, "console", strlen("console")) == 0 || strncmp(sessionType, "default", strlen("default")) == 0 || strncmp(sessionType, "gnome", strlen("gnome")) == 0 || strncmp(sessionType, "kde", strlen("kde")) == 0 || strncmp(sessionType, "cde", strlen("cde")) == 0 || strncmp(sessionType, "xdm", strlen("xdm")) == 0) { control -> SessionMode = session_agent; } else if (strncmp(sessionType, "win", strlen("win")) == 0 || strncmp(sessionType, "vnc", strlen("vnc")) == 0) { control -> SessionMode = session_agent; } else if (strncmp(sessionType, "shadow", strlen("shadow")) == 0) { control -> SessionMode = session_shadow; } else if (strncmp(sessionType, "proxy", strlen("proxy")) == 0 || strncmp(sessionType, "application", strlen("application")) == 0 || strncmp(sessionType, "raw", strlen("raw")) == 0) { control -> SessionMode = session_proxy; } else { // // If the session type is not passed or // it is not among the recognized strings, // we assume that the proxy is connected // to the agent. // if (*sessionType != '\0' && (control -> isProtoStep8() == 1 || strncmp(sessionType, "unix-", strlen("unix-")) != 0)) { #ifdef WARNING *logofs << "Loop: WARNING! Unrecognized session type '" << sessionType << "'. Assuming agent session.\n" << logofs_flush; #endif cerr << "Warning" << ": Unrecognized session type '" << sessionType << "'. Assuming agent session.\n"; } control -> SessionMode = session_agent; } #if defined(TEST) || defined(INFO) *logofs << "Loop: Assuming session type '" << DumpSession(control -> SessionMode) << "' with " << "string '" << sessionType << "'.\n" << logofs_flush; #endif // // By default the policy is immediate. Agents // will set a different policy, if they like. // Anyway we need to check if the user has // provided a custom flush policy. // if (usePolicy != -1) { if (usePolicy > 0) { control -> FlushPolicy = policy_deferred; } else { control -> FlushPolicy = policy_immediate; } #if defined(TEST) || defined(INFO) *logofs << "Loop: WARNING! Forcing flush policy to '" << DumpPolicy(control -> FlushPolicy) << ".\n" << logofs_flush; #endif } else { control -> FlushPolicy = policy_immediate; #if defined(TEST) || defined(INFO) *logofs << "Loop: Setting initial flush policy to '" << DumpPolicy(control -> FlushPolicy) << "'.\n" << logofs_flush; #endif } // // Check if the proxy library is run inside // another program providing encryption, as // it is the case of the SSH client. // if (useEncryption != -1) { if (useEncryption > 0) { control -> LinkEncrypted = 1; } else { control -> LinkEncrypted = 0; } } if (control -> LinkEncrypted == 1) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Proxy running as part of an " << "encrypting client.\n" << logofs_flush; #endif } else { #if defined(TEST) || defined(INFO) *logofs << "Loop: Assuming proxy running as a " << "standalone program.\n" << logofs_flush; #endif } // // Check if the system administrator has // enabled the respawn of the client at // the end of session. // if (control -> ProxyMode == proxy_server) { struct stat fileStat; char fileName[DEFAULT_STRING_LENGTH]; snprintf(fileName, DEFAULT_STRING_LENGTH - 1, "%s/share/noexit", control -> SystemPath); *(fileName + DEFAULT_STRING_LENGTH - 1) = '\0'; if (stat(fileName, &fileStat) == 0) { #ifdef TEST *logofs << "Loop: Enabling respawn of client at session shutdown.\n" << logofs_flush; #endif control -> EnableRestartOnShutdown = 1; } } return 1; } int SetStorage() { // // If differential compression is disabled // we don't need a cache at all. // if (control -> LocalDeltaCompression == 0) { control -> ClientTotalStorageSize = 0; control -> ServerTotalStorageSize = 0; } // // Set a a cache size literal. // int size = control -> getUpperStorageSize(); if (size / 1024 > 0) { sprintf(cacheSizeName, "%dk", size / 1024); } else { sprintf(cacheSizeName, "%d", size); } if (control -> ProxyMode == proxy_client) { control -> LocalTotalStorageSize = control -> ClientTotalStorageSize; control -> RemoteTotalStorageSize = control -> ServerTotalStorageSize; } else { control -> LocalTotalStorageSize = control -> ServerTotalStorageSize; control -> RemoteTotalStorageSize = control -> ClientTotalStorageSize; } #ifdef DEBUG *logofs << "Loop: Storage size limit is " << control -> ClientTotalStorageSize << " at client and " << control -> ServerTotalStorageSize << " at server.\n" << logofs_flush; #endif #ifdef DEBUG *logofs << "Loop: Storage local limit set to " << control -> LocalTotalStorageSize << " remote limit set to " << control -> RemoteTotalStorageSize << ".\n" << logofs_flush; #endif // // Never reserve for split store more than // half the memory available for messages. // if (size > 0 && control -> SplitTotalStorageSize > size / 2) { #ifdef TEST *logofs << "Loop: Reducing size of split store to " << size / 2 << " bytes.\n" << logofs_flush; #endif control -> SplitTotalStorageSize = size / 2; } // // Don't load render from persistent // cache if extension is hidden or // not supported by agent. // if (control -> HideRender == 1) { #ifdef TEST *logofs << "Loop: Not loading render extension " << "from persistent cache.\n" << logofs_flush; #endif control -> PersistentCacheLoadRender = 0; } return 1; } int SetShmem() { // // If not set, adjust the size of the shared // memory segment according to size of the // message cache. // if (*shsegSizeName == '\0') { int size = control -> getUpperStorageSize(); const int mega = 1048576; if (size > 0) { if (size <= 1 * mega) { size = 0; } else if (size <= 2 * mega) { size = 524288; } else if (size < 4 * mega) { size = 1048576; } else { size = size / 4; } if (size > 4194304) { size = 4194304; } control -> ShmemClientSize = size; control -> ShmemServerSize = size; } else { // // The delta compression is disabled. // Use a default segment size of 2 MB. // control -> ShmemServerSize = 2 * mega; } } // // Client side shared memory support is // not useful and not implemented. // if (control -> ShmemServerSize >= 524288) { control -> ShmemServer = 1; #if defined(TEST) || defined(INFO) *logofs << "Loop: Set initial shared memory size " << "to " << control -> ShmemServerSize << " bytes.\n" << logofs_flush; #endif } else { #if defined(TEST) || defined(INFO) *logofs << "Loop: Disabled use of the shared memory " << "extension.\n" << logofs_flush; #endif control -> ShmemServer = 0; } return 1; } // // Adjust the pack method according to the // type of the session. // int SetPack() { #ifdef TEST *logofs << "Loop: Setting pack with initial method " << packMethod << " and quality " << packQuality << ".\n" << logofs_flush; #endif // // Check if this is a proxy session and, in // this case, set the pack method to none. // Packed images are not supported by plain // X applications. // if (control -> SessionMode == session_proxy) { #ifdef TEST *logofs << "Loop: WARNING! Disabling pack with proxy session.\n" << logofs_flush; #endif packMethod = PACK_NONE; } // // Adjust the internal settings according // to the newly selected pack method. // ParsePackMethod(packMethod, packQuality); // // Don't load messages from persistent // cache if packed images are disabled. // if (control -> PackMethod == PACK_NONE) { control -> PersistentCacheLoadPacked = 0; #ifdef TEST *logofs << "Loop: Not loading packed images " << "from persistent cache.\n" << logofs_flush; #endif } return 1; } // // Set the disk-based image cache parameters // according to the user's wishes. // int SetImages() { // // Be sure we disable the image cache if we // are connecting to plain X clients. // if (control -> SessionMode == session_proxy) { #ifdef TEST *logofs << "Loop: Disabling image cache with " << "session '" << DumpSession(control -> SessionMode) << "'.\n" << logofs_flush; #endif sprintf(imagesSizeName, "0"); control -> ImageCacheEnableLoad = 0; control -> ImageCacheEnableSave = 0; return 1; } int size = control -> ImageCacheDiskLimit; if (size / 1024 > 0) { sprintf(imagesSizeName, "%dk", size / 1024); } else { sprintf(imagesSizeName, "%d", size); } if (size > 0) { control -> ImageCacheEnableLoad = 1; control -> ImageCacheEnableSave = 1; if (control -> ProxyMode == proxy_server) { if ((control -> ImageCachePath = GetImagesPath()) == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Error getting or creating image cache path.\n" << logofs_flush; #endif cerr << "Error" << ": Error getting or creating image cache path.\n"; HandleCleanup(); } #ifdef TEST *logofs << "Loop: Path of image cache files is '" << control -> ImageCachePath << "'.\n" << logofs_flush; #endif } } else { #ifdef TEST *logofs << "Loop: Disabling the persistent image cache.\n" << logofs_flush; #endif control -> ImageCacheEnableLoad = 0; control -> ImageCacheEnableSave = 0; } return 1; } int SetVersion() { // // Normalize the different proxy versions. // int local = (control -> LocalVersionMajor << 24) | (control -> LocalVersionMinor << 16) | control -> LocalVersionPatch; int remote = (control -> RemoteVersionMajor << 24) | (control -> RemoteVersionMinor << 16) | control -> RemoteVersionPatch; int major = -1; int minor = -1; int patch = -1; if (control -> RemoteVersionMajor <= 1) { // // The remote proxy uses a different // logic to determine the version so // we default to the compatibility // version. // major = control -> CompatVersionMajor; minor = control -> CompatVersionMinor; patch = control -> CompatVersionPatch; #ifdef TEST *logofs << "Loop: Using compatibility version '" << major << "." << minor << "." << patch << "'.\n" << logofs_flush; #endif } else if (control -> LocalVersionMajor > control -> RemoteVersionMajor) { // // We use a more recent version. Let's // negotiate the version based on the // version supported by the remote. // major = control -> RemoteVersionMajor; minor = control -> RemoteVersionMinor; patch = control -> RemoteVersionPatch; #ifdef TEST *logofs << "Loop: Using remote version '" << major << "." << minor << "." << patch << "'.\n" << logofs_flush; #endif } else { // // We support a major version that is // equal or older than the remote. We // assume the smaller version between // the two, including the minor and // the patch numbers. // if (local > remote) { major = control -> RemoteVersionMajor; minor = control -> RemoteVersionMinor; patch = control -> RemoteVersionPatch; #ifdef TEST *logofs << "Loop: Using remote version '" << major << "." << minor << "." << patch << "'.\n" << logofs_flush; #endif } else { major = control -> LocalVersionMajor; minor = control -> LocalVersionMinor; patch = control -> LocalVersionPatch; #ifdef TEST *logofs << "Loop: Using local version '" << major << "." << minor << "." << patch << "'.\n" << logofs_flush; #endif } } // // Handle the 1.5.0 versions. The protocol // step 6 is the minimum supported version. // int step = 0; if (major == 1) { if (minor == 5) { step = 6; } } else if (major == 2) { step = 7; } else if (major == 3) { if (minor >= 2) { step = 10; } else if (minor > 0 || patch > 0) { step = 9; } else { step = 8; } } else if (major > 3) { step = 10; } if (step == 0) { #ifdef PANIC *logofs << "Loop: PANIC! Incompatible remote version " << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch << " with local version " << control -> LocalVersionMajor << "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch << ".\n" << logofs_flush; #endif cerr << "Error" << ": Incompatible remote version " << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch << " with local version " << control -> LocalVersionMajor << "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch << ".\n"; return -1; } #ifdef TEST *logofs << "Loop: Using NX protocol step " << step << ".\n" << logofs_flush; #endif control -> setProtoStep(step); // // Ignore the differences in patch version // and print a warning if the local version // is different or obsolete compared to // the remote. // local &= 0xffff0000; remote &= 0xffff0000; if (local != remote) { #ifdef WARNING *logofs << "Loop: WARNING! Connected to remote version " << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch << " with local version " << control -> LocalVersionMajor << "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch << ".\n" << logofs_flush; #endif cerr << "Warning" << ": Connected to remote version " << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch << " with local version " << control -> LocalVersionMajor << "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch << ".\n" << logofs_flush; } if (local < remote) { cerr << "Warning" << ": Consider checking http://www.nomachine.com/ for updates.\n"; } // // Now that we are aware of the remote // version, let's adjust the options to // be compatible with the remote proxy. // if (control -> ProxyMode == proxy_client) { if (control -> isProtoStep8() == 0) { if (strncmp(sessionType, "shadow", strlen("shadow")) == 0 || strncmp(sessionType, "application", strlen("application")) == 0 || strncmp(sessionType, "console", strlen("console")) == 0 || strncmp(sessionType, "default", strlen("default")) == 0 || strncmp(sessionType, "gnome", strlen("gnome")) == 0 || strncmp(sessionType, "kde", strlen("kde")) == 0 || strncmp(sessionType, "cde", strlen("cde")) == 0 || strncmp(sessionType, "xdm", strlen("xdm")) == 0) { #if defined(TEST) || defined(INFO) *logofs << "Loop: WARNING! Prepending 'unix-' to the " << "name of the session.\n" << logofs_flush; #endif char buffer[DEFAULT_STRING_LENGTH]; snprintf(buffer, DEFAULT_STRING_LENGTH - 1, "unix-%s", sessionType); strcpy(sessionType, buffer); } } // // Check if the remote is able to handle // the selected pack method. // if (control -> isProtoStep8() == 0) { if (packMethod == PACK_ADAPTIVE || packMethod == PACK_LOSSY) { #ifdef TEST *logofs << "Loop: WARNING! Assuming a lossy encoding with " << "an old proxy version.\n" << logofs_flush; #endif packMethod = PACK_JPEG_16M_COLORS; } else if (packMethod == PACK_LOSSLESS) { #ifdef TEST *logofs << "Loop: WARNING! Assuming a lossless encoding with " << "an old proxy version.\n" << logofs_flush; #endif if (control -> isProtoStep7() == 1) { packMethod = PACK_RLE_16M_COLORS; } else { packMethod = PACK_PNG_16M_COLORS; } } } // // If the remote doesn't support the // selected method use something that // is compatible. // if ((packMethod == PACK_RGB_16M_COLORS || packMethod == PACK_RLE_16M_COLORS || packMethod == PACK_BITMAP_16M_COLORS) && control -> isProtoStep7() == 0) { #ifdef TEST *logofs << "Loop: WARNING! Setting the pack method to '" << PACK_PNG_16M_COLORS << "' with '" << packMethod << "' unsupported.\n" << logofs_flush; #endif packMethod = PACK_PNG_16M_COLORS; packQuality = 9; } else if (packMethod == PACK_BITMAP_16M_COLORS && control -> isProtoStep8() == 0) { #ifdef TEST *logofs << "Loop: WARNING! Setting the pack method to '" << PACK_RLE_16M_COLORS << "' with '" << packMethod << "' unsupported.\n" << logofs_flush; #endif packMethod = PACK_RLE_16M_COLORS; packQuality = 9; } // // Update the pack method name. // ParsePackMethod(packMethod, packQuality); } // // At the moment the image cache is not used by the // agent but we need to take care of the compatibi- // lity with old versions. Proxy versions older than // the 3.0.0 assume that it is enabled and will send // specific bits as part of the encoding. Conversely, // it is advisable to disable the cache right now. // By not enabling the image cache, the house-keep- // ing process will only take care of cleaning up // the "cache-" directories. // if (control -> isProtoStep8() == 1) { #ifdef TEST *logofs << "Loop: Disabling image cache with protocol " << "step '" << control -> getProtoStep() << "'.\n" << logofs_flush; #endif sprintf(imagesSizeName, "0"); control -> ImageCacheEnableLoad = 0; control -> ImageCacheEnableSave = 0; } return 1; } // // Identify the requested link settings // and update the control parameters // accordingly. // int SetLink() { #ifdef TEST *logofs << "Loop: Setting link with initial value " << linkSpeedName << ".\n" << logofs_flush; #endif if (*linkSpeedName == '\0') { strcpy(linkSpeedName, "lan"); } #ifdef TEST *logofs << "Loop: Link speed is " << linkSpeedName << ".\n" << logofs_flush; #endif if (strcasecmp(linkSpeedName, "modem") == 0) { SetLinkModem(); } else if (strcasecmp(linkSpeedName, "isdn") == 0) { SetLinkIsdn(); } else if (strcasecmp(linkSpeedName, "adsl") == 0) { SetLinkAdsl(); } else if (strcasecmp(linkSpeedName, "wan") == 0) { SetLinkWan(); } else if (strcasecmp(linkSpeedName, "lan") == 0) { SetLinkLan(); } else { return -1; } // // Set TCP_NODELAY according to the user's // wishes. // if (useNoDelay != -1) { control -> OptionProxyClientNoDelay = useNoDelay; control -> OptionProxyServerNoDelay = useNoDelay; } // // Select the image compression method. // if (packMethod == -1) { packMethod = control -> PackMethod; } if (packQuality == -1) { packQuality = control -> PackQuality; } if (ParsePackMethod(packMethod, packQuality) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Unrecognized pack method id " << packMethod << " with quality " << packQuality << ".\n" << logofs_flush; #endif cerr << "Error" << ": Unrecognized pack method id " << packMethod << " with quality " << packQuality << ".\n"; HandleCleanup(); } // // Check if the user disabled the ability // to generate simple replies at the client // side. // if (control -> SessionMode == session_proxy) { if (useTaint != -1) { control -> TaintReplies = (useTaint == 1); } else { #ifdef WARNING *logofs << "Loop: WARNING! Forcing taint of replies " << "with a proxy session.\n" << logofs_flush; #endif control -> TaintReplies = 1; } } else { // // There is no need to taint the // replies if we have an agent. // control -> TaintReplies = 0; } // // Be sure that the requests needing a reply // are flushed immediately. Normal X clients // use so many replies to make the queuing // completely useless. // if (control -> SessionMode == session_proxy) { #ifdef WARNING *logofs << "Loop: WARNING! Forcing flush on priority " << "with a proxy session.\n" << logofs_flush; #endif control -> FlushPriority = 1; } return 1; } // // Parameters for MODEM 28.8/33.6/56 Kbps. // int SetLinkModem() { #ifdef TEST *logofs << "Loop: Setting parameters for MODEM.\n" << logofs_flush; #endif control -> LinkMode = LINK_TYPE_MODEM; control -> TokenSize = 256; control -> TokenLimit = 24; control -> SplitMode = 1; control -> SplitTotalSize = 128; control -> SplitTotalStorageSize = 1048576; control -> SplitTimeout = 50; control -> MotionTimeout = 50; control -> IdleTimeout = 50; control -> PackMethod = PACK_ADAPTIVE; control -> PackQuality = 3; return 1; } // // Parameters for ISDN 64/128 Kbps. // int SetLinkIsdn() { #ifdef TEST *logofs << "Loop: Setting parameters for ISDN.\n" << logofs_flush; #endif control -> LinkMode = LINK_TYPE_ISDN; control -> TokenSize = 384; control -> TokenLimit = 24; control -> SplitMode = 1; control -> SplitTotalSize = 128; control -> SplitTotalStorageSize = 1048576; control -> SplitTimeout = 50; control -> MotionTimeout = 20; control -> IdleTimeout = 50; control -> PackMethod = PACK_ADAPTIVE; control -> PackQuality = 5; return 1; } // // Parameters for ADSL 256 Kbps. // int SetLinkAdsl() { #ifdef TEST *logofs << "Loop: Setting parameters for ADSL.\n" << logofs_flush; #endif control -> LinkMode = LINK_TYPE_ADSL; control -> TokenSize = 512; control -> TokenLimit = 24; control -> SplitMode = 1; control -> SplitTotalSize = 128; control -> SplitTotalStorageSize = 1048576; control -> SplitTimeout = 50; control -> MotionTimeout = 10; control -> IdleTimeout = 50; control -> PackMethod = PACK_ADAPTIVE; control -> PackQuality = 7; return 1; } // // Parameters for XDSL/FDDI/ATM 1/2/34 Mbps WAN. // int SetLinkWan() { #ifdef TEST *logofs << "Loop: Setting parameters for WAN.\n" << logofs_flush; #endif control -> LinkMode = LINK_TYPE_WAN; control -> TokenSize = 768; control -> TokenLimit = 24; control -> SplitMode = 1; control -> SplitTotalSize = 128; control -> SplitTotalStorageSize = 1048576; control -> SplitTimeout = 50; control -> MotionTimeout = 5; control -> IdleTimeout = 50; control -> PackMethod = PACK_ADAPTIVE; control -> PackQuality = 9; return 1; } // // Parameters for LAN 10/100 Mbps. // int SetLinkLan() { #ifdef TEST *logofs << "Loop: Setting parameters for LAN.\n" << logofs_flush; #endif control -> LinkMode = LINK_TYPE_LAN; control -> TokenSize = 1536; control -> TokenLimit = 24; control -> SplitMode = 1; control -> SplitTotalSize = 128; control -> SplitTotalStorageSize = 1048576; control -> SplitTimeout = 50; control -> MotionTimeout = 0; control -> IdleTimeout = 50; control -> PackMethod = PACK_ADAPTIVE; control -> PackQuality = 9; return 1; } // // Identify the requested link type and set // the control parameters accordingly. // int SetCompression() { if (strcasecmp(linkSpeedName, "modem") == 0) { SetCompressionModem(); } else if (strcasecmp(linkSpeedName, "isdn") == 0) { SetCompressionIsdn(); } else if (strcasecmp(linkSpeedName, "adsl") == 0) { SetCompressionAdsl(); } else if (strcasecmp(linkSpeedName, "wan") == 0) { SetCompressionWan(); } else if (strcasecmp(linkSpeedName, "lan") == 0) { SetCompressionLan(); } else { return -1; } if (control -> LocalDeltaCompression < 0) { control -> LocalDeltaCompression = 1; } // // If we didn't set remote delta compression // (as it should always be the case at client // side) assume value of local side. // if (control -> RemoteDeltaCompression < 0) { control -> RemoteDeltaCompression = control -> LocalDeltaCompression; } // // If we didn't set remote compression levels // assume values of local side. // if (control -> RemoteStreamCompression < 0) { control -> RemoteStreamCompressionLevel = control -> LocalStreamCompressionLevel; if (control -> RemoteStreamCompressionLevel > 0) { control -> RemoteStreamCompression = 1; } else { control -> RemoteStreamCompression = 0; } } if (control -> RemoteDataCompression < 0) { control -> RemoteDataCompressionLevel = control -> LocalDataCompressionLevel; if (control -> RemoteDataCompressionLevel > 0) { control -> RemoteDataCompression = 1; } else { control -> RemoteDataCompression = 0; } } return 1; } // // Compression for MODEM. // int SetCompressionModem() { if (control -> LocalDataCompression < 0) { control -> LocalDataCompression = 1; control -> LocalDataCompressionLevel = 1; } if (control -> LocalDataCompressionThreshold < 0) { control -> LocalDataCompressionThreshold = 32; } if (control -> LocalStreamCompression < 0) { control -> LocalStreamCompression = 1; control -> LocalStreamCompressionLevel = 9; } return 1; } // // Compression for ISDN. // int SetCompressionIsdn() { if (control -> LocalDataCompression < 0) { control -> LocalDataCompression = 1; control -> LocalDataCompressionLevel = 1; } if (control -> LocalDataCompressionThreshold < 0) { control -> LocalDataCompressionThreshold = 32; } if (control -> LocalStreamCompression < 0) { control -> LocalStreamCompression = 1; control -> LocalStreamCompressionLevel = 6; } return 1; } // // Compression for ADSL. // int SetCompressionAdsl() { if (control -> LocalDataCompression < 0) { control -> LocalDataCompression = 1; control -> LocalDataCompressionLevel = 1; } if (control -> LocalDataCompressionThreshold < 0) { control -> LocalDataCompressionThreshold = 32; } if (control -> LocalStreamCompression < 0) { control -> LocalStreamCompression = 1; control -> LocalStreamCompressionLevel = 4; } return 1; } // // Compression for WAN. // int SetCompressionWan() { if (control -> LocalDataCompression < 0) { control -> LocalDataCompression = 1; control -> LocalDataCompressionLevel = 1; } if (control -> LocalDataCompressionThreshold < 0) { control -> LocalDataCompressionThreshold = 32; } if (control -> LocalStreamCompression < 0) { control -> LocalStreamCompression = 1; control -> LocalStreamCompressionLevel = 1; } return 1; } // // Compression for LAN. // int SetCompressionLan() { // // Disable delta compression if not // explicitly enabled. // if (control -> LocalDeltaCompression < 0) { control -> LocalDeltaCompression = 0; } if (control -> LocalDataCompression < 0) { control -> LocalDataCompression = 0; control -> LocalDataCompressionLevel = 0; } if (control -> LocalDataCompressionThreshold < 0) { control -> LocalDataCompressionThreshold = 0; } if (control -> LocalStreamCompression < 0) { control -> LocalStreamCompression = 0; control -> LocalStreamCompressionLevel = 0; } return 1; } int SetLimits() { // // Check if the user requested strict // control flow parameters. // if (useStrict == 1) { #if defined(TEST) || defined(INFO) *logofs << "Loop: LIMIT! Decreasing the token limit " << "to " << control -> TokenLimit / 2 << " with option 'strict'.\n" << logofs_flush; #endif control -> TokenLimit /= 2; } #ifdef STRICT control -> TokenLimit = 1; #if defined(TEST) || defined(INFO) *logofs << "Loop: WARNING! LIMIT! Setting the token limit " << "to " << control -> TokenLimit << " to simulate the proxy congestion.\n" << logofs_flush; #endif #endif // // Reduce the size of the log file. // #ifdef QUOTA control -> FileSizeLimit = 8388608; #endif // // Check the bitrate limits. // if (control -> LocalBitrateLimit == -1) { if (control -> ProxyMode == proxy_client) { control -> LocalBitrateLimit = control -> ClientBitrateLimit; } else { control -> LocalBitrateLimit = control -> ServerBitrateLimit; } } #if defined(TEST) || defined(INFO) *logofs << "Loop: LIMIT! Setting client bitrate limit " << "to " << control -> ClientBitrateLimit << " server bitrate limit to " << control -> ServerBitrateLimit << " with local limit " << control -> LocalBitrateLimit << ".\n" << logofs_flush; #endif return 1; } // // These functions are used to parse literal // values provided by the user and set the // control parameters accordingly. // int ParseCacheOption(const char *opt) { int size = ParseArg("", "cache", opt); if (size < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid value '" << opt << "' for option 'cache'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid value '" << opt << "' for option 'cache'.\n"; return -1; } #ifdef TEST *logofs << "Loop: Setting size of cache to " << size << " bytes.\n" << logofs_flush; #endif control -> ClientTotalStorageSize = size; control -> ServerTotalStorageSize = size; strcpy(cacheSizeName, opt); if (size == 0) { #ifdef WARNING *logofs << "Loop: WARNING! Disabling NX delta compression.\n" << logofs_flush; #endif control -> LocalDeltaCompression = 0; #ifdef WARNING *logofs << "Loop: WARNING! Disabling use of NX persistent cache.\n" << logofs_flush; #endif control -> PersistentCacheEnableLoad = 0; control -> PersistentCacheEnableSave = 0; } return 1; } int ParseImagesOption(const char *opt) { int size = ParseArg("", "images", opt); if (size < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid value '" << opt << "' for option 'images'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid value '" << opt << "' for option 'images'.\n"; return -1; } #ifdef TEST *logofs << "Loop: Setting size of images cache to " << size << " bytes.\n" << logofs_flush; #endif control -> ImageCacheDiskLimit = size; strcpy(imagesSizeName, opt); return 1; } int ParseShmemOption(const char *opt) { int size = ParseArg("", "shseg", opt); if (size < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid value '" << opt << "' for option 'shseg'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid value '" << opt << "' for option 'shseg'.\n"; return -1; } control -> ShmemClientSize = size; control -> ShmemServerSize = size; #if defined(TEST) || defined(INFO) *logofs << "Loop: Set shared memory size to " << control -> ShmemServerSize << " bytes.\n" << logofs_flush; #endif strcpy(shsegSizeName, opt); return 1; } int ParseBitrateOption(const char *opt) { int bitrate = ParseArg("", "limit", opt); if (bitrate < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid value '" << opt << "' for option 'limit'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid value '" << opt << "' for option 'limit'.\n"; return -1; } strcpy(bitrateLimitName, opt); if (bitrate == 0) { #ifdef TEST *logofs << "Loop: Disabling bitrate limit on proxy link.\n" << logofs_flush; #endif control -> LocalBitrateLimit = 0; } else { #ifdef TEST *logofs << "Loop: Setting bitrate to " << bitrate << " bits per second.\n" << logofs_flush; #endif // // Internal representation is in bytes // per second. // control -> LocalBitrateLimit = bitrate >> 3; } return 1; } int ParseHostOption(const char *opt, char *host, int &port) { #ifdef TEST *logofs << "Loop: Trying to parse options string '" << opt << "' as a remote NX host.\n" << logofs_flush; #endif if (opt == NULL || *opt == '\0') { #ifdef PANIC *logofs << "Loop: PANIC! No host parameter provided.\n" << logofs_flush; #endif return 0; } else if (strlen(opt) >= DEFAULT_STRING_LENGTH) { #ifdef PANIC *logofs << "Loop: PANIC! Host parameter exceeds length of " << DEFAULT_STRING_LENGTH << " characters.\n" << logofs_flush; #endif return 0; } // // Look for a host name followed // by a colon followed by port. // int newPort = port; const char *separator = rindex(opt, ':'); if (separator != NULL) { const char *check = separator + 1; while (*check != '\0' && *check != ',' && *check != '=' && isdigit(*check) != 0) { check++; } newPort = atoi(separator + 1); if (newPort < 0 || *check != '\0') { #ifdef TEST *logofs << "Loop: Can't identify remote NX port in string '" << separator << "'.\n" << logofs_flush; #endif return 0; } } else if (newPort < 0) { // // Complain if port was not passed // by other means. // #ifdef TEST *logofs << "Loop: Can't identify remote NX port in string '" << opt << "'.\n" << logofs_flush; #endif return 0; } else { separator = opt + strlen(opt); } char newHost[DEFAULT_STRING_LENGTH] = { 0 }; strncpy(newHost, opt, strlen(opt) - strlen(separator)); *(newHost + strlen(opt) - strlen(separator)) = '\0'; const char *check = newHost; while (*check != '\0' && *check != ',' && *check != '=') { check++; } if (*check != '\0') { #ifdef TEST *logofs << "Loop: Can't identify remote NX host in string '" << newHost << "'.\n" << logofs_flush; #endif return 0; } else if (*acceptHost != '\0') { #ifdef PANIC *logofs << "Loop: PANIC! Can't manage to connect and accept connections " << "at the same time.\n" << logofs_flush; *logofs << "Loop: PANIC! Refusing remote NX host with string '" << opt << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Can't manage to connect and accept connections " << "at the same time.\n"; cerr << "Error" << ": Refusing remote NX host with string '" << opt << "'.\n"; return -1; } if (*host != '\0' && strcmp(host, newHost) != 0) { #ifdef WARNING *logofs << "Loop: WARNING! Overriding remote NX host '" << host << "' with new value '" << newHost << "'.\n" << logofs_flush; #endif } strcpy(host, newHost); if (port != -1 && port != newPort) { #ifdef WARNING *logofs << "Loop: WARNING! Overriding remote NX port '" << port << "' with new value '" << newPort << "'.\n" << logofs_flush; #endif } #ifdef TEST *logofs << "Loop: Parsed options string '" << opt << "' with host '" << newHost << "' and port '" << newPort << "'.\n" << logofs_flush; #endif port = newPort; return 1; } int ParseFontPath(char *path) { char oldPath[DEFAULT_STRING_LENGTH]; strcpy(oldPath, path); if (path == NULL || *path == '\0' || strcmp(path, "0") == 0) { return 0; } #ifdef TEST *logofs << "Loop: Parsing font server option '" << path << "'.\n" << logofs_flush; #endif // // Convert the value to our default port. // if (strcmp(fontPort, "1") == 0) { if (control -> ProxyMode == proxy_server) { snprintf(fontPort, DEFAULT_STRING_LENGTH - 1, "%d", DEFAULT_NX_FONT_PORT_OFFSET + proxyPort); } else { // // Let the client use the well-known // "unix/:7100" font path. // snprintf(fontPort, DEFAULT_STRING_LENGTH - 1, "unix/:7100"); } } // // Check if a simple numaric value was given. // if (atoi(path) > 0) { #ifdef TEST *logofs << "Loop: Assuming numeric TCP port '" << atoi(path) << "' for font server.\n" << logofs_flush; #endif return 1; } // // Let's assume that a port specification "unix/:7100" // corresponds to "$TEMP/.font-unix/fs7100" and a port // "unix/:-1" corresponds to "$TEMP/.font-unix/fs-1". // if (strncmp("unix/:", path, 6) == 0) { snprintf(path, DEFAULT_STRING_LENGTH - 1, "%s/.font-unix/fs%s", control -> TempPath, oldPath + 6); *(path + DEFAULT_STRING_LENGTH - 1) = '\0'; #ifdef TEST *logofs << "Loop: Assuming Unix socket '" << path << "' for font server.\n" << logofs_flush; #endif } else if (strncmp("tcp/:", path, 5) == 0) { snprintf(path, DEFAULT_STRING_LENGTH - 1, "%d", atoi(oldPath + 5)); *(path + DEFAULT_STRING_LENGTH - 1) = '\0'; if (atoi(path) <= 0) { goto ParseFontPathError; } #ifdef TEST *logofs << "Loop: Assuming TCP port '" << atoi(path) << "' for font server.\n" << logofs_flush; #endif } else { // // Accept an absolute file path as // a valid Unix socket. // if (*path != '/') { goto ParseFontPathError; } #ifdef TEST *logofs << "Loop: Assuming Unix socket '" << path << "' for font server.\n" << logofs_flush; #endif } return 1; ParseFontPathError: #ifdef TEST *logofs << "Loop: Unable to determine the font server " << "port in string '" << path << "'.\n" << logofs_flush; #endif return -1; } int ParseListenOption(int &address) { if (*listenHost == '\0') { // // On the X client side listen on any address. // On the X server side listen to the forwarder // on localhost. // if (control -> ProxyMode == proxy_server) { address = (int) inet_addr("127.0.0.1"); } else { address = htonl(INADDR_ANY); } } else { address = inet_addr(listenHost); } return 1; } int OpenLogFile(char *name, ostream *&stream) { if (name == NULL || *name == '\0') { #ifdef TEST *logofs << "Loop: WARNING! No name provided for output. Using standard error.\n" << logofs_flush; #endif if (stream == NULL) { stream = &cerr; } return 1; } if (stream == NULL || stream == &cerr) { if (*name != '/' && *name != '.') { char *filePath = GetSessionPath(); if (filePath == NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Cannot determine directory of NX session file.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot determine directory of NX session file.\n"; return -1; } if (strlen(filePath) + strlen("/") + strlen(name) + 1 > DEFAULT_STRING_LENGTH) { #ifdef PANIC *logofs << "Loop: PANIC! Full name of NX file '" << name << " would exceed length of " << DEFAULT_STRING_LENGTH << " characters.\n" << logofs_flush; #endif cerr << "Error" << ": Full name of NX file '" << name << " would exceed length of " << DEFAULT_STRING_LENGTH << " characters.\n"; return -1; } char *file = new char[strlen(filePath) + strlen("/") + strlen(name) + 1]; // // Transform name in a fully qualified name. // strcpy(file, filePath); strcat(file, "/"); strcat(file, name); strcpy(name, file); delete [] filePath; delete [] file; } mode_t fileMode = umask(0077); for (;;) { if ((stream = new ofstream(name, ios::app)) != NULL) { break; } usleep(200000); } umask(fileMode); } else { #ifdef PANIC *logofs << "Loop: PANIC! Bad stream provided for output.\n" << logofs_flush; #endif cerr << "Error" << ": Bad stream provided for output.\n"; return -1; } return 1; } int ReopenLogFile(char *name, ostream *&stream, int limit) { if (*name != '\0' && limit >= 0) { struct stat fileStat; if (limit > 0) { // // This is used for the log file, if the // size exceeds the limit. // if (stat(name, &fileStat) != 0) { #ifdef WARNING *logofs << "Loop: WARNING! Can't get stats of file '" << name << "'. Error is " << EGET() << " '" << ESTR() << "'.\n" << logofs_flush; #endif return 0; } else if (fileStat.st_size < (long) limit) { return 0; } } #ifdef TEST *logofs << "Loop: Deleting file '" << name << "' with size " << fileStat.st_size << ".\n" << logofs_flush; #endif // // Create a new stream over the previous // file. Trying to delete the file fails // to work on recent Cygwin installs. // *stream << flush; delete stream; mode_t fileMode = umask(0077); for (;;) { if ((stream = new ofstream(name, ios::out)) != NULL) { break; } usleep(200000); } umask(fileMode); #ifdef TEST *logofs << "Loop: Reopened file '" << name << "'.\n" << logofs_flush; #endif } return 1; } void PrintProcessInfo() { if (agent == NULL) { cerr << "\nNXPROXY - Version " << control -> LocalVersionMajor << "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch << "\n\n"; cerr << "Copyright (C) 2001, 2011 NoMachine.\n" << "See http://www.nomachine.com/ for more information.\n\n"; } // // People get confused by the fact that client // mode is running on NX server and viceversa. // Let's adopt an user-friendly naming conven- // tion here. // cerr << "Info: Proxy running in " << (control -> ProxyMode == proxy_client ? "server" : "client") << " mode with pid '" << getpid() << "'.\n"; if (agent == NULL) { cerr << "Session" << ": Starting session at '" << strTimestamp() << "'.\n"; } #ifdef TEST if (*errorsFileName != '\0') { cerr << "Info" << ": Using errors file '" << errorsFileName << "'.\n"; } if (*statsFileName != '\0') { cerr << "Info" << ": Using stats file '" << statsFileName << "'.\n"; } #endif } void PrintConnectionInfo() { cerr << "Info" << ": Using " << linkSpeedName << " link parameters " << control -> TokenSize << "/" << control -> TokenLimit << "/" << control -> FlushPolicy + 1 << "/" << control -> FlushPriority << ".\n"; if (control -> ProxyMode == proxy_client) { cerr << "Info" << ": Using agent parameters " << control -> PingTimeout << "/" << control -> MotionTimeout << "/" << control -> IdleTimeout << "/" << control -> TaintReplies << "/" << control -> HideRender << ".\n"; } if (control -> LocalDeltaCompression == 1) { cerr << "Info" << ": Using cache parameters " << control -> MinimumMessageSize << "/" << control -> MaximumMessageSize / 1024 << "KB" << "/" << control -> ClientTotalStorageSize / 1024 << "KB" << "/" << control -> ServerTotalStorageSize / 1024 << "KB" << ".\n"; } if (control -> ImageCacheEnableLoad == 1 || control -> ImageCacheEnableSave == 1) { cerr << "Info" << ": Using image streaming parameters " << control -> SplitTimeout << "/" << control -> SplitTotalSize << "/" << control -> SplitTotalStorageSize / 1024 << "KB" << "/" << control -> SplitDataThreshold << "/" << control -> SplitDataPacketLimit << ".\n"; cerr << "Info" << ": Using image cache parameters " << control -> ImageCacheEnableLoad << "/" << control -> ImageCacheEnableSave << "/" << control -> ImageCacheDiskLimit / 1024 << "KB" << ".\n"; } cerr << "Info" << ": Using pack method '" << packMethodName << "' with session '" << sessionType << "'.\n"; if (*productName != '\0') { cerr << "Info" << ": Using product '" << productName << "'.\n" << logofs_flush; } if (control -> LocalDeltaCompression == 0) { cerr << "Info" << ": Not using NX delta compression.\n"; } if (control -> LocalDataCompression == 1 || control -> RemoteDataCompression == 1) { cerr << "Info" << ": Using ZLIB data compression " << control -> LocalDataCompressionLevel << "/" << control -> RemoteDataCompressionLevel << "/" << control -> LocalDataCompressionThreshold << ".\n"; } else { cerr << "Info" << ": Not using ZLIB data compression.\n"; } if (control -> LocalStreamCompression == 1 || control -> RemoteStreamCompression == 1) { cerr << "Info" << ": Using ZLIB stream compression " << control -> LocalStreamCompressionLevel << "/" << control -> RemoteStreamCompressionLevel << ".\n"; } else { cerr << "Info" << ": Not using ZLIB stream compression.\n"; } if (control -> LocalBitrateLimit > 0) { cerr << "Info" << ": Using bandwidth limit of " << bitrateLimitName << " bits per second.\n"; } if (control -> PersistentCacheName != NULL) { cerr << "Info" << ": Using cache file '" << control -> PersistentCachePath << "/" << control -> PersistentCacheName << "'.\n"; } else { if (control -> PersistentCacheEnableLoad == 0 || control -> LocalDeltaCompression == 0) { cerr << "Info" << ": Not using a persistent cache.\n"; } else { cerr << "Info" << ": No suitable cache file found.\n"; } } if (control -> ProxyMode == proxy_client && (useUnixSocket > 0 || useTcpSocket > 0 || useAgentSocket > 0)) { cerr << "Info" << ": Listening to X11 connections " << "on display ':" << xPort << "'.\n"; } else if (control -> ProxyMode == proxy_server) { cerr << "Info" << ": Forwarding X11 connections " << "to display '" << displayHost << "'.\n"; } if (control -> ProxyMode == proxy_client && useCupsSocket > 0 && cupsPort > 0) { cerr << "Info" << ": Listening to CUPS connections " << "on port '" << cupsPort << "'.\n"; } else if (control -> ProxyMode == proxy_server && cupsPort > 0) { cerr << "Info" << ": Forwarding CUPS connections " << "to port '" << cupsPort << "'.\n"; } if (control -> ProxyMode == proxy_client && useAuxSocket > 0 && auxPort > 0) { cerr << "Info" << ": Listening to auxiliary X11 connections " << "on port '" << auxPort << "'.\n"; } else if (control -> ProxyMode == proxy_server && auxPort > 0) { cerr << "Info" << ": Forwarding auxiliary X11 connections " << "to display '" << displayHost << "'.\n"; } if (control -> ProxyMode == proxy_client && useSmbSocket > 0 && smbPort > 0) { cerr << "Info" << ": Listening to SMB connections " << "on port '" << smbPort << "'.\n"; } else if (control -> ProxyMode == proxy_server && smbPort > 0) { cerr << "Info" << ": Forwarding SMB connections " << "to port '" << smbPort << "'.\n"; } if (control -> ProxyMode == proxy_client && useMediaSocket > 0 && mediaPort > 0) { cerr << "Info" << ": Listening to multimedia connections " << "on port '" << mediaPort << "'.\n"; } else if (control -> ProxyMode == proxy_server && mediaPort > 0) { cerr << "Info" << ": Forwarding multimedia connections " << "to port '" << mediaPort << "'.\n"; } if (control -> ProxyMode == proxy_client && useHttpSocket > 0 && httpPort > 0) { cerr << "Info" << ": Listening to HTTP connections " << "on port '" << httpPort << "'.\n"; } else if (control -> ProxyMode == proxy_server && httpPort > 0) { cerr << "Info" << ": Forwarding HTTP connections " << "to port '" << httpPort << "'.\n"; } if (control -> ProxyMode == proxy_server && useFontSocket > 0 && *fontPort != '\0') { cerr << "Info" << ": Listening to font server connections " << "on port '" << fontPort << "'.\n"; } else if (control -> ProxyMode == proxy_client && *fontPort != '\0') { cerr << "Info" << ": Forwarding font server connections " << "to port '" << fontPort << "'.\n"; } if (useSlaveSocket > 0 && slavePort > 0) { cerr << "Info" << ": Listening to slave connections " << "on port '" << slavePort << "'.\n"; } } void PrintVersionInfo() { cerr << "NXPROXY - " << "Version " << control -> LocalVersionMajor << "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch; cerr << endl; } void PrintCopyrightInfo() { cerr << endl; PrintVersionInfo(); cerr << endl; cerr << GetCopyrightInfo(); // // Print third party's copyright info. // cerr << endl; cerr << GetOtherCopyrightInfo(); cerr << endl; } void PrintOptionIgnored(const char *type, const char *name, const char *value) { if (control -> ProxyMode == proxy_server) { #ifdef WARNING *logofs << "Loop: WARNING! Ignoring " << type << " option '" << name << "' with value '" << value << "' at " << "NX client side.\n" << logofs_flush; #endif cerr << "Warning" << ": Ignoring " << type << " option '" << name << "' with value '" << value << "' at " << "NX client side.\n"; } else { #ifdef WARNING *logofs << "Loop: WARNING! Ignoring " << type << " option '" << name << "' with value '" << value << "' at " << "NX server side.\n" << logofs_flush; #endif cerr << "Warning" << ": Ignoring " << type << " option '" << name << "' with value '" << value << "' at " << "NX server side.\n"; } } const char *GetOptions(const char *options) { if (options != NULL) { if (strncasecmp(options, "nx/nx,", 6) != 0 && strncasecmp(options, "nx,", 3) != 0 && strncasecmp(options, "nx:", 3) != 0) { #ifdef TEST *logofs << "Loop: PANIC! Display options string '" << options << "' must start with 'nx' or 'nx/nx' prefix.\n" << logofs_flush; #endif cerr << "Error" << ": Display options string '" << options << "' must start with 'nx' or 'nx/nx' prefix.\n"; HandleCleanup(); } } else { options = getenv("DISPLAY"); } return options; } const char *GetArg(int &argi, int argc, const char **argv) { // // Skip "-" and flag character. // const char *arg = argv[argi] + 2; if (*arg == 0) { if (argi + 1 == argc) { return NULL; } else { argi++; return (*argv[argi] == '-' ? NULL : argv[argi]); } } else { return (*arg == '-' ? NULL : arg); } } int CheckArg(const char *type, const char *name, const char *value) { #ifdef TEST *logofs << "Loop: Parsing " << type << " option '" << name << "' with value '" << (value ? value : "(null)") << "'.\n" << logofs_flush; #endif if (value == NULL || strstr(value, "=") != NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Error in " << type << " option '" << name << "'. No value found.\n" << logofs_flush; #endif cerr << "Error" << ": Error in " << type << " option '" << name << "'. No value found.\n"; return -1; } else if (strstr(name, ",") != NULL) { #ifdef PANIC *logofs << "Loop: PANIC! Parse error at " << type << " option '" << name << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Parse error at " << type << " option '" << name << "'.\n"; return -1; } else if (strlen(value) >= DEFAULT_STRING_LENGTH) { #ifdef PANIC *logofs << "Loop: PANIC! Value '" << value << "' of " << type << " option '" << name << "' exceeds length of " << DEFAULT_STRING_LENGTH << " characters.\n" << logofs_flush; #endif cerr << "Error" << ": Value '" << value << "' of " << type << " option '" << name << "' exceeds length of " << DEFAULT_STRING_LENGTH << " characters.\n"; return -1; } return 1; } int ParseArg(const char *type, const char *name, const char *value) { if (strcasecmp(value, "0") == 0) { return 0; } // // Find the base factor. // double base; const char *id = value + strlen(value) - 1; if (strcasecmp(id, "g") == 0) { base = 1024 * 1024 * 1024; } else if (strcasecmp(id, "m") == 0) { base = 1024 * 1024; } else if (strcasecmp(id, "k") == 0) { base = 1024; } else if (strcasecmp(id, "b") == 0 || isdigit(*id) == 1) { base = 1; } else { return -1; } char *string = new char[strlen(value)]; strncpy(string, value, strlen(value) - 1); *(string + (strlen(value) - 1)) = '\0'; #ifdef TEST *logofs << "Loop: Parsing integer option '" << name << "' from string '" << string << "' with base set to "; switch (tolower(*id)) { case 'k': case 'm': case 'g': { *logofs << (char) toupper(*id); } break; } *logofs << ".\n" << logofs_flush; #endif double result = atof(string) * base; if (result < 0 || result > (((unsigned) -1) >> 1)) { delete [] string; return -1; } delete [] string; #ifdef TEST *logofs << "Loop: Integer option parsed to '" << (int) result << "'.\n" << logofs_flush; #endif return (int) result; } int ValidateArg(const char *type, const char *name, const char *value) { int number = atoi(value); if (number < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Invalid " << type << " option '" << name << "' with value '" << value << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid " << type << " option '" << name << "' with value '" << value << "'.\n"; HandleCleanup(); } return number; } int LowercaseArg(const char *type, const char *name, char *value) { char *next = value; while (*next != '\0') { *next = tolower(*next); next++; } return 1; } int CheckSignal(int signal) { // // Return 1 if the signal needs to be handled // by the proxy, 2 if the signal just needs to // be blocked to avoid interrupting a system // call. // switch (signal) { case SIGCHLD: case SIGUSR1: case SIGUSR2: case SIGHUP: case SIGINT: case SIGTERM: case SIGPIPE: case SIGALRM: { return 1; } case SIGVTALRM: case SIGWINCH: case SIGIO: case SIGTSTP: case SIGTTIN: case SIGTTOU: { return 2; } default: { #ifdef __CYGWIN32__ // // This signal can be raised by the Cygwin // library. // if (signal == 12) { return 1; } #endif return 0; } } } static void PrintUsageInfo(const char *option, int error) { if (error == 1) { cerr << "Error" << ": Invalid command line option '" << option << "'.\n"; } cerr << GetUsageInfo(); if (error == 1) { cerr << "Error" << ": NX transport initialization failed.\n"; } } static void handleCheckSessionInLoop() { // // Check if we completed the shutdown procedure // and the remote confirmed the shutdown. The // tear down should be always initiated by the // agent, but the X server side may unilateral- // ly shut down the link without our permission. // if (proxy -> getShutdown() > 0) { #ifdef TEST *logofs << "Loop: End of NX transport requested " << "by remote.\n" << logofs_flush; #endif handleTerminatingInLoop(); if (control -> ProxyMode == proxy_server) { #ifdef TEST *logofs << "Loop: Bytes received so far are " << (unsigned long long) statistics -> getBytesIn() << ".\n" << logofs_flush; #endif if (statistics -> getBytesIn() < 1024) { cerr << "Info" << ": Your session was closed before reaching " << "a usable state.\n"; cerr << "Info" << ": This can be due to the local X server " << "refusing access to the client.\n"; cerr << "Info" << ": Please check authorization provided " << "by the remote X application.\n"; } } #ifdef TEST *logofs << "Loop: Shutting down the NX transport.\n" << logofs_flush; #endif HandleCleanup(); } else if (proxy -> handlePing() < 0) { #ifdef TEST *logofs << "Loop: Failure handling the ping for " << "proxy FD#" << proxyFD << ".\n" << logofs_flush; #endif HandleShutdown(); } // // Check if the watchdog has exited and we didn't // get the SIGCHLD. This can happen if the parent // has overridden our signal handlers. // if (IsRunning(lastWatchdog) && CheckProcess(lastWatchdog, "watchdog") == 0) { #ifdef WARNING *logofs << "Loop: WARNING! Watchdog is gone unnoticed. " << "Setting the last signal to SIGTERM.\n" << logofs_flush; #endif lastSignal = SIGTERM; #ifdef WARNING *logofs << "Loop: WARNING! Resetting pid of last " << "watchdog process.\n" << logofs_flush; #endif SetNotRunning(lastWatchdog); } // // Let the client proxy find out if the agent's // channel is gone. This is the normal shutdown // procedure in the case of an internal connect- // ion to the agent. // int cleanup = 0; if (control -> ProxyMode == proxy_client && agent != NULL && proxy -> getType(agentFD[1]) == channel_none && lastKill == 0 && lastDestroy == 1) { #ifdef TEST *logofs << "Loop: End of NX transport requested " << "by agent.\n" << logofs_flush; #endif #ifdef TEST *logofs << "Loop: Bytes sent so far are " << (unsigned long long) statistics -> getBytesOut() << ".\n" << logofs_flush; #endif if (statistics -> getBytesOut() < 1024) { cerr << "Info" << ": Your session has died before reaching " << "an usable state.\n"; cerr << "Info" << ": This can be due to the remote X server " << "refusing access to the client.\n"; cerr << "Info" << ": Please check the authorization provided " << "by your X application.\n"; } cleanup = 1; } // // Check if the user requested the end of the // session by sending a signal to the proxy. // All signals are handled in the main loop // so we need to reset the value to get ready // for the next iteration. // int signal = 0; if (lastSignal != 0) { switch (lastSignal) { case SIGCHLD: case SIGUSR1: case SIGUSR2: { break; } default: { signal = lastSignal; cleanup = 1; break; } } lastSignal = 0; } if (cleanup == 1) { // // The first time termination signal is received // disable all further connections, close down any // X channel and wait for a second signal. // if (lastKill == 0) { // // Don't print a message if cleanup is // due to normal termination of agent. // if (signal != 0) { #ifdef TEST *logofs << "Loop: End of NX transport requested by signal '" << signal << "' '" << DumpSignal(signal) << "'.\n" << logofs_flush; #endif handleTerminatingInLoop(); } // // Disable any further connection. // CleanupListeners(); // // Close all the remaining X channels and // let proxies save their persistent cache // on disk. // CleanupConnections(); // // We'll need to wait for the X channels // to be shut down before waiting for the // cleanup signal. // lastKill = 1; } else if (lastKill == 2) { #ifdef TEST *logofs << "Loop: Shutting down the NX transport.\n" << logofs_flush; #endif proxy -> handleShutdown(); HandleCleanup(); } } if (lastKill == 1 && proxy -> getChannels(channel_x11) == 0) { // // Save the message stores to the // persistent cache. // proxy -> handleSave(); // // Run a watchdog process so we can finally // give up at the time the watchdog exits. // if (IsNotRunning(lastWatchdog)) { int timeout = control -> CleanupTimeout; if (timeout > 0) { if (proxy -> getChannels() == 0) { timeout = 500; } #ifdef TEST *logofs << "Loop: Starting watchdog process with timeout " << "of " << timeout << " Ms.\n" << logofs_flush; #endif } #ifdef TEST else { *logofs << "Loop: Starting watchdog process without " << "a timeout.\n" << logofs_flush; } #endif lastWatchdog = NXTransWatchdog(timeout); if (IsFailed(lastWatchdog)) { #ifdef PANIC *logofs << "Loop: PANIC! Can't start the NX watchdog " << "process in shutdown.\n" << logofs_flush; #endif cerr << "Error" << ": Can't start the NX watchdog " << "process in shutdown.\n"; HandleCleanup(); } #ifdef TEST else { *logofs << "Loop: Watchdog started with pid '" << lastWatchdog << "'.\n" << logofs_flush; } #endif } else { #ifdef PANIC *logofs << "Loop: PANIC! Previous watchdog detected " << "in shutdown with pid '" << lastWatchdog << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Previous watchdog detected " << "in shutdown with pid '" << lastWatchdog << "'.\n"; HandleCleanup(); } if (control -> CleanupTimeout > 0) { #ifdef TEST *logofs << "Loop: Waiting the cleanup timeout to complete.\n" << logofs_flush; #endif cerr << "Info" << ": Waiting the cleanup timeout to complete.\n"; } else { // // The NX server will kill the watchdog // process after having shut down the // service channels. // cerr << "Info" << ": Watchdog running with pid '" << lastWatchdog << "'.\n"; #ifdef TEST *logofs << "Loop: Waiting the watchdog process to complete.\n" << logofs_flush; #endif cerr << "Info" << ": Waiting the watchdog process to complete.\n"; } lastKill = 2; } } static void handleCheckBitrateInLoop() { static long int slept = 0; #ifdef TEST *logofs << "Loop: Bitrate is " << statistics -> getBitrateInShortFrame() << " B/s and " << statistics -> getBitrateInLongFrame() << " B/s in " << control -> ShortBitrateTimeFrame / 1000 << "/" << control -> LongBitrateTimeFrame / 1000 << " seconds timeframes.\n" << logofs_flush; #endif // // This can be improved. We may not jump out // of the select often enough to guarantee // the necessary accuracy. // if (control -> LocalBitrateLimit > 0) { #ifdef TEST *logofs << "Loop: Calculating bandwidth usage with limit " << control -> LocalBitrateLimit << ".\n" << logofs_flush; #endif int reference = (statistics -> getBitrateInLongFrame() + statistics -> getBitrateInShortFrame()) / 2; if (reference > control -> LocalBitrateLimit) { double ratio = ((double) reference) / ((double) control -> LocalBitrateLimit); if (ratio > 1.2) { ratio = 1.2; } slept += (unsigned int) (pow(50000, ratio) / 1000); if (slept > 2000) { #ifdef WARNING *logofs << "Loop: WARNING! Sleeping due to " << "reference bitrate of " << reference << " B/s.\n" << logofs_flush; #endif cerr << "Warning" << ": Sleeping due to " << "reference bitrate of " << reference << " B/s.\n"; slept %= 2000; } T_timestamp idleTs = getNewTimestamp(); usleep((unsigned int) pow(50000, ratio)); int diffTs = diffTimestamp(idleTs, getNewTimestamp()); statistics -> addIdleTime(diffTs); statistics -> subReadTime(diffTs); } } } #if defined(TEST) || defined(INFO) static void handleCheckStateInLoop(int &setFDs) { int fdLength; int fdPending; int fdSplits; for (int j = 0; j < setFDs; j++) { if (j != proxyFD) { fdPending = proxy -> getPending(j); if (fdPending > 0) { #ifdef PANIC *logofs << "Loop: PANIC! Buffer for descriptor FD#" << j << " has pending bytes to read.\n" << logofs_flush; #endif HandleCleanup(); } fdLength = proxy -> getLength(j); if (fdLength > 0) { #ifdef TEST *logofs << "Loop: WARNING! Buffer for descriptor FD#" << j << " has " << fdLength << " bytes to write.\n" << logofs_flush; #endif } } } fdPending = proxy -> getPending(proxyFD); if (fdPending > 0) { #ifdef PANIC *logofs << "Loop: PANIC! Buffer for proxy descriptor FD#" << proxyFD << " has pending bytes to read.\n" << logofs_flush; #endif HandleCleanup(); } fdLength = proxy -> getFlushable(proxyFD); if (fdLength > 0) { if (control -> FlushPolicy == policy_immediate && proxy -> getBlocked(proxyFD) == 0) { #ifdef PANIC *logofs << "Loop: PANIC! Buffer for proxy descriptor FD#" << proxyFD << " has " << fdLength << " bytes " << "to write with policy 'immediate'.\n" << logofs_flush; #endif HandleCleanup(); } else { #ifdef TEST *logofs << "Loop: WARNING! Buffer for proxy descriptor FD#" << proxyFD << " has " << fdLength << " bytes " << "to write.\n" << logofs_flush; #endif } } fdSplits = proxy -> getSplitSize(); if (fdSplits > 0) { #ifdef WARNING *logofs << "Loop: WARNING! Proxy descriptor FD#" << proxyFD << " has " << fdSplits << " splits to send.\n" << logofs_flush; #endif } } static void handleCheckSelectInLoop(int &setFDs, fd_set &readSet, fd_set &writeSet, T_timestamp selectTs) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Maximum descriptors is [" << setFDs << "] at " << strMsTimestamp() << ".\n" << logofs_flush; #endif int i; if (setFDs > 0) { i = 0; #if defined(TEST) || defined(INFO) *logofs << "Loop: Selected for read are "; #endif for (int j = 0; j < setFDs; j++) { if (FD_ISSET(j, &readSet)) { #if defined(TEST) || defined(INFO) *logofs << "[" << j << "]" << logofs_flush; #endif i++; } } if (i > 0) { #if defined(TEST) || defined(INFO) *logofs << ".\n" << logofs_flush; #endif } else { #if defined(TEST) || defined(INFO) *logofs << "[none].\n" << logofs_flush; #endif } i = 0; #if defined(TEST) || defined(INFO) *logofs << "Loop: Selected for write are "; #endif for (int j = 0; j < setFDs; j++) { if (FD_ISSET(j, &writeSet)) { #if defined(TEST) || defined(INFO) *logofs << "[" << j << "]" << logofs_flush; #endif i++; } } if (i > 0) { #if defined(TEST) || defined(INFO) *logofs << ".\n" << logofs_flush; #endif } else { #if defined(TEST) || defined(INFO) *logofs << "[none].\n" << logofs_flush; #endif } } #if defined(TEST) || defined(INFO) *logofs << "Loop: Select timeout is " << selectTs.tv_sec << " S and " << (double) selectTs.tv_usec / 1000 << " Ms.\n" << logofs_flush; #endif } static void handleCheckResultInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, fd_set &writeSet, struct timeval &selectTs, struct timeval &startTs) { int diffTs = diffTimestamp(startTs, getNewTimestamp()); #if defined(TEST) || defined(INFO) if (diffTs >= (control -> PingTimeout - (control -> LatencyTimeout * 4))) { *logofs << "Loop: Select result is [" << resultFDs << "] at " << strMsTimestamp() << " with no " << "communication within " << diffTs << " Ms.\n" << logofs_flush; } else { *logofs << "Loop: Select result is [" << resultFDs << "] error is [" << errorFDs << "] at " << strMsTimestamp() << " after " << diffTs << " Ms.\n" << logofs_flush; } #endif int i; if (resultFDs > 0) { i = 0; #if defined(TEST) || defined(INFO) *logofs << "Loop: Selected for read are "; #endif for (int j = 0; j < setFDs; j++) { if (FD_ISSET(j, &readSet)) { #if defined(TEST) || defined(INFO) *logofs << "[" << j << "]" << logofs_flush; #endif i++; } } if (i > 0) { #if defined(TEST) || defined(INFO) *logofs << ".\n" << logofs_flush; #endif } else { #if defined(TEST) || defined(INFO) *logofs << "[none].\n" << logofs_flush; #endif } i = 0; #if defined(TEST) || defined(INFO) *logofs << "Loop: Selected for write are "; #endif for (int j = 0; j < setFDs; j++) { if (FD_ISSET(j, &writeSet)) { #if defined(TEST) || defined(INFO) *logofs << "[" << j << "]" << logofs_flush; #endif i++; } } if (i > 0) { #if defined(TEST) || defined(INFO) *logofs << ".\n" << logofs_flush; #endif } else { #if defined(TEST) || defined(INFO) *logofs << "[none].\n" << logofs_flush; #endif } } } #endif static void handleCheckSessionInConnect() { #ifdef TEST *logofs << "Loop: Going to check session in connect.\n" << logofs_flush; #endif if (control -> ProxyMode == proxy_client) { HandleAlert(FAILED_PROXY_CONNECTION_CLIENT_ALERT, 1); } else if (IsNotRunning(lastDialog)) { HandleAlert(FAILED_PROXY_CONNECTION_SERVER_ALERT, 1); } handleAlertInLoop(); } static void handleStatisticsInLoop() { if (lastSignal == 0) { return; } int mode = NO_STATS; if (control -> EnableStatistics == 1) { if (lastSignal == SIGUSR1) { // // Print overall statistics. // mode = TOTAL_STATS; } else if (lastSignal == SIGUSR2) { // // Print partial statistics. // mode = PARTIAL_STATS; } if (mode == TOTAL_STATS || mode == PARTIAL_STATS) { #ifdef TEST *logofs << "Loop: Going to request proxy statistics " << "with signal '" << DumpSignal(lastSignal) << "'.\n" << logofs_flush; #endif if (proxy != NULL) { if (ReopenLogFile(statsFileName, statofs, 0) < 0) { HandleCleanup(); } proxy -> handleStatistics(mode, statofs); } } } } static void handleNegotiationInLoop(int &setFDs, fd_set &readSet, fd_set &writeSet, T_timestamp &selectTs) { int yield = 0; while (yield == 0) { #ifdef TEST *logofs << "Loop: Going to run a new negotiation loop " << "with stage " << control -> ProxyStage << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif switch (control -> ProxyStage) { case stage_undefined: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_undefined" << "'.\n" << logofs_flush; #endif control -> ProxyStage = stage_initializing; break; } case stage_initializing: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_initializing" << "'.\n" << logofs_flush; #endif InitBeforeNegotiation(); control -> ProxyStage = stage_connecting; break; } case stage_connecting: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_connecting" << "'.\n" << logofs_flush; #endif SetupProxyConnection(); control -> ProxyStage = stage_connected; break; } case stage_connected: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_connected" << "'.\n" << logofs_flush; #endif // // Server side proxy must always be the one that // sends its version and options first, so, in // some way, client side can be the the one that // has the last word on the matter. // if (control -> ProxyMode == proxy_server) { // // Check if we have been listening for a // forwarder. In this case it will have to // authenticate itself. // if (WE_LISTEN_FORWARDER) { control -> ProxyStage = stage_waiting_forwarder_version; break; } control -> ProxyStage = stage_sending_proxy_options; } else { // // The X client side is the side that has to wait // for the authorization cookie and any remote // option. // control -> ProxyStage = stage_waiting_proxy_version; } break; } case stage_sending_proxy_options: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_sending_proxy_options" << "'.\n" << logofs_flush; #endif if (SendProxyOptions(proxyFD) < 0) { goto handleNegotiationInLoopError; } if (control -> ProxyMode == proxy_server) { control -> ProxyStage = stage_waiting_proxy_version; } else { control -> ProxyStage = stage_sending_proxy_caches; } break; } case stage_waiting_forwarder_version: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_waiting_forwarder_version" << "'.\n" << logofs_flush; #endif int result = ReadForwarderVersion(proxyFD); if (result == 0) { yield = 1; } else if (result == 1) { control -> ProxyStage = stage_waiting_forwarder_options; } else { goto handleNegotiationInLoopError; } break; } case stage_waiting_forwarder_options: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_waiting_forwarder_options" << "'.\n" << logofs_flush; #endif int result = ReadForwarderOptions(proxyFD); if (result == 0) { yield = 1; } else if (result == 1) { control -> ProxyStage = stage_sending_proxy_options; } else { goto handleNegotiationInLoopError; } break; } case stage_waiting_proxy_version: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_waiting_proxy_version" << "'.\n" << logofs_flush; #endif int result = ReadProxyVersion(proxyFD); if (result == 0) { yield = 1; } else if (result == 1) { control -> ProxyStage = stage_waiting_proxy_options; } else { goto handleNegotiationInLoopError; } break; } case stage_waiting_proxy_options: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_waiting_proxy_options" << "'.\n" << logofs_flush; #endif int result = ReadProxyOptions(proxyFD); if (result == 0) { yield = 1; } else if (result == 1) { if (control -> ProxyMode == proxy_server) { control -> ProxyStage = stage_waiting_proxy_caches; } else { control -> ProxyStage = stage_sending_proxy_options; } } else { goto handleNegotiationInLoopError; } break; } case stage_sending_proxy_caches: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_sending_proxy_caches" << "'.\n" << logofs_flush; #endif if (SendProxyCaches(proxyFD) < 0) { goto handleNegotiationInLoopError; } if (control -> ProxyMode == proxy_server) { control -> ProxyStage = stage_operational; } else { control -> ProxyStage = stage_waiting_proxy_caches; } break; } case stage_waiting_proxy_caches: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_waiting_proxy_caches" << "'.\n" << logofs_flush; #endif int result = ReadProxyCaches(proxyFD); if (result == 0) { yield = 1; } else if (result == 1) { if (control -> ProxyMode == proxy_server) { control -> ProxyStage = stage_sending_proxy_caches; } else { control -> ProxyStage = stage_operational; } } else { goto handleNegotiationInLoopError; } break; } case stage_operational: { #ifdef TEST *logofs << "Loop: Handling negotiation with '" << "stage_operational" << "'.\n" << logofs_flush; #endif InitAfterNegotiation(); yield = 1; break; } default: { #ifdef PANIC *logofs << "Loop: PANIC! Unmanaged case '" << control -> ProxyStage << "' while handling negotiation.\n" << logofs_flush; #endif cerr << "Error" << ": Unmanaged case '" << control -> ProxyStage << "' while handling negotiation.\n"; HandleCleanup(); } } } // // Check if the user requested the end of // the session. // if (CheckAbort() != 0) { HandleCleanup(); } // // Select the proxy descriptor so that we // can proceed negotiating the session. // FD_SET(proxyFD, &readSet); if (proxyFD >= setFDs) { setFDs = proxyFD + 1; } setMinTimestamp(selectTs, control -> PingTimeout); #ifdef TEST *logofs << "Loop: Selected proxy FD#" << proxyFD << " in negotiation " << "phase with timeout of " << selectTs.tv_sec << " S and " << selectTs.tv_usec << " Ms.\n" << logofs_flush; #endif return; handleNegotiationInLoopError: #ifdef PANIC *logofs << "Loop: PANIC! Failure negotiating the session in stage '" << control -> ProxyStage << "'.\n" << logofs_flush; #endif cerr << "Error" << ": Failure negotiating the session in stage '" << control -> ProxyStage << "'.\n"; if (control -> ProxyMode == proxy_server && control -> ProxyStage == stage_waiting_proxy_version) { #ifdef PANIC *logofs << "Loop: PANIC! Wrong version or invalid session " << "authentication cookie.\n" << logofs_flush; #endif cerr << "Error" << ": Wrong version or invalid session " << "authentication cookie.\n"; } handleTerminatingInLoop(); HandleCleanup(); } static void handleTerminatingInLoop() { if (getpid() == lastProxy) { if (control -> ProxyStage < stage_terminating) { if (agent == NULL) { cerr << "Session" << ": Terminating session at '" << strTimestamp() << "'.\n"; } control -> ProxyStage = stage_terminating; } } } static void handleTerminatedInLoop() { if (getpid() == lastProxy) { if (control -> ProxyStage < stage_terminated) { if (agent == NULL) { cerr << "Session" << ": Session terminated at '" << strTimestamp() << "'.\n"; } control -> ProxyStage = stage_terminated; } } } static void handleAlertInLoop() { if (lastAlert.code == 0) { return; } if (lastAlert.local == 0 && (lastAlert.code > LAST_PROTO_STEP_6_ALERT && control -> isProtoStep7() == 0)) { // // The remote proxy would be unable // to handle the alert. // #ifdef WARNING *logofs << "Loop: WARNING! Ignoring unsupported alert " << "with code '" << lastAlert.code << "'.\n" << logofs_flush; #endif } else if (lastAlert.local == 0) { if (proxy != NULL) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Requesting a remote alert with code '" << lastAlert.code << "'.\n" << logofs_flush; #endif if (proxy -> handleAlert(lastAlert.code) < 0) { HandleShutdown(); } } } else { #if defined(TEST) || defined(INFO) *logofs << "Loop: Handling a local alert with code '" << lastAlert.code << "'.\n" << logofs_flush; #endif if (control -> ProxyMode == proxy_client) { // // If we are at X client side and server // proxy is not responding, we don't have // any possibility to interact with user. // if (lastAlert.code != CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT && lastAlert.code != RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT && lastAlert.code != FAILED_PROXY_CONNECTION_CLIENT_ALERT) { // // Let the server proxy show the dialog. // if (proxy != NULL && proxy -> handleAlert(lastAlert.code) < 0) { HandleShutdown(); } } } else { char caption[DEFAULT_STRING_LENGTH]; strcpy(caption, ALERT_CAPTION_PREFIX); int length = strlen(sessionId); // // Get rid of the trailing MD5 from session id. // if (length > (MD5_LENGTH * 2 + 1) && *(sessionId + (length - (MD5_LENGTH * 2 + 1))) == '-') { strncat(caption, sessionId, length - (MD5_LENGTH * 2 + 1)); } else { strcat(caption, sessionId); } // // Use the display to which we are forwarding // the remote X connections. // char *display = displayHost; int replace = 1; int local = 1; const char *message; const char *type; switch (lastAlert.code) { case CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT: { message = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_STRING; type = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_TYPE; break; } case CLOSE_DEAD_X_CONNECTION_SERVER_ALERT: { message = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_STRING; type = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_TYPE; break; } case CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT: { message = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING; type = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE; break; } case RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT: { message = RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING; type = RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE; break; } case CLOSE_UNRESPONSIVE_X_SERVER_ALERT: { message = CLOSE_UNRESPONSIVE_X_SERVER_ALERT_STRING; type = CLOSE_UNRESPONSIVE_X_SERVER_ALERT_TYPE; break; } case WRONG_PROXY_VERSION_ALERT: { message = WRONG_PROXY_VERSION_ALERT_STRING; type = WRONG_PROXY_VERSION_ALERT_TYPE; break; } case FAILED_PROXY_CONNECTION_SERVER_ALERT: { message = FAILED_PROXY_CONNECTION_SERVER_ALERT_STRING; type = FAILED_PROXY_CONNECTION_SERVER_ALERT_TYPE; break; } case MISSING_PROXY_CACHE_ALERT: { message = MISSING_PROXY_CACHE_ALERT_STRING; type = MISSING_PROXY_CACHE_ALERT_TYPE; break; } case ABORT_PROXY_CONNECTION_ALERT: { message = ABORT_PROXY_CONNECTION_ALERT_STRING; type = ABORT_PROXY_CONNECTION_ALERT_TYPE; break; } case DISPLACE_MESSAGE_ALERT: { message = DISPLACE_MESSAGE_ALERT_STRING; type = DISPLACE_MESSAGE_ALERT_TYPE; break; } case GREETING_MESSAGE_ALERT: { message = GREETING_MESSAGE_ALERT_STRING; type = GREETING_MESSAGE_ALERT_TYPE; break; } case START_RESUME_SESSION_ALERT: { message = START_RESUME_SESSION_ALERT_STRING; type = START_RESUME_SESSION_ALERT_TYPE; break; } case FAILED_RESUME_DISPLAY_ALERT: { message = FAILED_RESUME_DISPLAY_ALERT_STRING; type = FAILED_RESUME_DISPLAY_ALERT_TYPE; break; } case FAILED_RESUME_DISPLAY_BROKEN_ALERT: { message = FAILED_RESUME_DISPLAY_BROKEN_STRING; type = FAILED_RESUME_DISPLAY_BROKEN_TYPE; break; } case FAILED_RESUME_VISUALS_ALERT: { message = FAILED_RESUME_VISUALS_ALERT_STRING; type = FAILED_RESUME_VISUALS_ALERT_TYPE; break; } case FAILED_RESUME_COLORMAPS_ALERT: { message = FAILED_RESUME_COLORMAPS_ALERT_STRING; type = FAILED_RESUME_COLORMAPS_ALERT_TYPE; break; } case FAILED_RESUME_PIXMAPS_ALERT: { message = FAILED_RESUME_PIXMAPS_ALERT_STRING; type = FAILED_RESUME_PIXMAPS_ALERT_TYPE; break; } case FAILED_RESUME_DEPTHS_ALERT: { message = FAILED_RESUME_DEPTHS_ALERT_STRING; type = FAILED_RESUME_DEPTHS_ALERT_TYPE; break; } case FAILED_RESUME_RENDER_ALERT: { message = FAILED_RESUME_RENDER_ALERT_STRING; type = FAILED_RESUME_RENDER_ALERT_TYPE; break; } case FAILED_RESUME_FONTS_ALERT: { message = FAILED_RESUME_FONTS_ALERT_STRING; type = FAILED_RESUME_FONTS_ALERT_TYPE; break; } case INTERNAL_ERROR_ALERT: { message = INTERNAL_ERROR_ALERT_STRING; type = INTERNAL_ERROR_ALERT_TYPE; break; } case ABORT_PROXY_NEGOTIATION_ALERT: { message = ABORT_PROXY_NEGOTIATION_ALERT_STRING; type = ABORT_PROXY_NEGOTIATION_ALERT_TYPE; break; } case ABORT_PROXY_SHUTDOWN_ALERT: { message = ABORT_PROXY_SHUTDOWN_ALERT_STRING; type = ABORT_PROXY_SHUTDOWN_ALERT_TYPE; break; } case FAILED_XDMCP_CONNECTION_ALERT: { message = FAILED_XDMCP_CONNECTION_ALERT_STRING; type = FAILED_XDMCP_CONNECTION_ALERT_TYPE; break; } default: { if (lastAlert.code > LAST_PROTO_STEP_7_ALERT) { #ifdef WARNING *logofs << "Loop: WARNING! An unrecognized alert type '" << lastAlert.code << "' was requested.\n" << logofs_flush; #endif cerr << "Warning" << ": An unrecognized alert type '" << lastAlert.code << "' was requested.\n"; } #ifdef WARNING else { *logofs << "Loop: WARNING! Ignoring obsolete alert type '" << lastAlert.code << "'.\n" << logofs_flush; } #endif message = NULL; type = NULL; replace = 0; break; } } if (replace == 1 && IsRunning(lastDialog)) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Killing the previous dialog with pid '" << lastDialog << "'.\n" << logofs_flush; #endif // // The client ignores the TERM signal // on Windows. // #ifdef __CYGWIN32__ KillProcess(lastDialog, "dialog", SIGKILL, 0); #else KillProcess(lastDialog, "dialog", SIGTERM, 0); #endif SetNotRunning(lastDialog); if (proxy != NULL) { proxy -> handleResetAlert(); } } if (message != NULL && type != NULL) { lastDialog = NXTransDialog(caption, message, 0, type, local, display); if (IsFailed(lastDialog)) { #ifdef PANIC *logofs << "Loop: PANIC! Can't start the NX dialog process.\n" << logofs_flush; #endif SetNotRunning(lastDialog); } #if defined(TEST) || defined(INFO) else { *logofs << "Loop: Dialog started with pid '" << lastDialog << "'.\n" << logofs_flush; } #endif } #if defined(TEST) || defined(INFO) else { *logofs << "Loop: No new dialog required for code '" << lastAlert.code << "'.\n" << logofs_flush; } #endif } } // // Reset state. // lastAlert.code = 0; lastAlert.local = 0; } static inline void handleSetAgentInLoop(int &setFDs, fd_set &readSet, fd_set &writeSet, struct timeval &selectTs) { #ifdef TEST *logofs << "Loop: Preparing the masks for the agent descriptors.\n" << logofs_flush; #endif agent -> saveChannelState(); agent -> saveReadMask(&readSet); agent -> saveWriteMask(&writeSet); if (control -> ProxyStage >= stage_operational) { if (agent -> remoteCanRead(&readSet) || agent -> remoteCanWrite(&writeSet) || agent -> localCanRead() || agent -> proxyCanRead()) { #ifdef TEST *logofs << "Loop: Setting a null timeout with agent descriptors ready.\n" << logofs_flush; #endif // // Force a null timeout so we'll bail out // of the select immediately. We will ac- // comodate the result code later. // selectTs.tv_sec = 0; selectTs.tv_usec = 0; } } #ifdef TEST *logofs << "Loop: Clearing the read and write agent descriptors.\n" << logofs_flush; #endif agent -> clearReadMask(&readSet); agent -> clearWriteMask(&writeSet); } static inline void handleAgentInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, fd_set &writeSet, struct timeval &selectTs) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Setting proxy and local agent descriptors.\n" << logofs_flush; #endif // // Check if I/O is possible on the local // agent or the proxy descriptor. // if (resultFDs >= 0) { // // Save if the proxy can read from the // the agent descriptor. // agent -> saveChannelState(); #if defined(TEST) || defined(INFO) *logofs << "Loop: Values were resultFDs " << resultFDs << " errorFDs " << errorFDs << " setFDs " << setFDs << ".\n" << logofs_flush; #endif if (agent -> localCanRead() == 1) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Setting agent descriptor FD#" << agent -> getLocalFd() << " as ready to read.\n" << logofs_flush; #endif agent -> setLocalRead(&readSet, &resultFDs); } #if defined(TEST) || defined(INFO) if (agent -> proxyCanRead(&readSet) == 0 && agent -> proxyCanRead() == 1) { *logofs << "Loop: WARNING! Can read from proxy FD#" << proxyFD << " but the descriptor " << "is not selected.\n" << logofs_flush; } if (agent -> proxyCanRead(&readSet) == 1) { *logofs << "Loop: Setting proxy descriptor FD#" << agent -> getProxyFd() << " as ready to read.\n" << logofs_flush; } #endif #if defined(TEST) || defined(INFO) *logofs << "Loop: Values are now resultFDs " << resultFDs << " errorFDs " << errorFDs << " setFDs " << setFDs << ".\n" << logofs_flush; #endif } } static inline void handleAgentLateInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, fd_set &writeSet, struct timeval &selectTs) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Setting remote agent descriptors.\n" << logofs_flush; #endif // // We reset the masks before calling our select. // We now set the descriptors that are ready but // only if they were set in the original mask. // We do this after having executed our loop as // we may have produced more data and the agent // descriptors may have become readable or writ- // able in the meanwhile. // if (resultFDs >= 0) { // // Save if the proxy can read from the // the agent descriptor. // agent -> saveChannelState(); #if defined(TEST) || defined(INFO) *logofs << "Loop: Values were resultFDs " << resultFDs << " errorFDs " << errorFDs << " setFDs " << setFDs << ".\n" << logofs_flush; #endif if (agent -> remoteCanRead(agent -> getSavedReadMask()) == 1) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Setting agent descriptor FD#" << agent -> getRemoteFd() << " as ready to read.\n" << logofs_flush; #endif agent -> setRemoteRead(&readSet, &resultFDs); } if (agent -> remoteCanWrite(agent -> getSavedWriteMask()) == 1) { #if defined(TEST) || defined(INFO) *logofs << "Loop: Setting agent descriptor FD#" << agent -> getRemoteFd() << " as ready to write.\n" << logofs_flush; #endif agent -> setRemoteWrite(&writeSet, &resultFDs); } #if defined(TEST) || defined(INFO) *logofs << "Loop: Values are now resultFDs " << resultFDs << " errorFDs " << errorFDs << " setFDs " << setFDs << ".\n" << logofs_flush; #endif } } static inline void handleReadableInLoop(int &resultFDs, fd_set &readSet) { if (resultFDs > 0) { T_channel_type type = channel_none; const char *label = NULL; int domain = -1; int fd = -1; if (tcpFD != -1 && FD_ISSET(tcpFD, &readSet)) { type = channel_x11; label = "X"; domain = AF_INET; fd = tcpFD; resultFDs--; } if (unixFD != -1 && FD_ISSET(unixFD, &readSet)) { type = channel_x11; label = "X"; domain = AF_UNIX; fd = unixFD; resultFDs--; } if (cupsFD != -1 && FD_ISSET(cupsFD, &readSet)) { type = channel_cups; label = "CUPS"; domain = AF_INET; fd = cupsFD; resultFDs--; } if (auxFD != -1 && FD_ISSET(auxFD, &readSet)) { // // Starting from version 1.5.0 we create real X // connections for the keyboard channel, so they // can use the fake authorization cookie. This // means that there is not such a thing like a // channel_aux anymore. // type = channel_x11; label = "auxiliary X11"; domain = AF_INET; fd = auxFD; resultFDs--; } if (smbFD != -1 && FD_ISSET(smbFD, &readSet)) { type = channel_smb; label = "SMB"; domain = AF_INET; fd = smbFD; resultFDs--; } if (mediaFD != -1 && FD_ISSET(mediaFD, &readSet)) { type = channel_media; label = "media"; domain = AF_INET; fd = mediaFD; resultFDs--; } if (httpFD != -1 && FD_ISSET(httpFD, &readSet)) { type = channel_http; label = "HTTP"; domain = AF_INET; fd = httpFD; resultFDs--; } if (fontFD != -1 && FD_ISSET(fontFD, &readSet)) { type = channel_font; label = "font server"; domain = AF_INET; fd = fontFD; resultFDs--; } if (slaveFD != -1 && FD_ISSET(slaveFD, &readSet)) { type = channel_slave; label = "slave"; domain = AF_INET; fd = slaveFD; resultFDs--; } if (type != channel_none) { int newFD = AcceptConnection(fd, domain, label); if (newFD != -1) { if (proxy -> handleNewConnection(type, newFD) < 0) { #ifdef PANIC *logofs << "Loop: PANIC! Error creating new " << label << " connection.\n" << logofs_flush; #endif cerr << "Error" << ": Error creating new " << label << " connection.\n"; close(newFD); // // Don't kill the proxy in the case of an error. // // HandleCleanup(); // } else if (proxy -> getReadable(newFD) > 0) { // // Add the descriptor, so we can try // to read immediately. // #ifdef TEST *logofs << "Loop: Trying to read immediately " << "from descriptor FD#" << newFD << ".\n" << logofs_flush; #endif FD_SET(newFD, &readSet); resultFDs++; } #ifdef TEST else { *logofs << "Loop: Nothing to read immediately " << "from descriptor FD#" << newFD << ".\n" << logofs_flush; } #endif } } } #ifdef DEBUG *logofs << "Loop: Going to check the readable descriptors.\n" << logofs_flush; #endif if (proxy -> handleRead(resultFDs, readSet) < 0) { #ifdef TEST *logofs << "Loop: Failure reading from descriptors " << "for proxy FD#" << proxyFD << ".\n" << logofs_flush; #endif HandleShutdown(); } } static inline void handleWritableInLoop(int &resultFDs, fd_set &writeSet) { #ifdef DEBUG *logofs << "Loop: Going to check the writable descriptors.\n" << logofs_flush; #endif if (resultFDs > 0 && proxy -> handleFlush(resultFDs, writeSet) < 0) { #ifdef TEST *logofs << "Loop: Failure writing to descriptors " << "for proxy FD#" << proxyFD << ".\n" << logofs_flush; #endif HandleShutdown(); } } static inline void handleFlushInLoop() { #ifdef DEBUG *logofs << "Loop: Going to flush any data to the proxy.\n" << logofs_flush; #endif if (agent == NULL || control -> FlushPolicy == policy_immediate) { #if defined(TEST) || defined(INFO) if (usePolicy == -1 && control -> ProxyMode == proxy_client) { *logofs << "Loop: WARNING! Flushing the proxy link " << "on behalf of the agent.\n" << logofs_flush; } #endif if (proxy -> handleFlush() < 0) { #ifdef TEST *logofs << "Loop: Failure flushing the proxy FD#" << proxyFD << ".\n" << logofs_flush; #endif HandleShutdown(); } } } static inline void handleRotateInLoop() { #ifdef DEBUG *logofs << "Loop: Going to rotate channels " << "for proxy FD#" << proxyFD << ".\n" << logofs_flush; #endif proxy -> handleRotate(); } static inline void handleEventsInLoop() { #ifdef DEBUG *logofs << "Loop: Going to check channel events " << "for proxy FD#" << proxyFD << ".\n" << logofs_flush; #endif if (proxy -> handleEvents() < 0) { #ifdef TEST *logofs << "Loop: Failure handling channel events " << "for proxy FD#" << proxyFD << ".\n" << logofs_flush; #endif HandleShutdown(); } } static void handleLogReopenInLoop(T_timestamp &logsTs, T_timestamp &nowTs) { // // If need to limit the size of the // log file, check the size at each // loop. // #ifndef QUOTA if (diffTimestamp(logsTs, nowTs) > control -> FileSizeCheckTimeout) #endif { #ifdef DEBUG *logofs << "Loop: Checking size of log file '" << errorsFileName << "'.\n" << logofs_flush; #endif #ifndef MIXED if (ReopenLogFile(errorsFileName, logofs, control -> FileSizeLimit) < 0) { HandleShutdown(); } #endif // // Reset to current timestamp. // logsTs = nowTs; } } static inline void handleSetReadInLoop(fd_set &readSet, int &setFDs, struct timeval &selectTs) { proxy -> setReadDescriptors(&readSet, setFDs, selectTs); } static inline void handleSetWriteInLoop(fd_set &writeSet, int &setFDs, struct timeval &selectTs) { proxy -> setWriteDescriptors(&writeSet, setFDs, selectTs); } static void handleSetListenersInLoop(fd_set &readSet, int &setFDs) { // // Set descriptors of listening sockets. // if (control -> ProxyMode == proxy_client) { if (useTcpSocket == 1) { FD_SET(tcpFD, &readSet); if (tcpFD >= setFDs) { setFDs = tcpFD + 1; } #ifdef DEBUG *logofs << "Loop: Selected listener tcpFD " << tcpFD << " with setFDs " << setFDs << ".\n" << logofs_flush; #endif } if (useUnixSocket == 1) { FD_SET(unixFD, &readSet); if (unixFD >= setFDs) { setFDs = unixFD + 1; } #ifdef DEBUG *logofs << "Loop: Selected listener unixFD " << unixFD << " with setFDs " << setFDs << ".\n" << logofs_flush; #endif } if (useCupsSocket == 1) { FD_SET(cupsFD, &readSet); if (cupsFD >= setFDs) { setFDs = cupsFD + 1; } #ifdef DEBUG *logofs << "Loop: Selected listener cupsFD " << cupsFD << " with setFDs " << setFDs << ".\n" << logofs_flush; #endif } if (useAuxSocket == 1) { FD_SET(auxFD, &readSet); if (auxFD >= setFDs) { setFDs = auxFD + 1; } #ifdef DEBUG *logofs << "Loop: Selected listener auxFD " << auxFD << " with setFDs " << setFDs << ".\n" << logofs_flush; #endif } if (useSmbSocket == 1) { FD_SET(smbFD, &readSet); if (smbFD >= setFDs) { setFDs = smbFD + 1; } #ifdef DEBUG *logofs << "Loop: Selected listener smbFD " << smbFD << " with setFDs " << setFDs << ".\n" << logofs_flush; #endif } if (useMediaSocket == 1) { FD_SET(mediaFD, &readSet); if (mediaFD >= setFDs) { setFDs = mediaFD + 1; } #ifdef DEBUG *logofs << "Loop: Selected listener mediaFD " << mediaFD << " with setFDs " << setFDs << ".\n" << logofs_flush; #endif } if (useHttpSocket == 1) { FD_SET(httpFD, &readSet); if (httpFD >= setFDs) { setFDs = httpFD + 1; } #ifdef DEBUG *logofs << "Loop: Selected listener httpFD " << httpFD << " with setFDs " << setFDs << ".\n" << logofs_flush; #endif } } else { if (useFontSocket == 1) { FD_SET(fontFD, &readSet); if (fontFD >= setFDs) { setFDs = fontFD + 1; } #ifdef DEBUG *logofs << "Loop: Selected listener fontFD " << fontFD << " with setFDs " << setFDs << ".\n" << logofs_flush; #endif } } if (useSlaveSocket == 1) { FD_SET(slaveFD, &readSet); if (slaveFD >= setFDs) { setFDs = slaveFD + 1; } #ifdef DEBUG *logofs << "Loop: Selected listener slaveFD " << slaveFD << " with setFDs " << setFDs << ".\n" << logofs_flush; #endif } } nxcomp/NXmitshm.h0000644000076400007640000000340111323113026014240 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef NXmitshm_H #define NXmitshm_H #ifdef __cplusplus extern "C" { #endif /* * Import opcodes from * to get rid of weird dependencies from other * headers of X environment. */ #define X_ShmQueryVersion 0 #define X_ShmAttach 1 #define X_ShmDetach 2 #define X_ShmPutImage 3 #define X_ShmGetImage 4 #define X_ShmCreatePixmap 5 #define ShmCompletion 0 #define ShmNumberEvents (ShmCompletion + 1) #define BadShmSeg 0 #define ShmNumberErrors (BadShmSeg + 1) #ifdef __cplusplus } #endif #endif /* NXmitshm_H */ nxcomp/Split.cpp0000644000076400007640000012323411323113030014121 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include #include #include #include "Misc.h" #include "Split.h" #include "Control.h" #include "Statistics.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "StaticCompressor.h" #include "Unpack.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Define this to trace elements // allocated and deallocated. // #undef REFERENCES // // Counters used for store control. // int SplitStore::totalSplitSize_; int SplitStore::totalSplitStorageSize_; // // This is used for reference count. // #ifdef REFERENCES int Split::references_ = 0; #endif Split::Split() { resource_ = nothing; position_ = nothing; store_ = NULL; d_size_ = 0; i_size_ = 0; c_size_ = 0; r_size_ = 0; next_ = 0; load_ = 0; save_ = 0; checksum_ = NULL; state_ = split_undefined; mode_ = split_none; action_ = is_discarded; #ifdef REFERENCES references_++; *logofs << "Split: Created new Split at " << this << " out of " << references_ << " allocated references.\n" << logofs_flush; #endif } Split::~Split() { delete [] checksum_; #ifdef REFERENCES references_--; *logofs << "Split: Deleted Split at " << this << " out of " << references_ << " allocated references.\n" << logofs_flush; #endif } SplitStore::SplitStore(StaticCompressor *compressor, CommitStore *commits, int resource) : compressor_(compressor), commits_(commits), resource_(resource) { splits_ = new T_splits(); current_ = splits_ -> end(); splitStorageSize_ = 0; #ifdef TEST *logofs << "SplitStore: Created new store ["; if (resource_ != nothing) { *logofs << resource_; } else { *logofs << "commit"; } *logofs << "].\n" << logofs_flush; *logofs << "SplitStore: Total messages in stores are " << totalSplitSize_ << " with total storage size " << totalSplitStorageSize_ << ".\n" << logofs_flush; #endif } SplitStore::~SplitStore() { totalSplitSize_ -= splits_ -> size(); totalSplitStorageSize_ -= splitStorageSize_; for (T_splits::iterator i = splits_ -> begin(); i != splits_ -> end(); i++) { delete *i; } delete splits_; #ifdef TEST *logofs << "SplitStore: Deleted store ["; if (resource_ != nothing) { *logofs << resource_; } else { *logofs << "commit"; } *logofs << "] with storage size " << splitStorageSize_ << ".\n" << logofs_flush; *logofs << "SplitStore: Total messages in stores are " << totalSplitSize_ << " with total storage size " << totalSplitStorageSize_ << ".\n" << logofs_flush; #endif } // // This is called at the encoding side. // Split *SplitStore::add(MessageStore *store, int resource, T_split_mode mode, int position, T_store_action action, T_checksum checksum, const unsigned char *buffer, const int size) { #ifdef TEST *logofs << "SplitStore: Adding message [" << (unsigned int) store -> opcode() << "] resource " << resource << " mode " << mode << " position " << position << " action [" << DumpAction(action) << "] and checksum [" << DumpChecksum(checksum) << "]" << ".\n" << logofs_flush; #endif Split *split = new Split(); if (split == NULL) { #ifdef PANIC *logofs << "SplitStore: PANIC! Can't allocate " << "memory for the split.\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory " << "for the split.\n"; HandleAbort(); } split -> store_ = store; split -> resource_ = resource; split -> mode_ = mode; split -> position_ = position; split -> action_ = action; split -> store_ -> validateSize(size); // // The checksum is not provided if the // message is cached. // if (checksum != NULL) { split -> checksum_ = new md5_byte_t[MD5_LENGTH]; memcpy(split -> checksum_, checksum, MD5_LENGTH); } // // We don't need the identity data at the // encoding side. This qualifies the split // as a split generated at the encoding // side. // split -> i_size_ = store -> identitySize(buffer, size); split -> d_size_ = size - split -> i_size_; if (action == IS_ADDED || action == is_discarded) { // // If the message was added to message // store or discarded we need to save // the real data so we can transfer it // at later time. // split -> data_.resize(split -> d_size_); memcpy(split -> data_.begin(), buffer + split -> i_size_, split -> d_size_); // // If the message was added, lock it so // it will not be used by the encoding // side until it is recomposed. // if (action == IS_ADDED) { split -> store_ -> lock(split -> position_); #ifdef TEST commits_ -> validate(split); #endif } } #ifdef WARNING else { *logofs << "SplitStore: WARNING! Not copying data for the cached message.\n" << logofs_flush; } #endif push(split); return split; } // // This is called at decoding side. If checksum // is provided, the message can be searched on // disk, then, if message is found, an event is // sent to abort the data transfer. // Split *SplitStore::add(MessageStore *store, int resource, int position, T_store_action action, T_checksum checksum, unsigned char *buffer, const int size) { #ifdef TEST *logofs << "SplitStore: Adding message [" << (unsigned int) store -> opcode() << "] resource " << resource << " position " << position << " action [" << DumpAction(action) << "] and checksum [" << DumpChecksum(checksum) << "].\n" << logofs_flush; #endif Split *split = new Split(); if (split == NULL) { #ifdef PANIC *logofs << "SplitStore: PANIC! Can't allocate " << "memory for the split.\n" << logofs_flush; #endif cerr << "Error" << ": Can't allocate memory " << "for the split.\n"; HandleAbort(); } split -> store_ = store; split -> resource_ = resource; split -> position_ = position; split -> action_ = action; split -> store_ -> validateSize(size); // // Check if the checksum was provided // by the remote. // if (checksum != NULL) { split -> checksum_ = new md5_byte_t[MD5_LENGTH]; memcpy(split -> checksum_, checksum, MD5_LENGTH); } split -> i_size_ = store -> identitySize(buffer, size); // // Copy the identity so we can expand the // message when it is committed. // split -> identity_.resize(split -> i_size_); memcpy(split -> identity_.begin(), buffer, split -> i_size_); split -> d_size_ = size - split -> i_size_; if (action == IS_ADDED || action == is_discarded) { // // The unpack procedure will check if the // first 2 bytes of the buffer contain the // pattern and will not try to expand the // image. // split -> data_.resize(2); unsigned char *data = split -> data_.begin(); data[0] = SPLIT_PATTERN; data[1] = SPLIT_PATTERN; // // If the message was added to the store, // we don't have the data part, yet, so // we need to lock the message until it // is recomposed. // if (action == IS_ADDED) { split -> store_ -> lock(split -> position_); #ifdef TEST commits_ -> validate(split); #endif } } else { #ifdef WARNING *logofs << "SplitStore: WARNING! Copying data for the cached message.\n" << logofs_flush; #endif // // We may optionally take the data from the // message store in compressed form, but, // as the data has been decompressed in the // buffer, we save a further decompression. // split -> data_.resize(split -> d_size_); memcpy(split -> data_.begin(), buffer + split -> i_size_, split -> d_size_); } push(split); return split; } void SplitStore::push(Split *split) { splits_ -> push_back(split); splitStorageSize_ += getNodeSize(split); totalSplitSize_++; totalSplitStorageSize_ += getNodeSize(split); statistics -> addSplit(); #ifdef TEST *logofs << "SplitStore: There are " << splits_ -> size() << " messages in store [" << resource_ << "] with " << "storage size " << splitStorageSize_ << ".\n" << logofs_flush; *logofs << "SplitStore: Total messages in stores are " << totalSplitSize_ << " with total storage size " << totalSplitStorageSize_ << ".\n" << logofs_flush; #endif split -> state_ = split_added; } void SplitStore::dump() { #ifdef DUMP int n; Split *split; *logofs << "SplitStore: DUMP! Dumping content of "; if (commits_ == NULL) { *logofs << "[commits]"; } else { *logofs << "[splits] for store [" << resource_ << "]"; } *logofs << " with [" << getSize() << "] elements " << "in the store.\n" << logofs_flush; n = 0; for (T_splits::iterator i = splits_ -> begin(); i != splits_ -> end(); i++, n++) { split = *i; *logofs << "SplitStore: DUMP! Split [" << n << "] has action [" << DumpAction(split -> action_) << "] state [" << DumpState(split -> state_) << "] "; if (split -> resource_ >= 0) { *logofs << "resource " << split -> resource_; } *logofs << " request " << (unsigned) split -> store_ -> opcode() << " position " << split -> position_ << " size is " << split -> data_.size() << " (" << split -> d_size_ << "/" << split -> c_size_ << "/" << split -> r_size_ << ") with " << split -> data_.size() - split -> next_ << "] bytes to go.\n" << logofs_flush; } #endif } int SplitStore::send(EncodeBuffer &encodeBuffer, int packetSize) { if (splits_ -> size() == 0) { #ifdef PANIC *logofs << "SplitStore: PANIC! Function send called with no splits available.\n" << logofs_flush; #endif cerr << "Error" << ": Function send called with no splits available.\n"; HandleAbort(); } // // A start operation must always be executed on // the split, even in the case the split will be // later aborted. // if (current_ == splits_ -> end()) { start(encodeBuffer); } // // If we have matched the checksum received from // the remote side then we must abort the current // split, else we can send another block of data // to the remote peer. // Split *split = *current_; unsigned int abort = 0; if (split -> state_ == split_loaded) { abort = 1; } encodeBuffer.encodeBoolValue(abort); if (abort == 1) { #ifdef TEST *logofs << "SplitStore: Aborting split for checksum [" << DumpChecksum(split -> checksum_) << "] position " << split -> position_ << " with " << (split -> data_.size() - split -> next_) << " bytes to go " << "out of " << split -> data_.size() << ".\n" << logofs_flush; #endif statistics -> addSplitAborted(); statistics -> addSplitAbortedBytesOut(split -> data_.size() - split -> next_); split -> next_ = split -> data_.size(); split -> state_ = split_aborted; } else { int count = (packetSize <= 0 || split -> next_ + packetSize > (int) split -> data_.size() ? split -> data_.size() - split -> next_ : packetSize); #ifdef TEST *logofs << "SplitStore: Sending split for checksum [" << DumpChecksum(split -> checksum_) << "] count " << count << " position " << split -> position_ << ". Data size is " << split -> data_.size() << " (" << split -> d_size_ << "/" << split -> c_size_ << "), " << split -> data_.size() - (split -> next_ + count) << " to go.\n" << logofs_flush; #endif encodeBuffer.encodeValue(count, 32, 10); encodeBuffer.encodeMemory(split -> data_.begin() + split -> next_, count); split -> next_ += count; } // // Was data completely transferred? We are the // sending side. We must update the message in // store, even if split was aborted. // if (split -> next_ != ((int) split -> data_.size())) { return 0; } // // Move the split at the head of the // list to the commits. // remove(split); // // Reset current position to the // end of repository. // current_ = splits_ -> end(); #ifdef TEST *logofs << "SplitStore: Removed split at head of the list. " << "Resource is " << split -> resource_ << " request " << (unsigned) split -> store_ -> opcode() << " position " << split -> position_ << ".\n" << logofs_flush; #endif return 1; } int SplitStore::start(EncodeBuffer &encodeBuffer) { // // Get the element at the top of the // list. // current_ = splits_ -> begin(); Split *split = *current_; #ifdef TEST *logofs << "SplitStore: Starting split for checksum [" << DumpChecksum(split -> checksum_) << "] position " << split -> position_ << " with " << (split -> data_.size() - split -> next_) << " bytes to go " << "out of " << split -> data_.size() << ".\n" << logofs_flush; #endif // // See if compression of the data part is // enabled. // if (split -> store_ -> enableCompress) { // // If the split is going to be aborted don't // compress the data and go straight to the // send. The new data size will be assumed // from the disk cache. // if (split -> state_ != split_loaded) { unsigned int compressedSize = 0; unsigned char *compressedData = NULL; if (control -> LocalDataCompression && (compressor_ -> compressBuffer(split -> data_.begin(), split -> d_size_, compressedData, compressedSize))) { // // Replace the data with the one in // compressed form. // #ifdef TEST *logofs << "SplitStore: Split data of size " << split -> d_size_ << " has been compressed to " << compressedSize << " bytes.\n" << logofs_flush; #endif split -> data_.clear(); split -> data_.resize(compressedSize); memcpy(split -> data_.begin(), compressedData, compressedSize); split -> c_size_ = compressedSize; // // Inform our peer that the data is // compressed and send the new size. // encodeBuffer.encodeBoolValue(1); encodeBuffer.encodeValue(compressedSize, 32, 14); #ifdef TEST *logofs << "SplitStore: Signaled " << split -> c_size_ << " bytes of compressed data for this message.\n" << logofs_flush; #endif return 1; } } #ifdef TEST else { *logofs << "SplitStore: Not trying to compress the " << "loaded message.\n" << logofs_flush; } #endif // // Tell to the remote that data will // follow uncompressed. // encodeBuffer.encodeBoolValue(0); } return 1; } int SplitStore::start(DecodeBuffer &decodeBuffer) { #ifdef TEST *logofs << "SplitStore: Going to receive a new split from the remote side.\n" << logofs_flush; #endif // // Get the element at the head // of the list. // current_ = splits_ -> begin(); Split *split = *current_; unsigned int compressedSize = 0; // // Save the data size known by the remote. // This information will be needed if the // remote will not have a chance to abort // the split. // split -> r_size_ = split -> d_size_; // // Find out if data was compressed by the // remote. // if (split -> store_ -> enableCompress) { decodeBuffer.decodeBoolValue(compressedSize); if (compressedSize == 1) { // // Get the compressed size. // if (control -> isProtoStep7() == 1) { decodeBuffer.decodeValue(compressedSize, 32, 14); } else { // // As we can't refuse to handle the decoding // of the split message when connected to an // old proxy version, we need to decode this // in a way that is compatible. // unsigned int diffSize; decodeBuffer.decodeValue(diffSize, 32, 14); split -> store_ -> lastResize += diffSize; compressedSize = split -> store_ -> lastResize; } split -> store_ -> validateSize(split -> d_size_, compressedSize); split -> r_size_ = compressedSize; } } // // Update the size if the split // was not already loaded. // if (split -> state_ != split_loaded) { split -> data_.clear(); if (compressedSize > 0) { split -> c_size_ = compressedSize; #ifdef TEST *logofs << "SplitStore: Split data of size " << split -> d_size_ << " was compressed to " << split -> c_size_ << " bytes.\n" << logofs_flush; #endif split -> data_.resize(split -> c_size_); } else { split -> data_.resize(split -> d_size_); } unsigned char *data = split -> data_.begin(); data[0] = SPLIT_PATTERN; data[1] = SPLIT_PATTERN; } #ifdef TEST else { // // The message had been already // loaded from disk. // if (compressedSize > 0) { if ((int) compressedSize != split -> c_size_) { *logofs << "SplitStore: WARNING! Compressed data size is " << "different than the loaded compressed size.\n" << logofs_flush; } *logofs << "SplitStore: Ignoring the new size with " << "loaded compressed size " << split -> c_size_ << ".\n" << logofs_flush; } } #endif return 1; } int SplitStore::receive(DecodeBuffer &decodeBuffer) { if (splits_ -> size() == 0) { #ifdef PANIC *logofs << "SplitStore: PANIC! Function receive called with no splits available.\n" << logofs_flush; #endif cerr << "Error" << ": Function receive called with no splits available.\n"; HandleAbort(); } if (current_ == splits_ -> end()) { start(decodeBuffer); } // // Check first if split was aborted, else add // any new data to message being recomposed. // Split *split = *current_; unsigned int abort = 0; decodeBuffer.decodeBoolValue(abort); if (abort == 1) { #ifdef TEST *logofs << "SplitStore: Aborting split for checksum [" << DumpChecksum(split -> checksum_) << "] position " << split -> position_ << " with " << (split -> data_.size() - split -> next_) << " bytes to go " << "out of " << split -> data_.size() << ".\n" << logofs_flush; #endif statistics -> addSplitAborted(); statistics -> addSplitAbortedBytesOut(split -> r_size_ - split -> next_); split -> next_ = split -> r_size_; split -> state_ = split_aborted; } else { // // Get the size of the packet. // unsigned int count; decodeBuffer.decodeValue(count, 32, 10); // // If the split was not already loaded from // disk, decode the packet and update our // copy of the data. The encoding side may // have not received the abort event, yet, // and may be unaware that the message is // stored in compressed form at our side. // #ifdef TEST *logofs << "SplitStore: Receiving split for checksum [" << DumpChecksum(split -> checksum_) << "] count " << count << " position " << split -> position_ << ". Data size is " << split -> data_.size() << " (" << split -> d_size_ << "/" << split -> c_size_ << "/" << split -> r_size_ << "), " << split -> r_size_ - (split -> next_ + count) << " to go.\n" << logofs_flush; #endif if (split -> next_ + count > (unsigned) split -> r_size_) { #ifdef PANIC *logofs << "SplitStore: PANIC! Invalid data count " << count << "provided in the split.\n" << logofs_flush; *logofs << "SplitStore: PANIC! While receiving split for " << "checksum [" << DumpChecksum(split -> checksum_) << "] with count " << count << " action [" << DumpAction(split -> action_) << "] state [" << DumpState(split -> state_) << "]. Data size is " << split -> data_.size() << " (" << split -> d_size_ << "/" << split -> c_size_ << "), " << split -> data_.size() - (split -> next_ + count) << " to go.\n" << logofs_flush; #endif cerr << "Error" << ": Invalid data count " << count << "provided in the split.\n"; HandleAbort(); } if (split -> state_ != split_loaded) { #ifdef TEST if (split -> next_ + count > split -> data_.size()) { #ifdef PANIC *logofs << "SplitStore: PANIC! Inconsistent split data size " << split -> data_.size() << " with expected size " << split -> r_size_ << ".\n" << logofs_flush; #endif HandleAbort(); } #endif memcpy(split -> data_.begin() + split -> next_, decodeBuffer.decodeMemory(count), count); } else { #ifdef TEST *logofs << "SplitStore: WARNING! Data discarded with split " << "loaded from disk.\n" << logofs_flush; #endif decodeBuffer.decodeMemory(count); } split -> next_ += count; } // // Is unsplit complete? // if (split -> next_ != split -> r_size_) { return 0; } // // If the persistent cache is enabled, // we have a valid checksum and the // split was not originally retrieved // from disk, save the message on disk. // if (split -> state_ != split_loaded && split -> state_ != split_aborted) { save(split); } // // Move the split at the head of the // list to the commits. // remove(split); // // Reset the current position to the // end of the repository. // current_ = splits_ -> end(); #ifdef TEST *logofs << "SplitStore: Removed split at head of the list. " << "Resource is " << split -> resource_ << " request " << (unsigned) split -> store_ -> opcode() << " position " << split -> position_ << ".\n" << logofs_flush; #endif return 1; } Split *SplitStore::pop() { if (splits_ -> size() == 0) { #ifdef TEST *logofs << "SplitStore: The split store is empty.\n" << logofs_flush; #endif return NULL; } // // Move the pointer at the end of the list. // The next send operation will eventually // start a new split. // current_ = splits_ -> end(); Split *split = *(splits_ -> begin()); splits_ -> pop_front(); #ifdef TEST *logofs << "SplitStore: Removed split at the head of the " << "list with resource " << split -> resource_ << " request " << (unsigned) split -> store_ -> opcode() << " position " << split -> position_ << ".\n" << logofs_flush; #endif splitStorageSize_ -= getNodeSize(split); totalSplitSize_--; totalSplitStorageSize_ -= getNodeSize(split); #ifdef TEST *logofs << "SplitStore: There are " << splits_ -> size() << " messages in store [" << resource_ << "] with " << "storage size " << splitStorageSize_ << ".\n" << logofs_flush; *logofs << "SplitStore: Total messages in stores are " << totalSplitSize_ << " with total storage size " << totalSplitStorageSize_ << ".\n" << logofs_flush; #endif return split; } void SplitStore::remove(Split *split) { #ifdef TEST *logofs << "SplitStore: Going to remove the split from the list.\n" << logofs_flush; #endif #ifdef TEST if (split != getFirstSplit()) { #ifdef PANIC *logofs << "SplitStore: PANIC! Trying to remove a split " << "not at the head of the list.\n" << logofs_flush; #endif cerr << "Error" << ": Trying to remove a split " << "not at the head of the list.\n"; HandleAbort(); } #endif // // Move the split to the commit store. // splits_ -> pop_front(); commits_ -> splits_ -> push_back(split); splitStorageSize_ -= getNodeSize(split); totalSplitSize_--; totalSplitStorageSize_ -= getNodeSize(split); #ifdef TEST *logofs << "SplitStore: There are " << splits_ -> size() << " messages in store [" << resource_ << "] with " << "storage size " << splitStorageSize_ << ".\n" << logofs_flush; *logofs << "SplitStore: Total messages in stores are " << totalSplitSize_ << " with total storage size " << totalSplitStorageSize_ << ".\n" << logofs_flush; #endif #ifdef TEST if (splits_ -> size() == 0) { if (splitStorageSize_ != 0) { #ifdef PANIC *logofs << "SplitStore: PANIC! Internal error calculating " << "split data size. It is " << splitStorageSize_ << " while should be 0.\n" << logofs_flush; #endif cerr << "Error" << ": Internal error calculating " << "split data size. It is " << splitStorageSize_ << " while should be 0.\n"; HandleAbort(); } } #endif } const char *SplitStore::name(const T_checksum checksum) { if (checksum == NULL) { return NULL; } char *pathName = control -> ImageCachePath; if (pathName == NULL) { #ifdef PANIC *logofs << "SplitStore: PANIC! Cannot determine directory of " << "NX image files.\n" << logofs_flush; #endif return NULL; } int pathSize = strlen(pathName); // // File name is "[path][/I-c/I-][checksum][\0]", // where c is the first hex digit of checksum. // int nameSize = pathSize + 7 + MD5_LENGTH * 2 + 1; char *fileName = new char[nameSize]; if (fileName == NULL) { #ifdef PANIC *logofs << "SplitStore: PANIC! Cannot allocate space for " << "NX image file name.\n" << logofs_flush; #endif return NULL; } strcpy(fileName, pathName); sprintf(fileName + pathSize, "/I-%1X/I-", *((unsigned char *) checksum) >> 4); for (unsigned int i = 0; i < MD5_LENGTH; i++) { sprintf(fileName + pathSize + 7 + (i * 2), "%02X", ((unsigned char *) checksum)[i]); } return fileName; } int SplitStore::save(Split *split) { // // Check if saving the message on the // persistent cache is enabled. // if (split -> save_ == 0) { return 0; } T_checksum checksum = split -> checksum_; const char *fileName = name(checksum); if (fileName == NULL) { return 0; } unsigned int splitSize; ostream *fileStream = NULL; unsigned char *fileHeader = NULL; // // Get the other data from the split. // unsigned char opcode = split -> store_ -> opcode(); unsigned char *data = split -> data_.begin(); int dataSize = split -> d_size_; int compressedSize = split -> c_size_; #ifdef DEBUG *logofs << "SplitStore: Going to save split OPCODE#" << (unsigned int) opcode << " to file '" << fileName << "' with size " << dataSize << " and compressed size " << compressedSize << ".\n" << logofs_flush; #endif DisableSignals(); // // Change the mask to make the file only // readable by the user, then restore the // old mask. // mode_t fileMode; // // Check if the file already exists. We try to // load the message when the split is started // and save it only if it is not found. Still // the remote side may send the same image mul- // tiple time and we may not have the time to // notify the abort. // struct stat fileStat; if (stat(fileName, &fileStat) == 0) { #ifdef TEST *logofs << "SplitStore: Image file '" << fileName << "' already present on disk.\n" << logofs_flush; #endif goto SplitStoreSaveError; } fileMode = umask(0077); fileStream = new ofstream(fileName, ios::out | ios::binary); umask(fileMode); if (CheckData(fileStream) < 0) { #ifdef PANIC *logofs << "SplitStore: PANIC! Cannot open file '" << fileName << "' for output.\n" << logofs_flush; #endif goto SplitStoreSaveError; } fileHeader = new unsigned char[SPLIT_HEADER_SIZE]; if (fileHeader == NULL) { #ifdef PANIC *logofs << "SplitStore: PANIC! Cannot allocate space for " << "NX image header.\n" << logofs_flush; #endif goto SplitStoreSaveError; } // // Leave 3 bytes for future use. Please note // that, on some CPUs, we can't use PutULONG() // to write integers that are not aligned to // the word boundary. // *fileHeader = opcode; *(fileHeader + 1) = 0; *(fileHeader + 2) = 0; *(fileHeader + 3) = 0; PutULONG(dataSize, fileHeader + 4, false); PutULONG(compressedSize, fileHeader + 8, false); splitSize = (compressedSize > 0 ? compressedSize : dataSize); if (PutData(fileStream, fileHeader, SPLIT_HEADER_SIZE) < 0 || PutData(fileStream, data, splitSize) < 0) { #ifdef PANIC *logofs << "SplitStore: PANIC! Cannot write to NX " << "image file '" << fileName << "'.\n" << logofs_flush; #endif goto SplitStoreSaveError; } // // Check if all the data was written on the // disk and, if not, remove the faulty copy. // FlushData(fileStream); if (CheckData(fileStream) < 0) { #ifdef PANIC *logofs << "SplitStore: PANIC! Failed to write NX " << "image file '" << fileName << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Failed to write NX " << "image file '" << fileName << "'.\n"; goto SplitStoreSaveError; } #ifdef TEST *logofs << "SplitStore: Saved split to file '" << fileName << "' with data size " << dataSize << " and " << "compressed data size " << compressedSize << ".\n" << logofs_flush; #endif delete fileStream; delete [] fileName; delete [] fileHeader; EnableSignals(); // // Update the timestamp as the operation // may have taken some time. // getNewTimestamp(); return 1; SplitStoreSaveError: delete fileStream; if (fileName != NULL) { unlink(fileName); } delete [] fileName; delete [] fileHeader; EnableSignals(); return -1; } int SplitStore::find(Split *split) { const char *fileName = name(split -> checksum_); if (fileName == NULL) { return 0; } #ifdef DEBUG *logofs << "SplitStore: Going to find split OPCODE#" << (unsigned) split -> store_ -> opcode() << " in file '" << fileName << "'.\n" << logofs_flush; #endif // // Check if the file exists and, at the // same time, update the modification // time to prevent its deletion. // if (utime(fileName, NULL) == 0) { #ifdef TEST *logofs << "SplitStore: Found split OPCODE#" << (unsigned) split -> store_ -> opcode() << " in file '" << fileName << "'.\n" << logofs_flush; #endif delete [] fileName; return 1; } #ifdef TEST *logofs << "SplitStore: WARNING! Can't find split " << "OPCODE#" << (unsigned) split -> store_ -> opcode() << " in file '" << fileName << "'.\n" << logofs_flush; #endif delete [] fileName; return 0; } int SplitStore::load(Split *split) { // // Check if loading the image is enabled. // if (split -> load_ == 0) { return 0; } const char *fileName = name(split -> checksum_); if (fileName == NULL) { return 0; } unsigned char fileOpcode; int fileSize; int fileCSize; istream *fileStream = NULL; unsigned char *fileHeader = NULL; DisableSignals(); #ifdef DEBUG *logofs << "SplitStore: Going to load split OPCODE#" << (unsigned int) split -> store_ -> opcode() << " from file '" << fileName << "'.\n" << logofs_flush; #endif fileStream = new ifstream(fileName, ios::in | ios::binary); if (CheckData(fileStream) < 0) { #ifdef TEST *logofs << "SplitStore: WARNING! Can't open image file '" << fileName << "' on disk.\n" << logofs_flush; #endif goto SplitStoreLoadError; } fileHeader = new unsigned char[SPLIT_HEADER_SIZE]; if (fileHeader == NULL) { #ifdef PANIC *logofs << "SplitStore: PANIC! Cannot allocate space for " << "NX image header.\n" << logofs_flush; #endif cerr << "Error" << ": Cannot allocate space for " << "NX image header.\n"; goto SplitStoreLoadError; } if (GetData(fileStream, fileHeader, SPLIT_HEADER_SIZE) < 0) { #ifdef PANIC *logofs << "SplitStore: PANIC! Cannot read header from " << "NX image file '" << fileName << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Cannot read header from " << "NX image file '" << fileName << "'.\n"; goto SplitStoreLoadError; } fileOpcode = *fileHeader; fileSize = GetULONG(fileHeader + 4, false); fileCSize = GetULONG(fileHeader + 8, false); // // Don't complain if we find that data was saved // in compressed form even if we were not aware // of the compressed data size. The remote side // compresses the data only at the time it starts // the transferral of the split. We replace our // copy of the data with whatever we find on the // disk. // if (fileOpcode != split -> store_ -> opcode() || fileSize != split -> d_size_ || fileSize > control -> MaximumRequestSize || fileCSize > control -> MaximumRequestSize) { #ifdef TEST *logofs << "SplitStore: PANIC! Corrupted image file '" << fileName << "'. Expected " << (unsigned int) split -> store_ -> opcode() << "/" << split -> d_size_ << "/" << split -> c_size_ << " found " << (unsigned int) fileOpcode << "/" << fileSize << "/" << fileCSize << ".\n" << logofs_flush; #endif cerr << "Warning" << ": Corrupted image file '" << fileName << "'. Expected " << (unsigned int) split -> store_ -> opcode() << "/" << split -> d_size_ << "/" << split -> c_size_ << " found " << (unsigned int) fileOpcode << "/" << fileSize << "/" << fileCSize << ".\n"; goto SplitStoreLoadError; } // // Update the data size with the size // we got from the disk record. // split -> d_size_ = fileSize; split -> c_size_ = fileCSize; unsigned int splitSize; if (fileCSize > 0) { splitSize = fileCSize; } else { splitSize = fileSize; } // // Allocate a new buffer if we didn't // do that already or if the size is // different. // if (split -> data_.size() != splitSize) { split -> data_.clear(); split -> data_.resize(splitSize); } if (GetData(fileStream, split -> data_.begin(), splitSize) < 0) { #ifdef PANIC *logofs << "SplitStore: PANIC! Cannot read data from " << "NX image file '" << fileName << "'.\n" << logofs_flush; #endif cerr << "Warning" << ": Cannot read data from " << "NX image file '" << fileName << "'.\n"; goto SplitStoreLoadError; } delete fileStream; delete [] fileHeader; delete [] fileName; EnableSignals(); // // Update the timestamp as the operation // may have taken some time. // getNewTimestamp(); return 1; SplitStoreLoadError: delete fileStream; unlink(fileName); delete [] fileName; delete [] fileHeader; EnableSignals(); return -1; } Split *CommitStore::pop() { if (splits_ -> size() == 0) { #ifdef TEST *logofs << "CommitStore: The commit store is empty.\n" << logofs_flush; #endif return NULL; } Split *split = *(splits_ -> begin()); splits_ -> pop_front(); #ifdef TEST *logofs << "CommitStore: Removed commit split at the head " << "of the list with resource " << split -> resource_ << " request " << (unsigned) split -> store_ -> opcode() << " position " << split -> position_ << ".\n" << logofs_flush; #endif return split; } int CommitStore::expand(Split *split, unsigned char *buffer, const int size) { #ifdef TEST *logofs << "CommitStore: Expanding split data with " << size << " bytes to write.\n" << logofs_flush; #endif #ifdef TEST if (size < split -> i_size_ + split -> d_size_) { #ifdef PANIC *logofs << "CommitStore: PANIC! Wrong size of the provided " << "buffer. It should be " << split -> i_size_ + split -> d_size_ << " instead of " << size << ".\n" << logofs_flush; #endif cerr << "Error" << ": Wrong size of the provided " << "buffer. It should be " << split -> i_size_ + split -> d_size_ << " instead of " << size << ".\n"; HandleAbort(); } #endif #ifdef DEBUG *logofs << "CommitStore: Copying " << split -> i_size_ << " bytes of identity.\n" << logofs_flush; #endif memcpy(buffer, split -> identity_.begin(), split -> i_size_); // // Copy data, if any, to the buffer. // if (size > split -> i_size_) { // // Check if message has been stored // in compressed format. // if (split -> c_size_ == 0) { #ifdef DEBUG *logofs << "CommitStore: Copying " << split -> d_size_ << " bytes of plain data.\n" << logofs_flush; #endif memcpy(buffer + split -> i_size_, split -> data_.begin(), split -> d_size_); } else { #ifdef DEBUG *logofs << "CommitStore: Decompressing " << split -> c_size_ << " bytes and copying " << split -> d_size_ << " bytes of data.\n" << logofs_flush; #endif if (compressor_ -> decompressBuffer(buffer + split -> i_size_, split -> d_size_, split -> data_.begin(), split -> c_size_) < 0) { #ifdef PANIC *logofs << "CommitStore: PANIC! Split data decompression failed.\n" << logofs_flush; #endif cerr << "Error" << ": Split data decompression failed.\n"; return -1; } } } return 1; } int CommitStore::update(Split *split) { if (split -> action_ != IS_ADDED) { return 0; } // // We don't need the identity data at // the encoding side. // if (split -> identity_.size() == 0) { #ifdef TEST *logofs << "SplitStore: Going to update the size " << "for object at position " << split -> position_ << " with data size " << split -> d_size_ << " and compressed data size " << split -> c_size_ << ".\n" << logofs_flush; #endif split -> store_ -> updateData(split -> position_, split -> d_size_, split -> c_size_); } else { #ifdef TEST *logofs << "SplitStore: Going to update data and size " << "for object at position " << split -> position_ << " with data size " << split -> d_size_ << " and compressed data size " << split -> c_size_ << ".\n" << logofs_flush; #endif split -> store_ -> updateData(split -> position_, split -> data_.begin(), split -> d_size_, split -> c_size_); } // // Unlock message so that we can remove // or save it on disk at shutdown. // if (split -> action_ == IS_ADDED) { split -> store_ -> unlock(split -> position_); #ifdef TEST validate(split); #endif } return 1; } int CommitStore::validate(Split *split) { MessageStore *store = split -> store_; int p, n, s; s = store -> cacheSlots; for (p = 0, n = 0; p < s; p++) { if (store -> getLocks(p) == 1) { n++; } else if (store -> getLocks(p) != 0) { #ifdef PANIC *logofs << "CommitStore: PANIC! Repository for OPCODE#" << (unsigned int) store -> opcode() << " has " << store -> getLocks(p) << " locks for message " << "at position " << p << ".\n" << logofs_flush; #endif cerr << "Error" << ": Repository for OPCODE#" << (unsigned int) store -> opcode() << " has " << store -> getLocks(p) << " locks for message " << "at position " << p << ".\n"; HandleAbort(); } } #ifdef TEST *logofs << "CommitStore: Repository for OPCODE#" << (unsigned int) store -> opcode() << " has " << n << " locked messages.\n" << logofs_flush; #endif return 1; } nxcomp/ClientReadBuffer.cpp0000644000076400007640000001057411323113030016174 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "ClientReadBuffer.h" #include "ClientChannel.h" #define PANIC #define WARNING #undef TEST #undef DEBUG unsigned int ClientReadBuffer::suggestedLength(unsigned int pendingLength) { // // Even if the pending data is not // enough to make a complete message, // resize the buffer to accomodate // it all. // unsigned int readLength = pendingLength; if (pendingLength < remaining_) { readLength = remaining_; } return readLength; } int ClientReadBuffer::locateMessage(const unsigned char *start, const unsigned char *end, unsigned int &controlLength, unsigned int &dataLength, unsigned int &trailerLength) { unsigned int size = end - start; #ifdef TEST *logofs << "ClientReadBuffer: Locating message for FD#" << transport_ -> fd() << " with " << size << " bytes.\n" << logofs_flush; #endif if (firstMessage_) { if (size < 12) { remaining_ = 12 - size; #ifdef TEST *logofs << "ClientReadBuffer: No message was located " << "with remaining " << remaining_ << ".\n" << logofs_flush; #endif return 0; } if (*start == 0x42) { bigEndian_ = 1; } else { bigEndian_ = 0; } channel_ -> setBigEndian(bigEndian_); dataLength = 12 + RoundUp4(GetUINT(start + 6, bigEndian_)) + RoundUp4(GetUINT(start + 8, bigEndian_)); // // Send the data immediately if this is unlikely // to be a X connection attempt. // if (dataLength > 4096) { #ifdef WARNING *logofs << "ClientReadBuffer: WARNING! Flushing suspicious X " << "connection with first request of " << dataLength << " bytes.\n" << logofs_flush; #endif dataLength = size; } } else { if (size < 4) { remaining_ = 4 - size; #ifdef TEST *logofs << "ClientReadBuffer: No message was located " << "with remaining " << remaining_ << ".\n" << logofs_flush; #endif return 0; } dataLength = (GetUINT(start + 2, bigEndian_) << 2); if (dataLength < 4) { #ifdef TEST *logofs << "ClientReadBuffer: WARNING! Assuming length 4 " << "for suspicious message of length " << dataLength << ".\n" << logofs_flush; #endif dataLength = 4; } } #ifdef TEST *logofs << "ClientReadBuffer: Length of the next message is " << dataLength << ".\n" << logofs_flush; #endif if (size < dataLength) { remaining_ = dataLength - size; #ifdef TEST *logofs << "ClientReadBuffer: No message was located " << "with remaining " << remaining_ << ".\n" << logofs_flush; #endif return 0; } firstMessage_ = 0; controlLength = 0; trailerLength = 0; remaining_ = 0; #ifdef TEST *logofs << "ClientReadBuffer: Located message with " << "remaining " << remaining_ << ".\n" << logofs_flush; #endif return 1; } nxcomp/GenericChannel.cpp0000644000076400007640000002637011323113027015704 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include #include #include "GenericChannel.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "StaticCompressor.h" #include "Statistics.h" #include "Proxy.h" extern Proxy *proxy; // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Log the important tracepoints related // to writing packets to the peer proxy. // #undef FLUSH // // Define this to log when a channel // is created or destroyed. // #undef REFERENCES // // Here are the static members. // #ifdef REFERENCES int GenericChannel::references_ = 0; #endif GenericChannel::GenericChannel(Transport *transport, StaticCompressor *compressor) : Channel(transport, compressor), readBuffer_(transport_, this) { #ifdef REFERENCES *logofs << "GenericChannel: Created new object at " << this << " for FD#" << fd_ << " out of " << ++references_ << " allocated channels.\n" << logofs_flush; #endif } GenericChannel::~GenericChannel() { #ifdef REFERENCES *logofs << "GenericChannel: Deleted object at " << this << " for FD#" << fd_ << " out of " << --references_ << " allocated channels.\n" << logofs_flush; #endif } // // Beginning of handleRead(). // int GenericChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, unsigned int length) { #ifdef TEST *logofs << "handleRead: Called for FD#" << fd_ << " with " << encodeBuffer.getLength() << " bytes already encoded.\n" << logofs_flush; #endif // // Pointer to located message and // its size in bytes. // const unsigned char *inputMessage; unsigned int inputLength; // // Tag message as generic data in compression // routine. Opcode is not actually transferred // over the network. // unsigned char inputOpcode = X_NXInternalGenericData; #if defined(TEST) || defined(INFO) *logofs << "handleRead: Trying to read from FD#" << fd_ << " at " << strMsTimestamp() << ".\n" << logofs_flush; #endif int result = readBuffer_.readMessage(); #ifdef DEBUG *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) *logofs << "handleRead: PANIC! No data read from FD#" << fd_ << " while encoding messages.\n" << logofs_flush; HandleCleanup(); #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 // // Divide the available data in multiple // messages and encode them one by one. // if (proxy -> handleAsyncSwitch(fd_) < 0) { return -1; } while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL) { encodeBuffer.encodeValue(inputLength, 32, 14); if (isCompressed() == 1) { unsigned int compressedDataSize = 0; unsigned char *compressedData = NULL; if (handleCompress(encodeBuffer, inputOpcode, 0, inputMessage, inputLength, compressedData, compressedDataSize) < 0) { return -1; } } else { encodeBuffer.encodeMemory(inputMessage, inputLength); } int bits = encodeBuffer.diffBits(); #if defined(TEST) || defined(OPCODES) *logofs << "handleRead: Handled generic data for FD#" << fd_ << ". " << inputLength << " bytes in, " << bits << " bits (" << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; #endif addProtocolBits(inputLength << 3, bits); if (isPrioritized() == 1) { priority_++; } } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL) ... // // All data has been read from the read buffer. // We still need to mark the end of the encode // buffer just before sending the frame. This // allows us to accomodate multiple reads in // a single frame. // 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 enough data or timeout on the " << "proxy link.\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 GenericChannel::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 unsigned char *outputMessage; unsigned int outputLength; // // Tag message as generic data // in decompression. // unsigned char outputOpcode = X_NXInternalGenericData; for (;;) { decodeBuffer.decodeValue(outputLength, 32, 14); if (outputLength == 0) { break; } if (isCompressed() == 1) { if (writeBuffer_.getAvailable() < outputLength || (int) outputLength >= control -> TransportFlushBufferSize) { #ifdef DEBUG *logofs << "handleWrite: Using scratch buffer for " << "generic data with size " << outputLength << " and " << writeBuffer_.getLength() << " bytes in buffer.\n" << logofs_flush; #endif outputMessage = writeBuffer_.addScratchMessage(outputLength); } else { outputMessage = writeBuffer_.addMessage(outputLength); } const unsigned char *compressedData = NULL; unsigned int compressedDataSize = 0; int decompressed = handleDecompress(decodeBuffer, outputOpcode, 0, outputMessage, outputLength, compressedData, compressedDataSize); if (decompressed < 0) { return -1; } } else { #ifdef DEBUG *logofs << "handleWrite: Using scratch buffer for " << "generic data with size " << outputLength << " and " << writeBuffer_.getLength() << " bytes in buffer.\n" << logofs_flush; #endif writeBuffer_.addScratchMessage((unsigned char *) decodeBuffer.decodeMemory(outputLength), outputLength); } #if defined(TEST) || defined(OPCODES) *logofs << "handleWrite: Handled generic data for FD#" << fd_ << ". " << outputLength << " bytes out.\n" << logofs_flush; #endif handleFlush(flush_if_needed); } // // Write any remaining data to socket. // if (handleFlush(flush_if_any) < 0) { return -1; } return 1; } // // End of handleWrite(). // // // Other members. // int GenericChannel::handleCompletion(EncodeBuffer &encodeBuffer) { // // Add the bits telling to the remote // that all data in the frame has been // encoded. // if (encodeBuffer.getLength() > 0) { #if defined(TEST) || defined(INFO) *logofs << "handleCompletion: Writing completion bits with " << encodeBuffer.getLength() << " bytes encoded " << "for FD#" << fd_ << ".\n" << logofs_flush; #endif encodeBuffer.encodeValue(0, 32, 14); return 1; } #if defined(TEST) || defined(INFO) else { *logofs << "handleCompletion: PANIC! No completion to write " << "for FD#" << fd_ << ".\n" << logofs_flush; HandleCleanup(); } #endif return 0; } int GenericChannel::handleConfiguration() { #ifdef TEST *logofs << "GenericChannel: Setting new buffer parameters.\n" << logofs_flush; #endif readBuffer_.setSize(control -> GenericInitialReadSize, control -> GenericMaximumBufferSize); writeBuffer_.setSize(control -> TransportGenericBufferSize, control -> TransportGenericBufferThreshold, control -> TransportMaximumBufferSize); transport_ -> setSize(control -> TransportGenericBufferSize, control -> TransportGenericBufferThreshold, control -> TransportMaximumBufferSize); return 1; } int GenericChannel::handleFinish() { #ifdef TEST *logofs << "GenericChannel: Finishing channel for FD#" << fd_ << ".\n" << logofs_flush; #endif congestion_ = 0; priority_ = 0; finish_ = 1; transport_ -> fullReset(); return 1; } int GenericChannel::setReferences() { #ifdef TEST *logofs << "GenericChannel: Initializing the static " << "members for the generic channels.\n" << logofs_flush; #endif #ifdef REFERENCES references_ = 0; #endif return 1; } nxcomp/BlockCacheSet.cpp0000644000076400007640000000732711323113030015464 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Misc.h" #include "BlockCacheSet.h" BlockCacheSet::BlockCacheSet(unsigned int numCaches): caches_(new BlockCache *[numCaches]), size_(numCaches), length_(0) { for (unsigned int i = 0; i < numCaches; i++) caches_[i] = new BlockCache(); } BlockCacheSet::~BlockCacheSet() { // // TODO: There is still a strange segfault occurring // at random time under Cygwin, when proxy is being // shutdown. Problem appeared just after upgrading // to the latest version of the Cygwin DLL. A stack // trace, obtained at the last minute, reveals that // failure happens in this destructor. // #ifndef __CYGWIN32__ for (unsigned int i = 0; i < size_; i++) delete caches_[i]; delete[]caches_; #endif /* ifdef __CYGWIN32__ */ } int BlockCacheSet::lookup(unsigned int dataLength, const unsigned char *data, unsigned int &index) { unsigned int checksum = BlockCache::checksum(dataLength, data); for (unsigned int i = 0; i < length_; i++) if ((caches_[i]->getChecksum() == checksum) && (caches_[i]->compare(dataLength, data, 0))) { // match index = i; if (i) { BlockCache *save = caches_[i]; unsigned int target = (i >> 1); do { caches_[i] = caches_[i - 1]; i--; } while (i > target); caches_[target] = save; } return 1; } // no match unsigned int insertionPoint = (length_ >> 1); unsigned int start; if (length_ >= size_) start = size_ - 1; else { start = length_; length_++; } BlockCache *save = caches_[start]; for (unsigned int k = start; k > insertionPoint; k--) caches_[k] = caches_[k - 1]; caches_[insertionPoint] = save; save->set(dataLength, data); return 0; } void BlockCacheSet::get(unsigned index, unsigned int &size, const unsigned char *&data) { size = caches_[index]->getLength(); data = caches_[index]->getData(); if (index) { BlockCache *save = caches_[index]; unsigned int target = (index >> 1); do { caches_[index] = caches_[index - 1]; index--; } while (index > target); caches_[target] = save; } } void BlockCacheSet::set(unsigned int dataLength, const unsigned char *data) { unsigned int insertionPoint = (length_ >> 1); unsigned int start; if (length_ >= size_) start = size_ - 1; else { start = length_; length_++; } BlockCache *save = caches_[start]; for (unsigned int k = start; k > insertionPoint; k--) caches_[k] = caches_[k - 1]; caches_[insertionPoint] = save; save->set(dataLength, data); } nxcomp/Auth.h0000644000076400007640000000630711323113027013403 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef Auth_H #define Auth_H #include "Timestamp.h" // // Handle the forwarding of authorization credentials // to the X server by replacing the fake cookie with // the real cookie as it is read from the auth file. // At the moment only the MIT-MAGIC-COOKIE-1 cookies // are recognized. The implementation is based on the // corresponding code found in the SSH client. // class Auth { public: // // Must be created by passing the fake cookie that // will be forwarded by the remote end and with the // real X display that is going to be used for the // session. // Auth(char *display, char *cookie); ~Auth(); int isValid() { return (isTimestamp(last_) == 1 && fakeCookie_ != NULL && *fakeCookie_ != '\0' && realCookie_ != NULL && *realCookie_ != '\0' && fakeData_ != NULL && realData_ != NULL && dataSize_ != 0); } int isFake() const { return generatedCookie_; } // // Method called in the channel class to find if the // provided cookie matches the fake one. If the data // matches, the fake cookie is replaced with the real // one. // int checkCookie(unsigned char *buffer); protected: // // Update the real cookie for the display. If called // a further time, check if the auth file is changed // and get the new cookie. // int updateCookie(); // // Find out which authorization file is to be used // and query the cookie for the current display. // int getCookie(); // // Extract the binary data from the cookies so that // data can be directly compared at the time it is // taken from the X request. // int validateCookie(); // // Generate a fake random cookie and copy it to the // provided string. // void generateCookie(char *cookie); private: char *display_; char *file_; T_timestamp last_; char *fakeCookie_; char *realCookie_; char *fakeData_; char *realData_; int dataSize_; int generatedCookie_; }; #endif /* Auth_H */ nxcomp/OpcodeStore.h0000644000076400007640000000553011323113031014720 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef OpcodeStore_H #define OpcodeStore_H #include "NXproto.h" class OpcodeStore { public: OpcodeStore(); ~OpcodeStore(); // // Map NX protocol messages. At the moment mapping is hard- // coded. Opcodes should be instead agreed with the proxied // X server (by excluding all opcodes used for extensions) // and exported by the proxy class to channels. // // Some toolkits query the server only once for extensions' // opcodes and share the same settings across all channels. // This could be a problem as channels needed to monitor the // traffic to find out the extensions' opcodes themselves, // so it is important that proxy passes an instance of this // class to new channels. // unsigned char getControlParameters; unsigned char getCleanupParameters; unsigned char getImageParameters; unsigned char getUnpackParameters; unsigned char getShmemParameters; unsigned char getFontParameters; unsigned char startSplit; unsigned char endSplit; unsigned char commitSplit; unsigned char finishSplit; unsigned char abortSplit; unsigned char splitData; unsigned char splitEvent; unsigned char setCacheParameters; unsigned char setExposeParameters; unsigned char setUnpackGeometry; unsigned char setUnpackColormap; unsigned char setUnpackAlpha; unsigned char putPackedImage; unsigned char freeUnpack; unsigned char freeSplit; unsigned char shapeExtension; unsigned char renderExtension; unsigned char noSplitNotify; unsigned char startSplitNotify; unsigned char commitSplitNotify; unsigned char endSplitNotify; unsigned char emptySplitNotify; }; #endif /* OpcodeStore_H */ nxcomp/TranslateCoords.h0000644000076400007640000001107311323113030015577 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef TranslateCoords_H #define TranslateCoords_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define TRANSLATECOORDS_ENABLE_CACHE 1 #define TRANSLATECOORDS_ENABLE_DATA 0 #define TRANSLATECOORDS_ENABLE_SPLIT 0 #define TRANSLATECOORDS_ENABLE_COMPRESS 0 #define TRANSLATECOORDS_DATA_LIMIT 0 #define TRANSLATECOORDS_DATA_OFFSET 16 #define TRANSLATECOORDS_CACHE_SLOTS 3000 #define TRANSLATECOORDS_CACHE_THRESHOLD 3 #define TRANSLATECOORDS_CACHE_LOWER_THRESHOLD 1 // // The message class. // class TranslateCoordsMessage : public Message { friend class TranslateCoordsStore; public: TranslateCoordsMessage() { } ~TranslateCoordsMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned int src_window; unsigned int dst_window; unsigned int src_x; unsigned int src_y; unsigned char r_same_screen; unsigned int r_child_window; unsigned int r_dst_x; unsigned int r_dst_y; }; class TranslateCoordsStore : public MessageStore { // // Constructors and destructors. // public: TranslateCoordsStore() : MessageStore() { enableCache = TRANSLATECOORDS_ENABLE_CACHE; enableData = TRANSLATECOORDS_ENABLE_DATA; enableSplit = TRANSLATECOORDS_ENABLE_SPLIT; enableCompress = TRANSLATECOORDS_ENABLE_COMPRESS; dataLimit = TRANSLATECOORDS_DATA_LIMIT; dataOffset = TRANSLATECOORDS_DATA_OFFSET; cacheSlots = TRANSLATECOORDS_CACHE_SLOTS; cacheThreshold = TRANSLATECOORDS_CACHE_THRESHOLD; cacheLowerThreshold = TRANSLATECOORDS_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~TranslateCoordsStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "TranslateCoords"; } virtual unsigned char opcode() const { return X_TranslateCoords; } virtual unsigned int storage() const { return sizeof(TranslateCoordsMessage); } // // Message handling methods. // public: virtual Message *create() const { return new TranslateCoordsMessage(); } virtual Message *create(const Message &message) const { return new TranslateCoordsMessage((const TranslateCoordsMessage &) message); } virtual void destroy(Message *message) const { delete (TranslateCoordsMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* TranslateCoords_H */ nxcomp/XidCache.cpp0000644000076400007640000000272211323113027014502 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Control.h" #include "XidCache.h" XidCache::XidCache() { for (int i = 0; i < 256; i++) { base_[i] = new IntCache(8); } slot_ = 0; last_ = 0; } XidCache::~XidCache() { for (int i = 0; i < 256; i++) { delete base_[i]; } } nxcomp/FillPoly.h0000644000076400007640000001213411323113027014227 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef FillPoly_H #define FillPoly_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define FILLPOLY_ENABLE_CACHE 1 #define FILLPOLY_ENABLE_DATA 0 #define FILLPOLY_ENABLE_SPLIT 0 #define FILLPOLY_ENABLE_COMPRESS 0 #define FILLPOLY_DATA_LIMIT 512 #define FILLPOLY_DATA_OFFSET 16 #define FILLPOLY_CACHE_SLOTS 2000 #define FILLPOLY_CACHE_THRESHOLD 3 #define FILLPOLY_CACHE_LOWER_THRESHOLD 1 #define FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 20 // // The message class. // class FillPolyMessage : public Message { friend class FillPolyStore; public: FillPolyMessage() { } ~FillPolyMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char shape; unsigned char mode; unsigned int drawable; unsigned int gcontext; unsigned short x_origin; unsigned short y_origin; }; class FillPolyStore : public MessageStore { // // Constructors and destructors. // public: FillPolyStore() : MessageStore() { enableCache = FILLPOLY_ENABLE_CACHE; enableData = FILLPOLY_ENABLE_DATA; enableSplit = FILLPOLY_ENABLE_SPLIT; enableCompress = FILLPOLY_ENABLE_COMPRESS; dataLimit = FILLPOLY_DATA_LIMIT; dataOffset = FILLPOLY_DATA_OFFSET; if (control -> isProtoStep8() == 1) { dataOffset = FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8; } cacheSlots = FILLPOLY_CACHE_SLOTS; cacheThreshold = FILLPOLY_CACHE_THRESHOLD; cacheLowerThreshold = FILLPOLY_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~FillPolyStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "FillPoly"; } virtual unsigned char opcode() const { return X_FillPoly; } virtual unsigned int storage() const { return sizeof(FillPolyMessage); } // // Message handling methods. // public: virtual Message *create() const { return new FillPolyMessage(); } virtual Message *create(const Message &message) const { return new FillPolyMessage((const FillPolyMessage &) message); } virtual void destroy(Message *message) const { delete (FillPolyMessage *) message; } virtual int identitySize(const unsigned char *buffer, unsigned int size) { unsigned int offset = (control -> isProtoStep8() == 1 ? FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 : FILLPOLY_DATA_OFFSET); return (size >= offset ? offset : size); } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* FillPoly_H */ nxcomp/Timestamp.cpp0000644000076400007640000000363711323113027015003 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "Timestamp.h" // // Log level. // #define PANIC #define WARNING #undef TEST #undef DEBUG // // Last timestamp taken from the system. // T_timestamp timestamp; // // The following functions all use the ctime // static buffer from the C library. // char *strTimestamp(const T_timestamp &ts) { char *ctime_now = ctime((time_t *) &ts.tv_sec); ctime_now[24] = '\0'; return ctime_now; } // // This is especially dirty. // char *strMsTimestamp(const T_timestamp &ts) { char *ctime_now = ctime((time_t *) &ts.tv_sec); char ctime_new[25]; sprintf(ctime_new, "%.8s:%3.3f", ctime_now + 11, (float) ts.tv_usec / 1000); strncpy(ctime_now, ctime_new, 24); return ctime_now; } nxcomp/ImageText16.h0000644000076400007640000001124111323113031014524 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ImageText16_H #define ImageText16_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define IMAGETEXT16_ENABLE_CACHE 1 #define IMAGETEXT16_ENABLE_DATA 0 #define IMAGETEXT16_ENABLE_SPLIT 0 #define IMAGETEXT16_ENABLE_COMPRESS 0 #define IMAGETEXT16_DATA_LIMIT 512 #define IMAGETEXT16_DATA_OFFSET 16 #define IMAGETEXT16_CACHE_SLOTS 3000 #define IMAGETEXT16_CACHE_THRESHOLD 5 #define IMAGETEXT16_CACHE_LOWER_THRESHOLD 1 // // The message class. // class ImageText16Message : public Message { friend class ImageText16Store; public: ImageText16Message() { } ~ImageText16Message() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char len; unsigned int drawable; unsigned int gcontext; unsigned short x; unsigned short y; }; class ImageText16Store : public MessageStore { // // Constructors and destructors. // public: ImageText16Store() : MessageStore() { enableCache = IMAGETEXT16_ENABLE_CACHE; enableData = IMAGETEXT16_ENABLE_DATA; enableSplit = IMAGETEXT16_ENABLE_SPLIT; enableCompress = IMAGETEXT16_ENABLE_COMPRESS; dataLimit = IMAGETEXT16_DATA_LIMIT; dataOffset = IMAGETEXT16_DATA_OFFSET; cacheSlots = IMAGETEXT16_CACHE_SLOTS; cacheThreshold = IMAGETEXT16_CACHE_THRESHOLD; cacheLowerThreshold = IMAGETEXT16_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~ImageText16Store() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "ImageText16"; } virtual unsigned char opcode() const { return X_ImageText16; } virtual unsigned int storage() const { return sizeof(ImageText16Message); } // // Message handling methods. // public: virtual Message *create() const { return new ImageText16Message(); } virtual Message *create(const Message &message) const { return new ImageText16Message((const ImageText16Message &) message); } virtual void destroy(Message *message) const { delete (ImageText16Message *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* ImageText16_H */ nxcomp/PutPackedImage.cpp0000644000076400007640000004651011323113027015660 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #include "PutPackedImage.h" #include "ClientCache.h" #include "EncodeBuffer.h" #include "DecodeBuffer.h" #include "WriteBuffer.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Constructors and destructors. // PutPackedImageStore::PutPackedImageStore(StaticCompressor *compressor) : MessageStore(compressor) { enableCache = PUTPACKEDIMAGE_ENABLE_CACHE; enableData = PUTPACKEDIMAGE_ENABLE_DATA; enableSplit = PUTPACKEDIMAGE_ENABLE_SPLIT; enableCompress = PUTPACKEDIMAGE_ENABLE_COMPRESS; dataLimit = PUTPACKEDIMAGE_DATA_LIMIT; dataOffset = PUTPACKEDIMAGE_DATA_OFFSET; cacheSlots = PUTPACKEDIMAGE_CACHE_SLOTS; cacheThreshold = PUTPACKEDIMAGE_CACHE_THRESHOLD; cacheLowerThreshold = PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD; if (control -> isProtoStep8() == 1) { enableSplit = PUTPACKEDIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8; } messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } PutPackedImageStore::~PutPackedImageStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } // // Here are the methods to handle messages' content. // int PutPackedImageStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, const unsigned int size, int bigEndian, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; #endif // Client. encodeBuffer.encodeCachedValue(*(buffer + 1), 8, clientCache -> resourceCache); // Size. encodeBuffer.encodeValue(GetUINT(buffer + 2, bigEndian), 16, 10); // Drawable. encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), clientCache -> drawableCache); // GC. encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), clientCache -> gcCache); // Method. encodeBuffer.encodeCachedValue(*(buffer + 12), 8, clientCache -> methodCache); // Format. encodeBuffer.encodeValue(*(buffer + 13), 2); // SrcDepth. encodeBuffer.encodeCachedValue(*(buffer + 14), 8, clientCache -> depthCache); // DstDepth. encodeBuffer.encodeCachedValue(*(buffer + 15), 8, clientCache -> depthCache); // SrcLength. encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 24, clientCache -> putPackedImageSrcLengthCache); // DstLength. encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 24, clientCache -> putPackedImageDstLengthCache); // SrcX. unsigned int x = GetUINT(buffer + 24, bigEndian); int xDiff = x - clientCache -> putImageLastX; clientCache -> putImageLastX = x; encodeBuffer.encodeCachedValue(xDiff, 16, clientCache -> putImageXCache, 8); // SrcY. unsigned int y = GetUINT(buffer + 26, bigEndian); int yDiff = y - clientCache -> putImageLastY; clientCache -> putImageLastY = y; encodeBuffer.encodeCachedValue(yDiff, 16, clientCache -> putImageYCache, 8); // SrcWidth. encodeBuffer.encodeCachedValue(GetUINT(buffer + 28, bigEndian), 16, clientCache -> putImageWidthCache, 8); // SrcHeight. encodeBuffer.encodeCachedValue(GetUINT(buffer + 30, bigEndian), 16, clientCache -> putImageHeightCache, 8); // DstX. x = GetUINT(buffer + 32, bigEndian); xDiff = x - clientCache -> putImageLastX; clientCache -> putImageLastX = x; encodeBuffer.encodeCachedValue(xDiff, 16, clientCache -> putImageXCache, 8); // DstY. y = GetUINT(buffer + 34, bigEndian); yDiff = y - clientCache -> putImageLastY; clientCache -> putImageLastY = y; encodeBuffer.encodeCachedValue(yDiff, 16, clientCache -> putImageYCache, 8); // DstWidth. encodeBuffer.encodeCachedValue(GetUINT(buffer + 36, bigEndian), 16, clientCache -> putImageWidthCache, 8); // DstHeight. encodeBuffer.encodeCachedValue(GetUINT(buffer + 38, bigEndian), 16, clientCache -> putImageHeightCache, 8); #ifdef DEBUG *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; #endif return 1; } int PutPackedImageStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, ChannelCache *channelCache) const { ClientCache *clientCache = (ClientCache *) channelCache; #ifdef DEBUG *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; #endif unsigned int value; unsigned char cValue; // Client. decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> resourceCache); // Size. decodeBuffer.decodeValue(size, 16, 10); size <<= 2; buffer = writeBuffer -> addMessage(size); *(buffer + 1) = cValue; // Drawable. decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); PutULONG(value, buffer + 4, bigEndian); // GC. decodeBuffer.decodeXidValue(value, clientCache -> gcCache); PutULONG(value, buffer + 8, bigEndian); // Method. decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> methodCache); *(buffer + 12) = cValue; // Format. decodeBuffer.decodeValue(value, 2); *(buffer + 13) = value; // SrcDepth. decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); *(buffer + 14) = cValue; // DstDepth. decodeBuffer.decodeCachedValue(cValue, 8, clientCache -> depthCache); *(buffer + 15) = cValue; // SrcLength. decodeBuffer.decodeCachedValue(value, 24, clientCache -> putPackedImageSrcLengthCache); PutULONG(value, buffer + 16, bigEndian); // DstLength. decodeBuffer.decodeCachedValue(value, 24, clientCache -> putPackedImageDstLengthCache); PutULONG(value, buffer + 20, bigEndian); // SrcX. decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageXCache, 8); clientCache -> putImageLastX += value; clientCache -> putImageLastX &= 0xffff; PutUINT(clientCache -> putImageLastX, buffer + 24, bigEndian); // SrcY. decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageYCache, 8); clientCache -> putImageLastY += value; clientCache -> putImageLastY &= 0xffff; PutUINT(clientCache -> putImageLastY, buffer + 26, bigEndian); // SrcWidth. decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageWidthCache, 8); PutUINT(value, buffer + 28, bigEndian); // SrcHeight. decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageHeightCache, 8); PutUINT(value, buffer + 30, bigEndian); // DstX. decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageXCache, 8); clientCache -> putImageLastX += value; clientCache -> putImageLastX &= 0xffff; PutUINT(clientCache -> putImageLastX, buffer + 32, bigEndian); // DstY. decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageYCache, 8); clientCache -> putImageLastY += value; clientCache -> putImageLastY &= 0xffff; PutUINT(clientCache -> putImageLastY, buffer + 34, bigEndian); // DstWidth. decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageWidthCache, 8); PutUINT(value, buffer + 36, bigEndian); // DstHeight. decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageHeightCache, 8); PutUINT(value, buffer + 38, bigEndian); #ifdef DEBUG *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; #endif return 1; } int PutPackedImageStore::parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message; // // Here is the fingerprint. // putPackedImage -> client = *(buffer + 1); putPackedImage -> drawable = GetULONG(buffer + 4, bigEndian); putPackedImage -> gcontext = GetULONG(buffer + 8, bigEndian); putPackedImage -> method = *(buffer + 12); putPackedImage -> format = *(buffer + 13); putPackedImage -> src_depth = *(buffer + 14); putPackedImage -> dst_depth = *(buffer + 15); putPackedImage -> src_length = GetULONG(buffer + 16, bigEndian); putPackedImage -> dst_length = GetULONG(buffer + 20, bigEndian); putPackedImage -> src_x = GetUINT(buffer + 24, bigEndian); putPackedImage -> src_y = GetUINT(buffer + 26, bigEndian); putPackedImage -> src_width = GetUINT(buffer + 28, bigEndian); putPackedImage -> src_height = GetUINT(buffer + 30, bigEndian); putPackedImage -> dst_x = GetUINT(buffer + 32, bigEndian); putPackedImage -> dst_y = GetUINT(buffer + 34, bigEndian); putPackedImage -> dst_width = GetUINT(buffer + 36, bigEndian); putPackedImage -> dst_height = GetUINT(buffer + 38, bigEndian); #ifdef DEBUG *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } int PutPackedImageStore::unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const { PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message; // // Fill all the message's fields. // *(buffer + 1) = putPackedImage -> client; PutULONG(putPackedImage -> drawable, buffer + 4, bigEndian); PutULONG(putPackedImage -> gcontext, buffer + 8, bigEndian); *(buffer + 12) = putPackedImage -> method; *(buffer + 13) = putPackedImage -> format; *(buffer + 14) = putPackedImage -> src_depth; *(buffer + 15) = putPackedImage -> dst_depth; PutULONG(putPackedImage -> src_length, buffer + 16, bigEndian); PutULONG(putPackedImage -> dst_length, buffer + 20, bigEndian); PutUINT(putPackedImage -> src_x, buffer + 24, bigEndian); PutUINT(putPackedImage -> src_y, buffer + 26, bigEndian); PutUINT(putPackedImage -> src_width, buffer + 28, bigEndian); PutUINT(putPackedImage -> src_height, buffer + 30, bigEndian); PutUINT(putPackedImage -> dst_x, buffer + 32, bigEndian); PutUINT(putPackedImage -> dst_y, buffer + 34, bigEndian); PutUINT(putPackedImage -> dst_width, buffer + 36, bigEndian); PutUINT(putPackedImage -> dst_height, buffer + 38, bigEndian); #ifdef DEBUG *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; #endif return 1; } void PutPackedImageStore::dumpIdentity(const Message *message) const { #ifdef DUMP PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message; *logofs << name() << ": Identity format " << "drawable " << putPackedImage -> drawable << ", " << "gcontext " << putPackedImage -> gcontext << ", " << "format " << (unsigned int) putPackedImage -> format << ", " << "method " << (unsigned int) putPackedImage -> method << ", " << "src_depth " << (unsigned int) putPackedImage -> src_depth << ", " << "dst_depth " << (unsigned int) putPackedImage -> dst_depth << ", " << "src_length " << putPackedImage -> src_length << ", " << "dst_length " << putPackedImage -> dst_length << ", " << "src_x " << putPackedImage -> src_x << ", " << "src_y " << putPackedImage -> src_y << ", " << "src_width " << putPackedImage -> src_width << ", " << "src_height " << putPackedImage -> src_height << ", " << "dst_x " << putPackedImage -> dst_x << ", " << "dst_y " << putPackedImage -> dst_y << ", " << "dst_width " << putPackedImage -> dst_width << ", " << "dst_height " << putPackedImage -> dst_height << ", " << "size " << putPackedImage -> size_ << ".\n" << logofs_flush; #endif } void PutPackedImageStore::identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const { // // Fields method, format, src_depth, dst_depth, // src_length, dst_length, src_x, src_y, src_width, // src_height. // // // TODO: We should better investigate the effect of // having fields src_x and src_y in identity instead // of keeping them in differences. // md5_append(md5_state_, buffer + 12, 20); } void PutPackedImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const { // // Encode the variant part. // PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message; PutPackedImageMessage *cachedPutPackedImage = (PutPackedImageMessage *) cachedMessage; ClientCache *clientCache = (ClientCache *) channelCache; #ifdef TEST *logofs << name() << ": Encoding value " << (unsigned int) putPackedImage -> client << " as client field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(putPackedImage -> client, 8, clientCache -> resourceCache); cachedPutPackedImage -> client = putPackedImage -> client; #ifdef TEST *logofs << name() << ": Encoding value " << putPackedImage -> drawable << " as drawable field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(putPackedImage -> drawable, clientCache -> drawableCache); cachedPutPackedImage -> drawable = putPackedImage -> drawable; #ifdef TEST *logofs << name() << ": Encoding value " << putPackedImage -> gcontext << " as gcontext field.\n" << logofs_flush; #endif encodeBuffer.encodeXidValue(putPackedImage -> gcontext, clientCache -> gcCache); cachedPutPackedImage -> gcontext = putPackedImage -> gcontext; #ifdef TEST *logofs << name() << ": Encoding value " << putPackedImage -> dst_x << " as " << "dst_x" << " field.\n" << logofs_flush; #endif unsigned short int diff_x = putPackedImage -> dst_x - cachedPutPackedImage -> dst_x; encodeBuffer.encodeCachedValue(diff_x, 16, clientCache -> putImageXCache, 8); cachedPutPackedImage -> dst_x = putPackedImage -> dst_x; #ifdef TEST *logofs << name() << ": Encoding value " << putPackedImage -> dst_y << " as " << "dst_y" << " field.\n" << logofs_flush; #endif unsigned short int diff_y = putPackedImage -> dst_y - cachedPutPackedImage -> dst_y; encodeBuffer.encodeCachedValue(diff_y, 16, clientCache -> putImageYCache, 8); cachedPutPackedImage -> dst_y = putPackedImage -> dst_y; #ifdef TEST *logofs << name() << ": Encoding value " << putPackedImage -> dst_width << " as " << "dst_width" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(putPackedImage -> dst_width, 16, clientCache -> putImageWidthCache, 8); cachedPutPackedImage -> dst_width = putPackedImage -> dst_width; #ifdef TEST *logofs << name() << ": Encoding value " << putPackedImage -> dst_height << " as " << "dst_height" << " field.\n" << logofs_flush; #endif encodeBuffer.encodeCachedValue(putPackedImage -> dst_height, 16, clientCache -> putImageHeightCache, 8); cachedPutPackedImage -> dst_height = putPackedImage -> dst_height; } void PutPackedImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const { // // Decode the variant part. // PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message; ClientCache *clientCache = (ClientCache *) channelCache; unsigned int value; decodeBuffer.decodeCachedValue(putPackedImage -> client, 8, clientCache -> resourceCache); #ifdef DEBUG *logofs << name() << ": Decoded value " << (unsigned int) putPackedImage -> client << " as client field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); putPackedImage -> drawable = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << putPackedImage -> drawable << " as drawable field.\n" << logofs_flush; #endif decodeBuffer.decodeXidValue(value, clientCache -> gcCache); putPackedImage -> gcontext = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << putPackedImage -> gcontext << " as gcontext field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageXCache, 8); putPackedImage -> dst_x += value; putPackedImage -> dst_x &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << putPackedImage -> dst_x << " as dst_x field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageYCache, 8); putPackedImage -> dst_y += value; putPackedImage -> dst_y &= 0xffff; #ifdef DEBUG *logofs << name() << ": Decoded value " << putPackedImage -> dst_y << " as dst_y field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageWidthCache, 8); putPackedImage -> dst_width = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << putPackedImage -> dst_width << " as dst_width field.\n" << logofs_flush; #endif decodeBuffer.decodeCachedValue(value, 16, clientCache -> putImageHeightCache, 8); putPackedImage -> dst_height = value; #ifdef DEBUG *logofs << name() << ": Decoded value " << putPackedImage -> dst_height << " as dst_height field.\n" << logofs_flush; #endif } nxcomp/ChangeProperty.h0000644000076400007640000001150611323113027015431 0ustar svetonisvetoni/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ /* */ /* NXCOMP, NX protocol compression and NX extensions to this software */ /* are copyright of NoMachine. Redistribution and use of the present */ /* software is allowed according to terms specified in the file LICENSE */ /* which comes in the source distribution. */ /* */ /* Check http://www.nomachine.com/licensing.html for applicability. */ /* */ /* NX and NoMachine are trademarks of Medialogic S.p.A. */ /* */ /* All rights reserved. */ /* */ /**************************************************************************/ #ifndef ChangeProperty_H #define ChangeProperty_H #include "Message.h" // // Set the verbosity level. // #define PANIC #define WARNING #undef TEST #undef DEBUG #undef DUMP // // Set default values. // #define CHANGEPROPERTY_ENABLE_CACHE 1 #define CHANGEPROPERTY_ENABLE_DATA 0 #define CHANGEPROPERTY_ENABLE_SPLIT 0 #define CHANGEPROPERTY_ENABLE_COMPRESS 0 #define CHANGEPROPERTY_DATA_LIMIT 28688 #define CHANGEPROPERTY_DATA_OFFSET 24 #define CHANGEPROPERTY_CACHE_SLOTS 2000 #define CHANGEPROPERTY_CACHE_THRESHOLD 2 #define CHANGEPROPERTY_CACHE_LOWER_THRESHOLD 1 // // The message class. // class ChangePropertyMessage : public Message { friend class ChangePropertyStore; public: ChangePropertyMessage() { } ~ChangePropertyMessage() { } // // Put here the fields which constitute // the 'identity' part of the message. // private: unsigned char mode; unsigned char format; unsigned int window; unsigned int property; unsigned int type; unsigned int length; }; class ChangePropertyStore : public MessageStore { // // Constructors and destructors. // public: ChangePropertyStore() : MessageStore() { enableCache = CHANGEPROPERTY_ENABLE_CACHE; enableData = CHANGEPROPERTY_ENABLE_DATA; enableSplit = CHANGEPROPERTY_ENABLE_SPLIT; enableCompress = CHANGEPROPERTY_ENABLE_COMPRESS; dataLimit = CHANGEPROPERTY_DATA_LIMIT; dataOffset = CHANGEPROPERTY_DATA_OFFSET; cacheSlots = CHANGEPROPERTY_CACHE_SLOTS; cacheThreshold = CHANGEPROPERTY_CACHE_THRESHOLD; cacheLowerThreshold = CHANGEPROPERTY_CACHE_LOWER_THRESHOLD; messages_ -> resize(cacheSlots); for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { *i = NULL; } temporary_ = NULL; } virtual ~ChangePropertyStore() { for (T_messages::iterator i = messages_ -> begin(); i < messages_ -> end(); i++) { destroy(*i); } destroy(temporary_); } virtual const char *name() const { return "ChangeProperty"; } virtual unsigned char opcode() const { return X_ChangeProperty; } virtual unsigned int storage() const { return sizeof(ChangePropertyMessage); } // // Message handling methods. // public: virtual Message *create() const { return new ChangePropertyMessage(); } virtual Message *create(const Message &message) const { return new ChangePropertyMessage((const ChangePropertyMessage &) message); } virtual void destroy(Message *message) const { delete (ChangePropertyMessage *) message; } virtual int parseIdentity(Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual int unparseIdentity(const Message *message, unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, const Message *cachedMessage, ChannelCache *channelCache) const; virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, ChannelCache *channelCache) const; virtual void identityChecksum(const Message *message, const unsigned char *buffer, unsigned int size, int bigEndian) const; virtual void dumpIdentity(const Message *message) const; }; #endif /* ChangeProperty_H */