diff options
Diffstat (limited to 'nxcomp/src')
226 files changed, 103327 insertions, 0 deletions
diff --git a/nxcomp/src/ActionCache.cpp b/nxcomp/src/ActionCache.cpp new file mode 100644 index 000000000..783b1724e --- /dev/null +++ b/nxcomp/src/ActionCache.cpp @@ -0,0 +1,51 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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]; + } +} diff --git a/nxcomp/src/ActionCache.h b/nxcomp/src/ActionCache.h new file mode 100644 index 000000000..2aedd4a07 --- /dev/null +++ b/nxcomp/src/ActionCache.h @@ -0,0 +1,49 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Agent.cpp b/nxcomp/src/Agent.cpp new file mode 100644 index 000000000..e55620601 --- /dev/null +++ b/nxcomp/src/Agent.cpp @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} diff --git a/nxcomp/src/Agent.h b/nxcomp/src/Agent.h new file mode 100644 index 000000000..3e1a50ae5 --- /dev/null +++ b/nxcomp/src/Agent.h @@ -0,0 +1,263 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Agent_H +#define Agent_H + +#include <unistd.h> + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> + +#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) + { + // OS X 10.5 requires the second argument to be non-const, so copy readSet. + // It's safe though, as FD_ISSET does not operate on it. + fd_set readWorkSet = *readSet; + + #if defined(TEST) || defined(INFO) + *logofs << "Agent: remoteCanRead() is " << + (FD_ISSET(remoteFd_, &readWorkSet) && transport_ -> dequeuable() != 0) + << " with FD_ISSET() " << (int) FD_ISSET(remoteFd_, &readWorkSet) + << " and dequeuable " << transport_ -> dequeuable() + << ".\n" << logofs_flush; + #endif + + return (FD_ISSET(remoteFd_, &readWorkSet) && + transport_ -> dequeuable() != 0); + } + + int remoteCanWrite(const fd_set * const writeSet) + { + // OS X 10.5 requires the second argument to be non-const, so copy writeSet. + // It's safe though, as FD_ISSET does not operate on it. + fd_set writeWorkSet = *writeSet; + + #if defined(TEST) || defined(INFO) + *logofs << "Agent: remoteCanWrite() is " << + (FD_ISSET(remoteFd_, &writeWorkSet) && transport_ -> + queuable() != 0 && canRead_ == 1) << " with FD_ISSET() " + << (int) FD_ISSET(remoteFd_, &writeWorkSet) << " queueable " + << transport_ -> queuable() << " channel can read " + << canRead_ << ".\n" << logofs_flush; + #endif + + return (FD_ISSET(remoteFd_, &writeWorkSet) && + 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) + { + // OS X 10.5 requires the second argument to be non-const, so copy readSet. + // It's safe though, as FD_ISSET does not operate on it. + fd_set readWorkSet = *readSet; + + #if defined(TEST) || defined(INFO) + *logofs << "Agent: proxyCanRead() is " + << ((int) FD_ISSET(proxy -> getFd(), &readWorkSet)) + << ".\n" << logofs_flush; + #endif + + return (FD_ISSET(proxy -> getFd(), &readWorkSet)); + } + + 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 */ diff --git a/nxcomp/src/Alpha.cpp b/nxcomp/src/Alpha.cpp new file mode 100644 index 000000000..6157e21e5 --- /dev/null +++ b/nxcomp/src/Alpha.cpp @@ -0,0 +1,138 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/Alpha.h b/nxcomp/src/Alpha.h new file mode 100644 index 000000000..ea5068812 --- /dev/null +++ b/nxcomp/src/Alpha.h @@ -0,0 +1,35 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Auth.cpp b/nxcomp/src/Auth.cpp new file mode 100644 index 000000000..d42c0556d --- /dev/null +++ b/nxcomp/src/Auth.cpp @@ -0,0 +1,671 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 we assume that + // the nxauth command is located under bin in the client + // installation directory. On Mac OS X we assume that the + // command is located directly in the client installation + // directory, to make bundle shipping easier. On all the + // other platforms we use the default xauth command that + // is in our path. + // + + char command[DEFAULT_STRING_LIMIT]; + + #if defined(__CYGWIN32__) + + snprintf(command, DEFAULT_STRING_LIMIT - 1, + "%s/bin/nxauth", control -> SystemPath); + + *(command + DEFAULT_STRING_LIMIT - 1) = '\0'; + + #elif defined(__APPLE__) + + snprintf(command, DEFAULT_STRING_LIMIT - 1, + "%s/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 +} diff --git a/nxcomp/src/Auth.h b/nxcomp/src/Auth.h new file mode 100644 index 000000000..d51d9a26f --- /dev/null +++ b/nxcomp/src/Auth.h @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Bitmap.cpp b/nxcomp/src/Bitmap.cpp new file mode 100644 index 000000000..c89df53e3 --- /dev/null +++ b/nxcomp/src/Bitmap.cpp @@ -0,0 +1,118 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/Bitmap.h b/nxcomp/src/Bitmap.h new file mode 100644 index 000000000..8143e3125 --- /dev/null +++ b/nxcomp/src/Bitmap.h @@ -0,0 +1,36 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/BlockCache.cpp b/nxcomp/src/BlockCache.cpp new file mode 100644 index 000000000..161145e3d --- /dev/null +++ b/nxcomp/src/BlockCache.cpp @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include "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; +} diff --git a/nxcomp/src/BlockCache.h b/nxcomp/src/BlockCache.h new file mode 100644 index 000000000..48e586966 --- /dev/null +++ b/nxcomp/src/BlockCache.h @@ -0,0 +1,67 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/BlockCacheSet.cpp b/nxcomp/src/BlockCacheSet.cpp new file mode 100644 index 000000000..1dd6361dc --- /dev/null +++ b/nxcomp/src/BlockCacheSet.cpp @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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); +} diff --git a/nxcomp/src/BlockCacheSet.h b/nxcomp/src/BlockCacheSet.h new file mode 100644 index 000000000..97273b0e0 --- /dev/null +++ b/nxcomp/src/BlockCacheSet.h @@ -0,0 +1,49 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/ChangeGC.cpp b/nxcomp/src/ChangeGC.cpp new file mode 100644 index 000000000..e92178f27 --- /dev/null +++ b/nxcomp/src/ChangeGC.cpp @@ -0,0 +1,184 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} diff --git a/nxcomp/src/ChangeGC.h b/nxcomp/src/ChangeGC.h new file mode 100644 index 000000000..9cac90e66 --- /dev/null +++ b/nxcomp/src/ChangeGC.h @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/ChangeProperty.cpp b/nxcomp/src/ChangeProperty.cpp new file mode 100644 index 000000000..74814c2ea --- /dev/null +++ b/nxcomp/src/ChangeProperty.cpp @@ -0,0 +1,191 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} + + diff --git a/nxcomp/src/ChangeProperty.h b/nxcomp/src/ChangeProperty.h new file mode 100644 index 000000000..c06ce10fc --- /dev/null +++ b/nxcomp/src/ChangeProperty.h @@ -0,0 +1,189 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Channel.cpp b/nxcomp/src/Channel.cpp new file mode 100644 index 000000000..24a422e37 --- /dev/null +++ b/nxcomp/src/Channel.cpp @@ -0,0 +1,2039 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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) + { + // Since ProtoStep7 (#issue 108) + #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) + { + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeActionValue(is_discarded, + store -> lastActionCache); + + 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; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeActionValue(is_removed, store -> lastRemoved, + store -> lastActionCache); + + #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 + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeActionValue(is_discarded, + store -> lastActionCache); + + 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 + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeActionValue(is_discarded, + store -> lastActionCache); + + 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"; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeActionValue(is_discarded, + store -> lastActionCache); + + 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; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeActionValue(IS_ADDED, store -> lastAdded, + store -> lastActionCache); + + 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; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeActionValue(IS_HIT, store -> lastHit, + store -> lastActionCache); + + // + // 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; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeActionValue(action, position, + store -> lastActionCache); + + // + // Clean operations must always come + // before any operation on message. + // + + while (action == is_removed) + { + // Since ProtoStep7 (#issue 108) + store -> lastRemoved = position; + + #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); + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeActionValue(action, position, + store -> lastActionCache); + } + + // + // If it's a cache hit, the position + // where object can be found follows. + // + + if ((T_store_action) action == IS_HIT) + { + // Since ProtoStep7 (#issue 108) + store -> lastHit = position; + + // + // 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) + { + // Since ProtoStep7 (#issue 108) + store -> lastAdded = position; + + #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 + + // Since ProtoStep7 (#issue 108) + if (resource != split -> getResource() || + 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; +} diff --git a/nxcomp/src/Channel.h b/nxcomp/src/Channel.h new file mode 100644 index 000000000..7e432416a --- /dev/null +++ b/nxcomp/src/Channel.h @@ -0,0 +1,664 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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) __attribute__((noreturn)); + + 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 */ diff --git a/nxcomp/src/ChannelCache.cpp b/nxcomp/src/ChannelCache.cpp new file mode 100644 index 000000000..f30f18bc1 --- /dev/null +++ b/nxcomp/src/ChannelCache.cpp @@ -0,0 +1,68 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +}; diff --git a/nxcomp/src/ChannelCache.h b/nxcomp/src/ChannelCache.h new file mode 100644 index 000000000..6a29c3847 --- /dev/null +++ b/nxcomp/src/ChannelCache.h @@ -0,0 +1,61 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef ChannelCache_H +#define ChannelCache_H + +// +// 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 */ diff --git a/nxcomp/src/ChannelEndPoint.cpp b/nxcomp/src/ChannelEndPoint.cpp new file mode 100644 index 000000000..921615bae --- /dev/null +++ b/nxcomp/src/ChannelEndPoint.cpp @@ -0,0 +1,349 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/stat.h> + +#include "ChannelEndPoint.h" + +#include "NXalert.h" + +ChannelEndPoint::ChannelEndPoint(const char *spec) + : defaultTCPPort_(0), defaultTCPInterface_(0), + defaultUnixPath_(NULL), spec_(NULL) { + setSpec(spec); +} + +ChannelEndPoint::~ChannelEndPoint() +{ + char *unixPath = NULL; + + if (getUnixPath(&unixPath)) + { + struct stat st; + lstat(unixPath, &st); + if(S_ISSOCK(st.st_mode)) + unlink(unixPath); + } +} + +void +ChannelEndPoint::setSpec(const char *spec) { + if (spec_) free(spec_); + + if (spec && strlen(spec)) + { + spec_ = strdup(spec); + isUnix_ = getUnixPath(); + isTCP_ = getTCPHostAndPort(); + } + else + { + spec_ = NULL; + isUnix_ = false; + isTCP_ = false; + } +} + +void +ChannelEndPoint::setSpec(long port) { + if (port >= 0) { + char tmp[20]; + sprintf(tmp, "%ld", port); + setSpec(tmp); + } + else { + disable(); + } +} + +void +ChannelEndPoint::setSpec(const char *hostName, long port) { + int length; + + if (spec_) free(spec_); + isUnix_ = false; + isTCP_ = false; + + if (hostName && strlen(hostName) && port >= 1) + { + length = snprintf(NULL, 0, "tcp:%s:%ld", hostName, port); + spec_ = (char *)calloc(length + 1, sizeof(char)); + snprintf(spec_, length+1, "tcp:%s:%ld", hostName, port); + isTCP_ = true; + } + else setSpec((char*)NULL); +} + +bool +ChannelEndPoint::getSpec(char **socketUri) const { + + if (socketUri) *socketUri = NULL; + + char *unixPath = NULL; + char *hostName = NULL; + long port = -1; + + char *newSocketUri = NULL; + int length = -1; + + if (getUnixPath(&unixPath)) + { + length = snprintf(NULL, 0, "unix:%s", unixPath); + } + else if (getTCPHostAndPort(&hostName, &port)) + { + length = snprintf(NULL, 0, "tcp:%s:%ld", hostName, port); + } + + if (length > 0) + { + newSocketUri = (char *)calloc(length + 1, sizeof(char)); + if (isUnixSocket()) + snprintf(newSocketUri, length+1, "unix:%s", unixPath); + else + snprintf(newSocketUri, length+1, "tcp:%s:%ld", hostName, port); + + if (socketUri) + *socketUri = strdup(newSocketUri); + } + + free(newSocketUri); + free(unixPath); + free(hostName); + + if (NULL != *socketUri) + return true; + + return false; +} + +void +ChannelEndPoint::setDefaultTCPPort(long port) { + defaultTCPPort_ = port; +} + +void +ChannelEndPoint::setDefaultTCPInterface(int publicInterface) { + defaultTCPInterface_ = publicInterface; +} + +void +ChannelEndPoint::setDefaultUnixPath(char *path) { + if (defaultUnixPath_) free(defaultUnixPath_); + + if (path && strlen(path)) + defaultUnixPath_ = strdup(path); + else + defaultUnixPath_ = NULL; +} + +void +ChannelEndPoint::disable() { + setSpec("0"); +} + +bool +ChannelEndPoint::getPort(long *port) const { + if (port) *port = 0; + long p = -1; + if (spec_) { + char *end; + p = strtol(spec_, &end, 10); + if ((end == spec_) || (*end != '\0')) + return false; + } + + if (port) *port = p; + return true; +} + +bool +ChannelEndPoint::getUnixPath(char **unixPath) const { + + if (unixPath) *unixPath = 0; + + long p; + char *path = NULL; + + if (getPort(&p)) { + if (p != 1) return false; + } + else if (spec_ && (strncmp("unix:", spec_, 5) == 0)) { + path = spec_ + 5; + } + else + return false; + + if (!path || (*path == '\0')) { + path = defaultUnixPath_; + if (!path) + return false; + } + + if (unixPath) + *unixPath = strdup(path); + + return true; +} + +bool +ChannelEndPoint::isUnixSocket() const { + return isUnix_; +} + +// FIXME!!! +static const char * +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; +} + +bool +ChannelEndPoint::getTCPHostAndPort(char **host, long *port) const { + long p; + char *h = NULL; + ssize_t h_len; + + if (host) *host = NULL; + if (port) *port = 0; + + if (getPort(&p)) { + h_len = 0; + } + else if (spec_ && (strncmp("tcp:", spec_, 4) == 0)) { + h = spec_ + 4; + char *colon = strrchr(h, ':'); + if (colon) { + char *end; + h_len = colon++ - h; + p = strtol(colon, &end, 10); + if ((end == colon) || (*end != '\0')) + return false; + } + else { + h_len = strlen(h); + p = 1; + } + } + else + return false; + + if (p == 1) p = defaultTCPPort_; + if (p < 1) return false; + + if (port) + *port = p; + + if (host) + *host = ( h_len + ? strndup(h, h_len) + : strdup(defaultTCPInterface_ ? getComputerName() : "localhost")); + + return true; +} + +bool +ChannelEndPoint::isTCPSocket() const { + return isTCP_; +} + +long ChannelEndPoint::getTCPPort() const { + long port; + if (getTCPHostAndPort(NULL, &port)) return port; + return -1; +} + +bool +ChannelEndPoint::enabled() const { + return (isUnixSocket() || isTCPSocket()); +} + +bool +ChannelEndPoint::validateSpec() { + isTCP_ = getTCPHostAndPort(); + isUnix_ = getUnixPath(); + return ( getPort() || isUnix_ || isTCP_ ); +} + +ChannelEndPoint &ChannelEndPoint::operator=(const ChannelEndPoint &other) { + char *old; + defaultTCPPort_ = other.defaultTCPPort_; + defaultTCPInterface_ = other.defaultTCPInterface_; + old = defaultUnixPath_; + defaultUnixPath_ = (other.defaultUnixPath_ ? strdup(other.defaultUnixPath_) : NULL); + free(old); + old = spec_; + spec_ = (other.spec_ ? strdup(other.spec_) : NULL); + free(old); + isUnix_ = getUnixPath(); + isTCP_ = getTCPHostAndPort(); + return *this; +} + +std::ostream& operator<<(std::ostream& os, const ChannelEndPoint& endPoint) { + if (endPoint.enabled()) { + char* endPointSpec = NULL; + if (endPoint.getSpec(&endPointSpec)) + { + os << endPointSpec; + free(endPointSpec); + } + else + os << "(invalid)"; + } + else + { + os << "(disabled)"; + } + return os; +} diff --git a/nxcomp/src/ChannelEndPoint.h b/nxcomp/src/ChannelEndPoint.h new file mode 100644 index 000000000..4c0c728f3 --- /dev/null +++ b/nxcomp/src/ChannelEndPoint.h @@ -0,0 +1,71 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef ChannelEndPoint_H +#define ChannelEndPoint_H + +#include <iostream> +#include <sys/un.h> + +class ChannelEndPoint +{ + private: + long defaultTCPPort_; + int defaultTCPInterface_; // 0=localhost, otherwise IP of public interface. + char *defaultUnixPath_; + char *spec_; + bool isUnix_; + bool isTCP_; + + bool getPort(long *port = NULL) const; + + public: + ChannelEndPoint(const char *spec = NULL); + ~ChannelEndPoint(); + ChannelEndPoint &operator=(const ChannelEndPoint &other); + + bool enabled() const; + bool disabled() { return !enabled(); } + void disable(); + void setSpec(const char *spec); + void setSpec(long port); + void setSpec(const char *hostName, long port); + bool getSpec(char **socketUri) const; + void setDefaultTCPPort(long port); + void setDefaultTCPInterface(int publicInterface); + void setDefaultUnixPath(char *path); + + bool getUnixPath(char **path = NULL) const; + bool isUnixSocket() const; + bool getTCPHostAndPort(char **hostname = NULL, long *port = NULL) const; + long getTCPPort() const; + bool isTCPSocket() const; + + bool validateSpec(); +}; + +std::ostream& operator<<(std::ostream& os, const ChannelEndPoint& endPoint); + +#endif diff --git a/nxcomp/src/ChannelStore.h b/nxcomp/src/ChannelStore.h new file mode 100644 index 000000000..53bb60f73 --- /dev/null +++ b/nxcomp/src/ChannelStore.h @@ -0,0 +1,54 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/CharCache.cpp b/nxcomp/src/CharCache.cpp new file mode 100644 index 000000000..ed0e5a02a --- /dev/null +++ b/nxcomp/src/CharCache.cpp @@ -0,0 +1,73 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/CharCache.h b/nxcomp/src/CharCache.h new file mode 100644 index 000000000..b8891d2df --- /dev/null +++ b/nxcomp/src/CharCache.h @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Children.cpp b/nxcomp/src/Children.cpp new file mode 100644 index 000000000..e77d1defd --- /dev/null +++ b/nxcomp/src/Children.cpp @@ -0,0 +1,1059 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> +#include <sys/types.h> +#include <signal.h> +#include <fcntl.h> + +#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 (*signalHandler)(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. + // + + if (i == 0) + { + + 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. + // + + if (i == 0) + { + + 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); + + signalHandler = 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; +} diff --git a/nxcomp/src/ClearArea.cpp b/nxcomp/src/ClearArea.cpp new file mode 100644 index 000000000..0cb152d95 --- /dev/null +++ b/nxcomp/src/ClearArea.cpp @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 " << (unsigned int) clearArea -> 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); +} diff --git a/nxcomp/src/ClearArea.h b/nxcomp/src/ClearArea.h new file mode 100644 index 000000000..8067edffd --- /dev/null +++ b/nxcomp/src/ClearArea.h @@ -0,0 +1,182 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/ClientCache.cpp b/nxcomp/src/ClientCache.cpp new file mode 100644 index 000000000..47bb7db1d --- /dev/null +++ b/nxcomp/src/ClientCache.cpp @@ -0,0 +1,392 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ClientCache.h" + +ClientCache::ClientCache() : + + freeGCCache(16), freeDrawableCache(16), freeWindowCache(16), + + cursorCache(16), colormapCache(16), visualCache(16), lastFont(0), + + changePropertyPropertyCache(16), changePropertyTypeCache(16), + changePropertyData32Cache(16), + + 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), + + polySegmentCacheX(8), polySegmentCacheY(8), polySegmentCacheIndex(0), + + polyTextLastX(0), polyTextLastY(0), polyTextCacheX(8), + polyTextCacheY(8), polyTextFontCache(8), + + 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), + + 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]; + } +} diff --git a/nxcomp/src/ClientCache.h b/nxcomp/src/ClientCache.h new file mode 100644 index 000000000..ed3361097 --- /dev/null +++ b/nxcomp/src/ClientCache.h @@ -0,0 +1,417 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 "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. + // + + 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; + + // + // 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; + + // + // 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; + + // + // 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]; + + 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 */ diff --git a/nxcomp/src/ClientChannel.cpp b/nxcomp/src/ClientChannel.cpp new file mode 100644 index 000000000..f01ad6d7b --- /dev/null +++ b/nxcomp/src/ClientChannel.cpp @@ -0,0 +1,7881 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include <X11/X.h> +#include <X11/Xatom.h> + +#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 <set> +#include <map> + +typedef set < unsigned int, less<unsigned int> > T_windows; +typedef set < unsigned int, less<unsigned int> > T_pixmaps; +typedef map < unsigned int, unsigned int, less<unsigned int> > 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; + + // + // 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. + // + + // Since ProtoStep7 (#issue 108) + 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_++; + } + 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) + { + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, dataLength); + } + 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; + } + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> lastId, clientCache_ -> lastIdCache, + clientCache_ -> gcCache, + clientCache_ -> freeGCCache); + + 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); + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> lastId, clientCache_ -> lastIdCache, + clientCache_ -> windowCache, + clientCache_ -> freeWindowCache); + + 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); + + // Since ProtoStep10 (#issue 108) + encodeBuffer.encodeCachedValue(numPoints, 16, + 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 + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> freeGCCache); + } + 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 + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> freeDrawableCache); + } + 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); + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, textLength); + nextSrc += textLength; + } + } + 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); + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, textLength * 2); + nextSrc += textLength * 2; + } + } + 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; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, textLength); + } + 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; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, textLength * 2); + } + 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; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, nameLength); + + 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; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, textLength); + + 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; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, textLength); + + 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) + { + // Since ProtoStep7 (#issue 108) + 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; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, nameLength); + } + 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); + + // Since ProtoStep9 (#issue 108) + encodeBuffer.encodeValue(numRectangles, 15, 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; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, nameLength); + } + 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; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, _length); + nextDest += _length; + } + + 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_); + + // Since ProtoStep8 (#issue 108) + 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) + + // Since ProtoStep8 (#issue 108) + *logofs << "handleSplit: PANIC! SPLIT! Split should " + << "not be enabled for message " << "OPCODE#" + << (unsigned int) store -> opcode() << ".\n" + << logofs_flush; + + HandleCleanup(); + + #endif + + // + // Refuse the split if it is not introduced + // by a start split. + // + + // Since ProtoStep7 (#issue 108) + 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 + + // Since ProtoStep7 (#issue 108) + + // + // 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::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 (since ProtoStep7 #issue 108). + // + + if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) || + (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); + + #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. + // + + // Since ProtoStep7 (#issue 108) + 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. + // + + // Since ProtoStep7 (#issue 108) + 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; +} diff --git a/nxcomp/src/ClientChannel.h b/nxcomp/src/ClientChannel.h new file mode 100644 index 000000000..ae92648d5 --- /dev/null +++ b/nxcomp/src/ClientChannel.h @@ -0,0 +1,434 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 -> TaintReplies > 0 && + opcode == X_GetInputFocus) + { + return handleTaintSyncRequest(opcode, buffer, size); + } + + #ifdef LAME + + return handleTaintLameRequest(opcode, buffer, size); + + #endif + + return 0; + } + + 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 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 */ diff --git a/nxcomp/src/ClientProxy.cpp b/nxcomp/src/ClientProxy.cpp new file mode 100644 index 000000000..3574d3bf3 --- /dev/null +++ b/nxcomp/src/ClientProxy.cpp @@ -0,0 +1,553 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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(ChannelEndPoint &cupsServerPort, + ChannelEndPoint &smbServerPort, + ChannelEndPoint &mediaServerPort, + ChannelEndPoint &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 handleNewGenericConnectionFromProxyTCP(channelId, channel_font, "localhost", + port, "font"); + } + else + { + // + // Connect to the Unix path. + // + + return handleNewGenericConnectionFromProxyUnix(channelId, channel_font, + 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 number of available " + << "channels exceeded.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Maximum number 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 number of available " + << "channels exceeded.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Maximum number 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; +} + diff --git a/nxcomp/src/ClientProxy.h b/nxcomp/src/ClientProxy.h new file mode 100644 index 000000000..b89785b1a --- /dev/null +++ b/nxcomp/src/ClientProxy.h @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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(ChannelEndPoint &cupsServerPort, + ChannelEndPoint &smbServerPort, + ChannelEndPoint &mediaServerPort, + ChannelEndPoint &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) + { + // Since ProtoStep7 (#issue 108) + return ((channelId & control -> ChannelMask) != 0); + } + + // + // Ports where to forward extended services' + // TCP connections. + // + + private: + + char *fontServerPort_; +}; + + +#endif /* ClientProxy_H */ diff --git a/nxcomp/src/ClientReadBuffer.cpp b/nxcomp/src/ClientReadBuffer.cpp new file mode 100644 index 000000000..c8f4f69a4 --- /dev/null +++ b/nxcomp/src/ClientReadBuffer.cpp @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 accommodate + // 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; +} diff --git a/nxcomp/src/ClientReadBuffer.h b/nxcomp/src/ClientReadBuffer.h new file mode 100644 index 000000000..c557417fa --- /dev/null +++ b/nxcomp/src/ClientReadBuffer.h @@ -0,0 +1,65 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/ClientStore.cpp b/nxcomp/src/ClientStore.cpp new file mode 100644 index 000000000..0c12eefd8 --- /dev/null +++ b/nxcomp/src/ClientStore.cpp @@ -0,0 +1,226 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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" + +// +// 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); + + // Since ProtoStep7 (#issue 108) + requests_[X_ChangeGC] = new ChangeGCStore(); + requests_[X_CreatePixmap] = new CreatePixmapStore(); + requests_[X_NXSetUnpackColormap] = new SetUnpackColormapStore(compressor); + requests_[X_NXSetUnpackAlpha] = new SetUnpackAlphaStore(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(); + } +} diff --git a/nxcomp/src/ClientStore.h b/nxcomp/src/ClientStore.h new file mode 100644 index 000000000..009d87d9f --- /dev/null +++ b/nxcomp/src/ClientStore.h @@ -0,0 +1,143 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Colormap.cpp b/nxcomp/src/Colormap.cpp new file mode 100644 index 000000000..afe99ecae --- /dev/null +++ b/nxcomp/src/Colormap.cpp @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/Colormap.h b/nxcomp/src/Colormap.h new file mode 100644 index 000000000..a96d003fa --- /dev/null +++ b/nxcomp/src/Colormap.h @@ -0,0 +1,32 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/ConfigureWindow.cpp b/nxcomp/src/ConfigureWindow.cpp new file mode 100644 index 000000000..32d3153b9 --- /dev/null +++ b/nxcomp/src/ConfigureWindow.cpp @@ -0,0 +1,142 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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); +} diff --git a/nxcomp/src/ConfigureWindow.h b/nxcomp/src/ConfigureWindow.h new file mode 100644 index 000000000..e02c2aae1 --- /dev/null +++ b/nxcomp/src/ConfigureWindow.h @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Control.cpp b/nxcomp/src/Control.cpp new file mode 100644 index 000000000..75fd56e3f --- /dev/null +++ b/nxcomp/src/Control.cpp @@ -0,0 +1,822 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "NX.h" +#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 + +// +// Protocol version compatibility values +// + +const int Control::NX_MIN_PROTO_STEP = 10; +const int Control::NX_MAX_PROTO_STEP = 10; +const char* const Control::NXPROXY_COMPATIBILITY_VERSION = "3.5.0"; + +// +// 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; + RemoteVersionMaintenancePatch = -1; + + CompatVersionMajor = -1; + CompatVersionMinor = -1; + CompatVersionPatch = -1; + CompatVersionMaintenancePatch = -1; + + LocalVersionMajor = NXMajorVersion(); + LocalVersionMinor = NXMinorVersion(); + LocalVersionPatch = NXPatchVersion(); + LocalVersionMaintenancePatch = NXMaintenancePatchVersion(); + + #ifdef TEST + *logofs << "Control: Major version is " << LocalVersionMajor + << " minor is " << LocalVersionMinor << " patch is " + << LocalVersionPatch << " Maintenance version is " + << LocalVersionMaintenancePatch << ".\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. + // + + protoStep_ = 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) +{ + if (isValidProtoStep(step)) + { + protoStep_ = step; + } + else + { + #ifdef PANIC + *logofs << "Control: PANIC! Invalid protocol step " + << "with value " << step << ".\n" + << logofs_flush; + #endif + + HandleCleanup(); + } +} + +int Control::getProtoStep() +{ + if (isValidProtoStep(protoStep_)) + { + return protoStep_; + } + 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; +} diff --git a/nxcomp/src/Control.h b/nxcomp/src/Control.h new file mode 100644 index 000000000..764fca2c1 --- /dev/null +++ b/nxcomp/src/Control.h @@ -0,0 +1,764 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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. + // + + /* + * LocalVersionMaintenancePatch, RemoteVersionMaintenancePatch + * CompatVersionMaintenancePatch + * + * currently not used, for future compatibility checks + */ + int LocalVersionMajor; + int LocalVersionMinor; + int LocalVersionPatch; + int LocalVersionMaintenancePatch; + + int RemoteVersionMajor; + int RemoteVersionMinor; + int RemoteVersionPatch; + int RemoteVersionMaintenancePatch; + + int CompatVersionMajor; + int CompatVersionMinor; + int CompatVersionPatch; + int CompatVersionMaintenancePatch; + + // + // Compatibility version for the proxy + // + + static const char* const NXPROXY_COMPATIBILITY_VERSION; + + // + // 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(); + + private: + + // + // Look in Control.cpp. + // + + void setLocalUnpackMethods(); + + // + // Manage the encoding according + // to the protocol version. + // + + int protoStep_; + + // + // Min and max values allowed for protocol step + // depending on protocol version compatibility + // + + static const int NX_MIN_PROTO_STEP; + static const int NX_MAX_PROTO_STEP; + + // + // Check the validity of protocol step + // + + bool isValidProtoStep(int step) + { + return ((step >= NX_MIN_PROTO_STEP) && (step <= NX_MAX_PROTO_STEP)); + } + +}; + +#endif /* Control_H */ diff --git a/nxcomp/src/CopyArea.cpp b/nxcomp/src/CopyArea.cpp new file mode 100644 index 000000000..c2a19c2df --- /dev/null +++ b/nxcomp/src/CopyArea.cpp @@ -0,0 +1,199 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} + + diff --git a/nxcomp/src/CopyArea.h b/nxcomp/src/CopyArea.h new file mode 100644 index 000000000..6b2617875 --- /dev/null +++ b/nxcomp/src/CopyArea.h @@ -0,0 +1,192 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/CreateGC.cpp b/nxcomp/src/CreateGC.cpp new file mode 100644 index 000000000..34978de13 --- /dev/null +++ b/nxcomp/src/CreateGC.cpp @@ -0,0 +1,194 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + + // Since ProtoStep7 (#issue 108) + #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; +} + +void CreateGCStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + CreateGCMessage *createGC = (CreateGCMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + // Since ProtoStep7 (#issue 108) + 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 +} diff --git a/nxcomp/src/CreateGC.h b/nxcomp/src/CreateGC.h new file mode 100644 index 000000000..03e27d685 --- /dev/null +++ b/nxcomp/src/CreateGC.h @@ -0,0 +1,186 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/CreatePixmap.cpp b/nxcomp/src/CreatePixmap.cpp new file mode 100644 index 000000000..a60134cd5 --- /dev/null +++ b/nxcomp/src/CreatePixmap.cpp @@ -0,0 +1,280 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} diff --git a/nxcomp/src/CreatePixmap.h b/nxcomp/src/CreatePixmap.h new file mode 100644 index 000000000..0a3212dd9 --- /dev/null +++ b/nxcomp/src/CreatePixmap.h @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/DecodeBuffer.cpp b/nxcomp/src/DecodeBuffer.cpp new file mode 100644 index 000000000..4c1530d9b --- /dev/null +++ b/nxcomp/src/DecodeBuffer.cpp @@ -0,0 +1,639 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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) +{ + // Since ProtoStep7 (#issue 108) + 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) + { + // Since ProtoStep8 (#issue 108) + 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); +} + diff --git a/nxcomp/src/DecodeBuffer.h b/nxcomp/src/DecodeBuffer.h new file mode 100644 index 000000000..f5f84c54f --- /dev/null +++ b/nxcomp/src/DecodeBuffer.h @@ -0,0 +1,138 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef DecodeBuffer_H +#define DecodeBuffer_H + +#include <string.h> + +#include "IntCache.h" +#include "CharCache.h" +#include "XidCache.h" +#include "FreeCache.h" +#include "OpcodeCache.h" +#include "ActionCache.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 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 */ diff --git a/nxcomp/src/EncodeBuffer.cpp b/nxcomp/src/EncodeBuffer.cpp new file mode 100644 index 000000000..e112113a8 --- /dev/null +++ b/nxcomp/src/EncodeBuffer.cpp @@ -0,0 +1,623 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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. + // + + // Since ProtoStep8 (#issue 108) + #ifdef DUMP + *logofs << "EncodeBuffer: Encoded missed int using " + << diffBits() << " bits out of " << numBits + << ".\n" << logofs_flush; + #endif + + encodeValue(value, numBits, blockSize); + } +} + +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++; + } + + // Since ProtoStep7 (#issue 108) + if (length > 0) + { + 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 accommodate " << numBytes + << " bytes .\n" << logofs_flush; + #endif + + cerr << "Error" << ": Error in context [C] " + << "growing encode buffer to accommodate " + << 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); +} diff --git a/nxcomp/src/EncodeBuffer.h b/nxcomp/src/EncodeBuffer.h new file mode 100644 index 000000000..67f6ff093 --- /dev/null +++ b/nxcomp/src/EncodeBuffer.h @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef EncodeBuffer_H +#define EncodeBuffer_H + +#include "IntCache.h" +#include "CharCache.h" +#include "XidCache.h" +#include "FreeCache.h" +#include "OpcodeCache.h" +#include "ActionCache.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 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 */ diff --git a/nxcomp/src/FillPoly.cpp b/nxcomp/src/FillPoly.cpp new file mode 100644 index 000000000..b5928d5cf --- /dev/null +++ b/nxcomp/src/FillPoly.cpp @@ -0,0 +1,239 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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); + + // Since ProtoStep8 (#issue 108) + if (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; + + // Since ProtoStep8 (#issue 108) + if (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; + + // Since ProtoStep8 (#issue 108) + if (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 + + // Since ProtoStep8 (#issue 108) + if (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 + } +} + + diff --git a/nxcomp/src/FillPoly.h b/nxcomp/src/FillPoly.h new file mode 100644 index 000000000..4ceb96c09 --- /dev/null +++ b/nxcomp/src/FillPoly.h @@ -0,0 +1,200 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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; + + // Since ProtoStep8 (#issue 108) + 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) + { + // Since ProtoStep8 (#issue 108) + return (size >= FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 ? + FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 : 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 */ diff --git a/nxcomp/src/Fork.cpp b/nxcomp/src/Fork.cpp new file mode 100644 index 000000000..657c36134 --- /dev/null +++ b/nxcomp/src/Fork.cpp @@ -0,0 +1,110 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> + +#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; +} diff --git a/nxcomp/src/Fork.h b/nxcomp/src/Fork.h new file mode 100644 index 000000000..94238ed90 --- /dev/null +++ b/nxcomp/src/Fork.h @@ -0,0 +1,31 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +// +// Try again if the fork() fails, as it can happen +// often on Cygwin. +// + +extern int Fork(); diff --git a/nxcomp/src/FreeCache.h b/nxcomp/src/FreeCache.h new file mode 100644 index 000000000..bf5c801e5 --- /dev/null +++ b/nxcomp/src/FreeCache.h @@ -0,0 +1,42 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef FreeCache_H +#define FreeCache_H + +#include "IntCache.h" + +class FreeCache : public IntCache +{ + public: + + FreeCache(unsigned int size) + + : IntCache(size) + { + } +}; + +#endif /* FreeCache_H */ diff --git a/nxcomp/src/GenericChannel.cpp b/nxcomp/src/GenericChannel.cpp new file mode 100644 index 000000000..877412cee --- /dev/null +++ b/nxcomp/src/GenericChannel.cpp @@ -0,0 +1,495 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/socket.h> + +#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 accommodate 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; +} diff --git a/nxcomp/src/GenericChannel.h b/nxcomp/src/GenericChannel.h new file mode 100644 index 000000000..3df18f444 --- /dev/null +++ b/nxcomp/src/GenericChannel.h @@ -0,0 +1,440 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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() + { + // Since ProtoStep8 (#issue 108) + 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() + { + // Since ProtoStep8 (#issue 108) + 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() + { + // Since ProtoStep8 (#issue 108) + 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() + { + // Since ProtoStep8 (#issue 108) + 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 */ diff --git a/nxcomp/src/GenericReadBuffer.cpp b/nxcomp/src/GenericReadBuffer.cpp new file mode 100644 index 000000000..78217157e --- /dev/null +++ b/nxcomp/src/GenericReadBuffer.cpp @@ -0,0 +1,82 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/GenericReadBuffer.h b/nxcomp/src/GenericReadBuffer.h new file mode 100644 index 000000000..5ea4d939d --- /dev/null +++ b/nxcomp/src/GenericReadBuffer.h @@ -0,0 +1,61 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/GenericReply.cpp b/nxcomp/src/GenericReply.cpp new file mode 100644 index 000000000..b56e579e8 --- /dev/null +++ b/nxcomp/src/GenericReply.cpp @@ -0,0 +1,302 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + + // Since ProtoStep7 (#issue 108) + 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 + } +} diff --git a/nxcomp/src/GenericReply.h b/nxcomp/src/GenericReply.h new file mode 100644 index 000000000..e899b8467 --- /dev/null +++ b/nxcomp/src/GenericReply.h @@ -0,0 +1,161 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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 */ diff --git a/nxcomp/src/GenericRequest.cpp b/nxcomp/src/GenericRequest.cpp new file mode 100644 index 000000000..c569d6a24 --- /dev/null +++ b/nxcomp/src/GenericRequest.cpp @@ -0,0 +1,334 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + + // Since ProtoStep7 (#issue 108) + enableCompress = GENERICREQUEST_ENABLE_COMPRESS_IF_PROTO_STEP_7; + + 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 + } +} diff --git a/nxcomp/src/GenericRequest.h b/nxcomp/src/GenericRequest.h new file mode 100644 index 000000000..13ffea718 --- /dev/null +++ b/nxcomp/src/GenericRequest.h @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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 */ diff --git a/nxcomp/src/GetImage.cpp b/nxcomp/src/GetImage.cpp new file mode 100644 index 000000000..20b6a07d1 --- /dev/null +++ b/nxcomp/src/GetImage.cpp @@ -0,0 +1,177 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} + diff --git a/nxcomp/src/GetImage.h b/nxcomp/src/GetImage.h new file mode 100644 index 000000000..1c3558d14 --- /dev/null +++ b/nxcomp/src/GetImage.h @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/GetImageReply.cpp b/nxcomp/src/GetImageReply.cpp new file mode 100644 index 000000000..5100804ab --- /dev/null +++ b/nxcomp/src/GetImageReply.cpp @@ -0,0 +1,194 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + + // Since ProtoStep7 (#issue 108) + 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); +} diff --git a/nxcomp/src/GetImageReply.h b/nxcomp/src/GetImageReply.h new file mode 100644 index 000000000..d4f7c4267 --- /dev/null +++ b/nxcomp/src/GetImageReply.h @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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 */ diff --git a/nxcomp/src/GetProperty.cpp b/nxcomp/src/GetProperty.cpp new file mode 100644 index 000000000..1c5e77ccc --- /dev/null +++ b/nxcomp/src/GetProperty.cpp @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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); +} diff --git a/nxcomp/src/GetProperty.h b/nxcomp/src/GetProperty.h new file mode 100644 index 000000000..46727280b --- /dev/null +++ b/nxcomp/src/GetProperty.h @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/GetPropertyReply.cpp b/nxcomp/src/GetPropertyReply.cpp new file mode 100644 index 000000000..bf6879c5f --- /dev/null +++ b/nxcomp/src/GetPropertyReply.cpp @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + + // Since ProtoStep7 (#issue 108) + 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 +{ +} diff --git a/nxcomp/src/GetPropertyReply.h b/nxcomp/src/GetPropertyReply.h new file mode 100644 index 000000000..01634b408 --- /dev/null +++ b/nxcomp/src/GetPropertyReply.h @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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 */ diff --git a/nxcomp/src/ImageText16.cpp b/nxcomp/src/ImageText16.cpp new file mode 100644 index 000000000..af057635f --- /dev/null +++ b/nxcomp/src/ImageText16.cpp @@ -0,0 +1,231 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} + + diff --git a/nxcomp/src/ImageText16.h b/nxcomp/src/ImageText16.h new file mode 100644 index 000000000..98462ab35 --- /dev/null +++ b/nxcomp/src/ImageText16.h @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/ImageText8.cpp b/nxcomp/src/ImageText8.cpp new file mode 100644 index 000000000..61fcef825 --- /dev/null +++ b/nxcomp/src/ImageText8.cpp @@ -0,0 +1,231 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} + + diff --git a/nxcomp/src/ImageText8.h b/nxcomp/src/ImageText8.h new file mode 100644 index 000000000..aa9ccb5d9 --- /dev/null +++ b/nxcomp/src/ImageText8.h @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/IntCache.cpp b/nxcomp/src/IntCache.cpp new file mode 100644 index 000000000..c72b81200 --- /dev/null +++ b/nxcomp/src/IntCache.cpp @@ -0,0 +1,230 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "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 +} diff --git a/nxcomp/src/IntCache.h b/nxcomp/src/IntCache.h new file mode 100644 index 000000000..69c522325 --- /dev/null +++ b/nxcomp/src/IntCache.h @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/InternAtom.cpp b/nxcomp/src/InternAtom.cpp new file mode 100644 index 000000000..9d0ab1b6d --- /dev/null +++ b/nxcomp/src/InternAtom.cpp @@ -0,0 +1,131 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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); +} diff --git a/nxcomp/src/InternAtom.h b/nxcomp/src/InternAtom.h new file mode 100644 index 000000000..6e69eca24 --- /dev/null +++ b/nxcomp/src/InternAtom.h @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Jpeg.cpp b/nxcomp/src/Jpeg.cpp new file mode 100644 index 000000000..2e8044049 --- /dev/null +++ b/nxcomp/src/Jpeg.cpp @@ -0,0 +1,890 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <X11/Xmd.h> + +#ifdef ANDROID +#include <strings.h> +#endif +#include <unistd.h> +#include <setjmp.h> +#include <zlib.h> + +#ifdef __cplusplus + +extern "C" +{ + #include <stdio.h> + #include <jpeglib.h> +} + +#else + +#include <stdio.h> +#include <jpeglib.h> + +#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) __attribute__((noreturn)); + +// +// 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, TRUE); + + 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, TRUE); + + 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, TRUE); + + 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 TRUE; +} + +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; +} diff --git a/nxcomp/src/Jpeg.h b/nxcomp/src/Jpeg.h new file mode 100644 index 000000000..58a5bffef --- /dev/null +++ b/nxcomp/src/Jpeg.h @@ -0,0 +1,36 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Keeper.cpp b/nxcomp/src/Keeper.cpp new file mode 100644 index 000000000..4babbe8a6 --- /dev/null +++ b/nxcomp/src/Keeper.cpp @@ -0,0 +1,612 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> + +#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 +} diff --git a/nxcomp/src/Keeper.h b/nxcomp/src/Keeper.h new file mode 100644 index 000000000..b440beceb --- /dev/null +++ b/nxcomp/src/Keeper.h @@ -0,0 +1,199 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ + diff --git a/nxcomp/src/List.cpp b/nxcomp/src/List.cpp new file mode 100644 index 000000000..b2db7151c --- /dev/null +++ b/nxcomp/src/List.cpp @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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); + } +} diff --git a/nxcomp/src/List.h b/nxcomp/src/List.h new file mode 100644 index 000000000..31c80f835 --- /dev/null +++ b/nxcomp/src/List.h @@ -0,0 +1,95 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/ListFontsReply.cpp b/nxcomp/src/ListFontsReply.cpp new file mode 100644 index 000000000..16be522a1 --- /dev/null +++ b/nxcomp/src/ListFontsReply.cpp @@ -0,0 +1,213 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + + // Since ProtoStep7 (#issue 108) + 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); +} diff --git a/nxcomp/src/ListFontsReply.h b/nxcomp/src/ListFontsReply.h new file mode 100644 index 000000000..c731878e0 --- /dev/null +++ b/nxcomp/src/ListFontsReply.h @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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 */ diff --git a/nxcomp/src/Loop.cpp b/nxcomp/src/Loop.cpp new file mode 100644 index 000000000..b6c82a2b4 --- /dev/null +++ b/nxcomp/src/Loop.cpp @@ -0,0 +1,16685 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <signal.h> +#include <setjmp.h> + +#include <math.h> +#include <ctype.h> +#include <string.h> +#include <dirent.h> +#include <pwd.h> + +#include <fcntl.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/resource.h> +#include <sys/utsname.h> +#include <sys/un.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "Misc.h" + +#ifdef __sun +#include <strings.h> +#endif + +// +// MacOSX 10.4 defines socklen_t. This is +// intended to ensure compatibility with +// older versions. +// + +#ifdef __APPLE__ +#include <AvailabilityMacros.h> +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_3 +typedef int socklen_t; +#endif +#endif + +#ifdef _AIX +#include <strings.h> +#include <sys/select.h> +#endif + +#ifndef __CYGWIN32__ +#include <netinet/tcp.h> +#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" +#include "ChannelEndPoint.h" + +// +// System specific defines. +// + + +// +// 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 (connectSocket.enabled()) + +// +// 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 && \ + listenSocket.enabled()) + +// +// 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 (*signalHandler)(int) = NULL; + +// +// Signal handling functions (that are not already mentioned in Misc.h). +// + +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(); + +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 (that are not already mentioned in Misc.h). +// + +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 &addr_family, sockaddr *&addr, + unsigned int &addr_length); + +// +// Setup a listening socket and accept +// a new connection. +// + +static int ListenConnection(ChannelEndPoint &endPoint, const char *label); +static int ListenConnectionTCP(const char *host, long port, const char *label); +static int ListenConnectionUnix(const char *path, const char *label); +static int ListenConnectionAny(sockaddr *addr, socklen_t addrlen, const char *label); +static int AcceptConnection(int fd, int domain, const char *label); + +// +// Other convenience functions. +// + +static int PrepareProxyConnectionTCP(char** hostName, long int* portNum, int* timeout, int* proxyFileDescriptor, int* reason); +static int PrepareProxyConnectionUnix(char** path, int* timeout, int* proxyFileDescriptor, int* reason); + +static int WaitForRemote(ChannelEndPoint &socketAddress); +static int ConnectToRemote(ChannelEndPoint &socketAddress); + +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 void SetAndValidateChannelEndPointArg(const char *type, const char *name, const char *value, + ChannelEndPoint &endPoint); +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, long &port); + +// +// Translate a font server port specification +// to the corresponding Unix socket path. +// + +static int ParseFontPath(char *path); + +// +// 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 &lTs, T_timestamp &nTs); + +// +// 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 acceptHost[DEFAULT_STRING_LENGTH] = { 0 }; +static char displayHost[DEFAULT_STRING_LENGTH] = { 0 }; +static char authCookie[DEFAULT_STRING_LENGTH] = { 0 }; + +static int loopbackBind = DEFAULT_LOOPBACK_BIND; +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 representation of a Unix socket path or +// a bind address, denoting where the local proxy +// will await the peer connection. +// + +static ChannelEndPoint listenSocket; + +// +// The TCP host and port or Unix file socket where +// the remote proxy will be contacted. +// + +static ChannelEndPoint connectSocket; + +// +// Helper channels are disabled by default. +// + +static ChannelEndPoint cupsPort; +static ChannelEndPoint auxPort; +static ChannelEndPoint smbPort; +static ChannelEndPoint mediaPort; +static ChannelEndPoint httpPort; +static ChannelEndPoint slavePort; + +// +// 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; + +// +// 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(); +} + +void NXTransCleanupForReconnect() +{ + HandleCleanupForReconnect(); +} + +// +// 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(); + + int 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) +{ + int diffTs; + #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) + { + + char *socketUri = NULL; + + // Let's make sure, the default value for listenSocket is properly set. Doing this + // here, because we have to make sure that we call it after the connectSocket + // declaration is really really complete. + + if (listenSocket.disabled() && connectSocket.disabled()) + { + char listenPortValue[20] = { 0 }; + sprintf(listenPortValue, "%ld", (long)(proxyPort + DEFAULT_NX_PROXY_PORT_OFFSET)); + + SetAndValidateChannelEndPointArg("local", "listen", listenPortValue, listenSocket); + } + + #ifdef TEST + connectSocket.getSpec(&socketUri); + *logofs << "Loop: connectSocket is "<< ( connectSocket.enabled() ? "enabled" : "disabled") << ". " + << "The socket URI is '"<< ( socketUri != NULL ? socketUri : "<unset>") << "'.\n" << logofs_flush; + listenSocket.getSpec(&socketUri); + *logofs << "Loop: listenSocket is "<< ( listenSocket.enabled() ? "enabled" : "disabled") << ". " + << "The socket URI is '"<< ( socketUri != NULL ? socketUri : "<unset>") << "'.\n" << logofs_flush; + free(socketUri); + socketUri = NULL; + #endif + + if (WE_INITIATE_CONNECTION) + { + if (connectSocket.getSpec(&socketUri)) + { + #ifdef TEST + *logofs << "Loop: Going to connect to '" << socketUri + << "'.\n" << logofs_flush; + #endif + free(socketUri); + + proxyFD = ConnectToRemote(connectSocket); + + #ifdef TEST + *logofs << "Loop: Connected to remote proxy on FD#" + << proxyFD << ".\n" << logofs_flush; + #endif + + cerr << "Info" << ": Connected to remote proxy on FD#" + << proxyFD << ".\n"; + } + } + else + { + + if (listenSocket.isTCPSocket() && (listenSocket.getTCPPort() < 0)) + { + listenSocket.setSpec(DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort); + } + + if (listenSocket.getSpec(&socketUri)) + { + #ifdef TEST + *logofs << "Loop: Going to wait for connection at '" + << socketUri << "'.\n" << logofs_flush; + #endif + free(socketUri); + + proxyFD = WaitForRemote(listenSocket); + + #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. + // + // either listenSocket or connectSocket is used here... + + if(listenSocket.isTCPSocket() || connectSocket.isTCPSocket()) + + 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. + // + + return ListenConnectionTCP((loopbackBind ? "localhost" : "*"), X_TCP_PORT + proxyPort, "X11"); +} + +int SetupUnixSocket() +{ + // + // Open UNIX domain socket for display. + // + + if (!control->TempPath) { + #ifdef PANIC + *logofs << "Loop: PANIC! Temporal path is null.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Temporal path is null.\n"; + HandleCleanup(); + } + + unsigned int required = snprintf(unixSocketName, DEFAULT_STRING_LENGTH, "%s/.X11-unix", control->TempPath); + if (required < sizeof(unixSocketName)) { + + // No need to execute the following actions conditionally + mkdir(unixSocketName, (0777 | S_ISVTX)); + chmod(unixSocketName, (0777 | S_ISVTX)); + + required = snprintf(unixSocketName, DEFAULT_STRING_LENGTH, "%s/.X11-unix/X%d", control->TempPath, proxyPort); + if (required < sizeof(unixSocketName)) { + + unixFD = ListenConnectionUnix(unixSocketName, "x11"); + if (unixFD >= 0) + chmod(unixSocketName, 0777); + return unixFD; + } + } + + unixSocketName[0] = '\0'; // Just in case! + + #ifdef PANIC + *logofs << "Loop: PANIC! path for unix socket is too long.\n" << logofs_flush; + #endif + + cerr << "Error" << ": path for Unix socket is too long.\n"; + HandleCleanup(); +} + +// +// The following is a dumb copy-paste. The +// nxcompsh library should offer a better +// implementation. +// + +int SetupDisplaySocket(int &addr_family, sockaddr *&addr, + unsigned int &addr_length) +{ + addr_family = AF_INET; + addr = NULL; + addr_length = 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/nx:", 6) == 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) || (strncasecmp(display, "/private/tmp/com.apple.launchd", 30) == 0)) + { + #ifdef TEST + *logofs << "Loop: Using launchd service on socket '" + << display << "'.\n" << logofs_flush; + #endif + + useLaunchdSocket = 1; + } + + #endif + + char *separator = strrchr(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; + + addr_family = 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. + // + + // Try abstract X11 socket first (via a test connect), if that fails + // fall back to Unix domain socket file. + + #ifdef __linux__ + int testSocketFD; + testSocketFD = socket(addr_family, SOCK_STREAM, PF_UNSPEC); + + int len = sprintf(unixSocketName + 1, "/tmp/.X11-unix/X%d", xPort); + unixSocketName[0] = '\0'; + + sockaddr_un *xServerAddrABSTRACT = new sockaddr_un; + memset(xServerAddrABSTRACT, 0, addr_length); + xServerAddrABSTRACT -> sun_family = AF_UNIX; + memcpy(xServerAddrABSTRACT -> sun_path, unixSocketName, len+1); + addr_length = len +3; + + int ret = connect(testSocketFD, (struct sockaddr *) xServerAddrABSTRACT, addr_length); + if (ret == 0) { + + cerr << "Info" << ": Using abstract X11 socket in kernel namespace " + << "for accessing DISPLAY=:" << xPort << ".\n"; + + close(testSocketFD); + addr = (sockaddr *) xServerAddrABSTRACT; + return 1; + + } else { + + cerr << "Info" << ": Falling back to file system X11 socket " + << "for accessing DISPLAY=:" << xPort << ".\n"; + + #endif + + 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); + + addr = (sockaddr *) xServerAddrUNIX; + addr_length = sizeof(sockaddr_un); + + #ifdef __linux__ + + } + #endif + } + else + { + // + // TCP port. + // + + #ifdef TEST + *logofs << "Loop: Using real X server on TCP port.\n" + << logofs_flush; + #endif + + addr_family = 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; + + addr = (sockaddr *) xServerAddrTCP; + addr_length = 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 + { + // + // Get ready to listen for the font server connections + // + + if (useFontSocket) + { + // Since ProtoStep7 (#issue 108) + int port = atoi(fontPort); + + if ((fontFD = ListenConnectionTCP("localhost", port, "font")) < 0) + { + useFontSocket = 0; + } + } + + useCupsSocket = 0; + useAuxSocket = 0; + useSmbSocket = 0; + useMediaSocket = 0; + useHttpSocket = 0; + } + + // + // Slave channels can be originated + // by both sides. + // + + if (useSlaveSocket) + { + // Since ProtoStep7 (#issue 108) + if ((slaveFD = ListenConnection(slavePort, "slave")) < 0) + { + useSlaveSocket = 0; + } + } + + return 1; +} + +int ListenConnectionAny(sockaddr *addr, socklen_t addrlen, const char *label) +{ + int newFD = socket(addr->sa_family, SOCK_STREAM, PF_UNSPEC); + + if (newFD == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed for " << label + << " socket. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed for " << label + << " socket. Error is " << EGET() << " '" + << ESTR() << "'.\n"; + + goto SetupSocketError; + } + + if (addr->sa_family == AF_INET) + if (SetReuseAddress(newFD) < 0) + { + // SetReuseAddress already warns with an error + goto SetupSocketError; + } + + if (bind(newFD, addr, addrlen) == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to bind failed for " << label + << ". Error is " << EGET() + << " '" << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to bind failed for " << label + << ". Error is " << EGET() + << " '" << ESTR() << "'.\n"; + goto SetupSocketError; + } + + if (listen(newFD, 8) == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to listen failed for " << label + << ". Error is " << EGET() + << " '" << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to listen failed for " << label + << ". 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(); + return -1; +} + +int ListenConnectionUnix(const char *path, const char *label) +{ + + sockaddr_un unixAddr; + unixAddr.sun_family = AF_UNIX; +#ifdef UNIX_PATH_MAX + if (strlen(path) >= UNIX_PATH_MAX) +#else + if (strlen(path) >= sizeof(unixAddr.sun_path)) +#endif + { + #ifdef PANIC + *logofs << "Loop: PANIC! Socket path \"" << path << "\" for " + << label << " is too long.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Socket path \"" << path << "\" for " + << label << " is too long.\n"; + + HandleCleanup(); + return -1; + } + + strcpy(unixAddr.sun_path, path); + return ListenConnectionAny((sockaddr *)&unixAddr, sizeof(unixAddr), label); +} + +int ListenConnectionTCP(const char *host, long port, const char *label) +{ + sockaddr_in tcpAddr; + tcpAddr.sin_family = AF_INET; + tcpAddr.sin_port = htons(port); + + if (loopbackBind || + !host || + !strcmp(host, "") || + !strcmp(host, "localhost")) { + tcpAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + else if(strcmp(host, "*") == 0) { + tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY); + } + else { + int ip = tcpAddr.sin_addr.s_addr = GetHostAddress(host); + if (ip == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Unknown " << label << " host '" << host + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unknown " << label << " host '" << host + << "'.\n"; + + HandleCleanup(); + return -1; + } + } + + return ListenConnectionAny((sockaddr *)&tcpAddr, sizeof(tcpAddr), label); +} + +int ListenConnection(ChannelEndPoint &endpoint, const char *label) +{ + char *unixPath, *host; + long port; + if (endpoint.getUnixPath(&unixPath)) { + return ListenConnectionUnix(unixPath, label); + } + else if (endpoint.getTCPHostAndPort(&host, &port)) { + return ListenConnectionTCP(host, port, label); + } + return -1; +} + +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 HandleCleanupForReconnect() +{ + #ifdef TEST + *logofs << "Loop: Going to clean up system resources for Reconnect " + << "in process '" << getpid() << "'.\n" + << logofs_flush; + #endif + handleTerminatedInLoop(); + DisableSignals(); + if (control) + CleanupChildren(); + CleanupListeners(); + CleanupSockets(); + CleanupKeeper(); + CleanupStreams(); + CleanupLocal(); + CleanupGlobal(); + RestoreSignals(); + ServerCache::lastInitReply.set(0,NULL); + ServerCache::lastKeymap.set(0,NULL); + ServerCache::getKeyboardMappingLastMap.set(0,NULL); +} +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'; + + *acceptHost = '\0'; + *displayHost = '\0'; + *authCookie = '\0'; + + proxyPort = DEFAULT_NX_PROXY_PORT; + xPort = DEFAULT_NX_X_PORT; + + xServerAddrFamily = -1; + xServerAddrLength = 0; + + delete xServerAddr; + + xServerAddr = NULL; + + listenSocket.disable(); + connectSocket.disable(); + + cupsPort.disable(); + auxPort.disable(); + smbPort.disable(); + mediaPort.disable(); + httpPort.disable(); + slavePort.disable(); + + *fontPort = '\0'; + + *bindHost = '\0'; + bindPort = -1; + + initTs = nullTimestamp(); + startTs = nullTimestamp(); + logsTs = nullTimestamp(); + nowTs = nullTimestamp(); + + 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; + + memset(&newAction, 0, sizeof(newAction)); + + newAction.sa_handler = HandleSignal; + + 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 && signalHandler != NULL) + { + #if defined(UNSAFE) && (defined(TEST) || defined(INFO)) + *logofs << "Loop: Calling slave handler in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + if ((*signalHandler)(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; + } + + proxy->checkSlaves(); + + // + // 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; + + memset(&action, 0, sizeof(action)); + + action.sa_handler = HandleTimer; + + 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 or UNIX file 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(ChannelEndPoint &socketAddress) +{ + char hostLabel[DEFAULT_STRING_LENGTH] = { 0 }; + char *socketUri = NULL; + + int retryAccept = -1; + + int pFD = -1; + int newFD = -1; + + int acceptIPAddr = 0; + + if (socketAddress.isTCPSocket()) + { + + // + // Get IP address of host to be awaited. + // + + 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; + } + snprintf(hostLabel, sizeof(hostLabel), "'%s'", acceptHost); + } + else + { + strcpy(hostLabel, "any host"); + } + + long _bindPort; + if (socketAddress.getTCPHostAndPort(NULL, &_bindPort)) + { + socketAddress.setSpec(loopbackBind ? "localhost" : "*", _bindPort); + } + else + { + // This should never happen + cerr << "Error" << ": Unable to change bind host\n"; + } + } + else if (socketAddress.isUnixSocket()) + strcpy(hostLabel, "this host"); + else + strcpy(hostLabel, "unknown origin (something went wrong!!!)"); + + + pFD = ListenConnection(socketAddress, "NX"); + + socketAddress.getSpec(&socketUri); + #ifdef TEST + *logofs << "Loop: Waiting for connection from " + << hostLabel << " on socket '" << socketUri + << "'.\n" << logofs_flush; + #endif + cerr << "Info" << ": Waiting for connection from " + << hostLabel << " on socket '" << socketUri + << "'.\n"; + free(socketUri); + + // + // 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(pFD, &readSet); + + T_timestamp selectTs; + + selectTs.tv_sec = 20; + selectTs.tv_usec = 0; + + int result = select(pFD + 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(pFD, &readSet)) + { + + sockaddr_in newAddrINET; + + if (socketAddress.isUnixSocket()) + { + socklen_t addrLen = sizeof(sockaddr_un); + newFD = accept(pFD, NULL, &addrLen); + } + else if (socketAddress.isTCPSocket()) + { + socklen_t addrLen = sizeof(sockaddr_in); + newFD = accept(pFD, (sockaddr *) &newAddrINET, &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; + } + + if (socketAddress.isUnixSocket()) + { + + char * unixPath = NULL; + socketAddress.getUnixPath(&unixPath); + #ifdef TEST + *logofs << "Loop: Accepted connection from this host on Unix file socket '" + << unixPath << "'.\n" + << logofs_flush; + #endif + + cerr << "Info" << ": Accepted connection from this host on Unix file socket '" + << unixPath << "'.\n"; + free(unixPath); + + break; + } + else if (socketAddress.isTCPSocket()) + { + + char *connectedHost = inet_ntoa(newAddrINET.sin_addr); + + if (*acceptHost == '\0' || (int) newAddrINET.sin_addr.s_addr == acceptIPAddr) + { + + #ifdef TEST + + unsigned int connectedPort = ntohs(newAddrINET.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 '" << socketAddress.getTCPPort() << "'.\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 (socketAddress.isUnixSocket()) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Connection via Unix file socket from this host " + << "could not be established.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Connection via Unix file socket from this host " + << "could not be established.\n"; + } + else 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(pFD); + + return newFD; + +WaitForRemoteError: + + close(pFD); + + HandleCleanup(); +} + +int PrepareProxyConnectionTCP(char** hostName, long int* portNum, int* timeout, int* proxyFileDescriptor, int* reason) +{ + + if (!proxyFileDescriptor) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionTCP). " + << "'proxyFileDescriptor' must not be a NULL pointer.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Implementation error (PrepareProxyConnectionTCP). " + << "'proxyFileDescriptor' must not be a NULL pointer.\n"; + + return -1; + } + + if (!reason) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionTCP). " + << "'reason' must not be a NULL pointer.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Implementation error (PrepareProxyConnectionTCP). " + << "'reason' must not be a NULL pointer.\n"; + + return -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; + + *proxyFileDescriptor = -1; + *reason = -1; + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(*portNum); + addr.sin_addr.s_addr = remoteIPAddr; + + *proxyFileDescriptor = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); + *reason = EGET(); + + if (*proxyFileDescriptor == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed. " + << "Error is " << *reason << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed. " + << "Error is " << *reason << " '" << ESTR() + << "'.\n"; + return -1; + + } + else if (SetReuseAddress(*proxyFileDescriptor) < 0) + { + return -1; + } + + // + // Ensure operation is timed out + // if there is a network problem. + // + + if (timeout) + SetTimer(*timeout); + else + SetTimer(20000); + + int result = connect(*proxyFileDescriptor, (sockaddr *) &addr, sizeof(sockaddr_in)); + + *reason = EGET(); + + ResetTimer(); + + return result; + +} + +int PrepareProxyConnectionUnix(char** path, int* timeout, int* proxyFileDescriptor, int* reason) +{ + + if (!proxyFileDescriptor) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionUnix). " + << "proxyFileDescriptor must not be a NULL pointer.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Implementation error (PrepareProxyConnectionUnix). " + << "proxyFileDescriptor must not be a NULL pointer.\n"; + + return -1; + } + + if (!reason) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionUnix). " + << "'reason' must not be a NULL pointer.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Implementation error (PrepareProxyConnectionUnix). " + << "'reason' must not be a NULL pointer.\n"; + + return -1; + } + + /* FIXME: Add socket file existence and permission checks */ + + *proxyFileDescriptor = -1; + *reason = -1; + + sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, *path, 108 - 1); + + *proxyFileDescriptor = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); + *reason = EGET(); + + if (*proxyFileDescriptor == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed. " + << "Error is " << *reason << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed. " + << "Error is " << *reason << " '" << ESTR() + << "'.\n"; + + return -1; + } + + // + // Ensure operation is timed out + // if there is a network problem. + // + + if (timeout) + SetTimer(*timeout); + else + SetTimer(20000); + + int result = connect(*proxyFileDescriptor, (sockaddr *) &addr, sizeof(sockaddr_un)); + + *reason = EGET(); + + ResetTimer(); + + return result; +} + +// +// Connect to remote proxy. If successful +// return FD of connection, else return -1. +// + +int ConnectToRemote(ChannelEndPoint &socketAddress) +{ + + // + // How many times we retry to connect to remote + // host / Unix domain socket 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(); + + int result = -1; + int reason = -1; + int pFD = -1; + + char *hostName = NULL; + long int portNum = -1; + char *unixPath = NULL; + + for (;;) + { + + #ifdef DEBUG + *logofs << "Loop: Timer set to " << connectTimeout / 1000 + << " S " << "with retry set to " << retryConnect + << " in process with pid '" << getpid() + << "'.\n" << logofs_flush; + #endif + + if (socketAddress.getUnixPath(&unixPath)) + result = PrepareProxyConnectionUnix(&unixPath, &connectTimeout, &pFD, &reason); + else if (socketAddress.getTCPHostAndPort(&hostName, &portNum)) + result = PrepareProxyConnectionTCP(&hostName, &portNum, &connectTimeout, &pFD, &reason); + + if (result < 0) + { + close(pFD); + + if (CheckAbort() != 0) + { + goto ConnectToRemoteError; + } + else if (--retryConnect == 0) + { + ESET(reason); + + if (socketAddress.isUnixSocket()) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Connection to Unix file socket '" + << unixPath << "' failed. Error is " + << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Connection to Unix file socket '" + << unixPath << "' failed. Error is " + << EGET() << " '" << ESTR() << "'.\n"; + } + else + { + + #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 + if (unixPath && unixPath[0] != '\0' ) + { + *logofs << "Loop: Connection to Unix socket file '" + << unixPath << "' failed with error '" + << ESTR() << "'. Retrying.\n" + << logofs_flush; + } + else + { + *logofs << "Loop: Connection to '" << hostName + << ":" << portNum << "' failed with error '" + << ESTR() << "'. Retrying.\n" + << logofs_flush; + } + #endif + } + else + { + // + // Connection was successful. + // + + break; + } + } + + return pFD; + +ConnectToRemoteError: + + if (pFD != -1) + { + close(pFD); + } + + 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-%s-%i.%i.%i", + control -> NXPROXY_COMPATIBILITY_VERSION, + 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. + // + + // Since ProtoStep7 (#issue 108) + if (useStrict != -1) + { + sprintf(options + strlen(options), "strict=%d,", useStrict); + } + + // + // Tell the remote the size of the shared + // memory segment. + // + + // Since ProtoStep7 (#issue 108) + if (*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)); + } + + *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; + + 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; +} + +static int +hexval(char c) { + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + return -1; +} + +static void +URLDecodeInPlace(char *str) { + if (str) { + char *to = str; + while (str[0]) { + if ((str[0] == '%') && + (hexval(str[1]) >= 0) && + (hexval(str[2]) >= 0)) { + *(to++) = hexval(str[1]) * 16 + hexval(str[2]); + str += 3; + } + else + *(to++) = *(str++); + } + *to = '\0'; + } +} + +int WriteLocalData(int fd, const char *buffer, int size) +{ + int position = 0; + int ret = 0; + fd_set writeSet; + struct timeval selectTs = {30, 0}; + + while (position < size) + { + + // A write to a non-blocking socket may fail with EAGAIN. The problem is + // that cache data is done in several writes, and there's no easy way + // to handle failure without rewriting a significant amount of code. + // + // Bailing out of the outer loop would result in restarting the sending + // of the entire cache list, which would confuse the other side. + + FD_ZERO(&writeSet); + FD_SET(fd, &writeSet); + + ret = select(fd+1, NULL, &writeSet, NULL, &selectTs); + + #ifdef DEBUG + *logofs << "Loop: WriteLocalData: select() returned with a code of " << ret << " and remaining timeout of " + << selectTs.tv_sec << " sec, " << selectTs.tv_usec << "usec\n" << logofs_flush; + #endif + + if ( ret < 0 ) + { + *logofs << "Loop: Error in select() when writing data to FD#" << fd << ": " << strerror(EGET()) << "\n" << logofs_flush; + + if ( EGET() == EINTR ) + continue; + + return -1; + } + else if ( ret == 0 ) + { + *logofs << "Loop: Timeout expired in select() when writing data to FD#" << fd << ": " << strerror(EGET()) << "\n" << logofs_flush; + return -1; + } + + int result = write(fd, buffer + position, size - position); + + getNewTimestamp(); + + if (result <= 0) + { + if (result < 0 && (EGET() == EINTR || EGET() == EAGAIN || EGET() == EWOULDBLOCK)) + { + 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 = strrchr(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, "="); + + char connectHost[DEFAULT_STRING_LENGTH] = { 0 }; + long connectPort = -1; + + while (name) + { + value = strtok(NULL, ","); + URLDecodeInPlace(value); + + 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"; + if (ParseLinkOption("adsl") < 0) + 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) + { + char *socketUri = NULL; + if (connectSocket.getSpec(&socketUri)) + { + #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 '" + << socketUri << "'.\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 '" + << socketUri << "'.\n"; + + free(socketUri); + return -1; + } + + SetAndValidateChannelEndPointArg("local", name, value, listenSocket); + + } + else if (strcasecmp(name, "loopback") == 0) + { + loopbackBind = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "accept") == 0) + { + char *socketUri = NULL; + if (connectSocket.getSpec(&socketUri)) + { + #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 '" + << socketUri << "'.\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 '" + << socketUri << "'.\n"; + + free(socketUri); + 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; + } + if ((strncmp(value, "tcp:", 4) == 0) || (strncmp(value, "unix:", 5) == 0)) + SetAndValidateChannelEndPointArg("local", name, value, connectSocket); + else + // if the "connect" parameter does not start with "unix:" or "tcp:" assume + // old parameter usage style (providing hostname string only). + strcpy(connectHost, value); + } + 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) + { + SetAndValidateChannelEndPointArg("local", name, value, cupsPort); + } + 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"; + + SetAndValidateChannelEndPointArg("local", name, value, cupsPort); + } + else if (strcasecmp(name, "keybd") == 0 || + strcasecmp(name, "aux") == 0) + { + SetAndValidateChannelEndPointArg("local", name, value, auxPort); + } + else if (strcasecmp(name, "samba") == 0 || + strcasecmp(name, "smb") == 0) + { + SetAndValidateChannelEndPointArg("local", name, value, smbPort); + } + else if (strcasecmp(name, "media") == 0) + { + SetAndValidateChannelEndPointArg("local", name, value, mediaPort); + } + else if (strcasecmp(name, "http") == 0) + { + SetAndValidateChannelEndPointArg("local", name, value, httpPort); + } + else if (strcasecmp(name, "font") == 0) + { + strncpy(fontPort, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "slave") == 0) + { + SetAndValidateChannelEndPointArg("local", name, value, slavePort); + } + 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"; + if (ParsePackOption("nopack")<0) + 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 || + strcasecmp(name, "sleep") == 0 || + strcasecmp(name, "tolerancechecks") == 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 || + strcasecmp(name, "xinerama") == 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 || + strcasecmp(name, "state") == 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) ... + + // Assemble the connectSocket channel end point if parameter values have been old-school... + if (connectSocket.disabled() && (connectHost[0] != '\0') && (proxyPort > 0 || connectPort > 0)) + { + if (connectPort < 0) + connectPort = proxyPort + DEFAULT_NX_PROXY_PORT_OFFSET; + + char tcpHostAndPort[DEFAULT_STRING_LENGTH] = { 0 }; + sprintf(tcpHostAndPort, "tcp:%s:%ld", connectHost, connectPort); + SetAndValidateChannelEndPointArg("local", name, tcpHostAndPort, connectSocket); + } + + #ifdef TEST + *logofs << "Loop: Completed parsing of string '" + << env << "'.\n" << logofs_flush; + #endif + + if ((*fileOptions != '\0') && (strncmp(fileOptions, "/dev/", 5) != 0) && (strncmp(fileOptions, "/proc/", 6) != 0) && (strncmp(fileOptions, "/sys/", 5) != 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. + // + + char cHost[DEFAULT_STRING_LENGTH] = { '\0' }; + long cPort = 0; + + if (ParseHostOption(nextArg, cHost, cPort) > 0) + { + // + // Assume port is at a proxied display offset. + // + + proxyPort = cPort; + + cPort += DEFAULT_NX_PROXY_PORT_OFFSET; + connectSocket.setSpec(cHost, cPort); + + } + 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 hasType = 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; + } + } + + } + else if (strcasecmp(name, "render") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + useRender = ValidateArg("remote", name, value); + } + + } + else if (strcasecmp(name, "taint") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + useTaint = ValidateArg("remote", name, value); + } + + } + 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); + } + + } + 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; + } + + } + 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 << "Loop: 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 set RLIMIT_CORE. Error is '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + return -2; + } + } + + #ifdef TEST + *logofs << "Loop: RLIMIT_CORE is "<< 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 = strrchr(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. + // + + useCupsSocket = 0; + if (cupsPort.enabled()) { + if (control -> ProxyMode == proxy_client) { + cupsPort.setDefaultTCPPort(DEFAULT_NX_CUPS_PORT_OFFSET + proxyPort); + useCupsSocket = 1; + } + else + cupsPort.setDefaultTCPPort(631); + } + +#ifdef TEST + *logofs << "Loop: cups port: " << cupsPort << "\n" + << logofs_flush; +#endif + + useAuxSocket = 0; + if (auxPort.enabled()) { + if (control -> ProxyMode == proxy_client) { + auxPort.setDefaultTCPPort(DEFAULT_NX_AUX_PORT_OFFSET + proxyPort); + useAuxSocket = 1; + } + else { + auxPort.setDefaultTCPPort(1); + + if (auxPort.getTCPPort() != 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.setSpec("1"); + } + } + } + +#ifdef TEST + *logofs << "Loop: aux port: " << auxPort << "\n" + << logofs_flush; +#endif + + useSmbSocket = 0; + if (smbPort.enabled()) { + if (control -> ProxyMode == proxy_client) { + auxPort.setDefaultTCPPort(DEFAULT_NX_SMB_PORT_OFFSET + proxyPort); + useAuxSocket = 1; + } + else + auxPort.setDefaultTCPPort(139); + } + + +#ifdef TEST + *logofs << "Loop: smb port: " << smbPort << "\n" + << logofs_flush; +#endif + + useMediaSocket = 0; + if (mediaPort.enabled()) { + if (control -> ProxyMode == proxy_client) { + mediaPort.setDefaultTCPPort(DEFAULT_NX_MEDIA_PORT_OFFSET + proxyPort); + useMediaSocket = 1; + } + else if (mediaPort.getTCPPort() == 1) { +#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(); + } + } + +#ifdef TEST + *logofs << "Loop: Using multimedia port '" << mediaPort + << "'.\n" << logofs_flush; +#endif + + useHttpSocket = 0; + if (httpPort.enabled()) { + if (control -> ProxyMode == proxy_client) { + httpPort.setDefaultTCPPort(DEFAULT_NX_HTTP_PORT_OFFSET + proxyPort); + useHttpSocket = 1; + } + else + httpPort.setDefaultTCPPort(80); + } + +#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 + } + + useSlaveSocket = 0; + if (slavePort.enabled()) { + useSlaveSocket = 1; + if (control -> ProxyMode == proxy_client) + slavePort.setDefaultTCPPort(DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET + proxyPort); + else + slavePort.setDefaultTCPPort(DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET + proxyPort); + } + +#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. + // + + // + // Since ProtoStep8 (#issue 108) and also + // with older "unix-" sessions + // + + if (*sessionType != '\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; + } + + // For android, no shared memory available + control -> ShmemServer = 0; + control -> ShmemClientSize = 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 versions from 3.5.0. The protocol + // step 10 is the minimum supported version. + // + + int step = 0; + + if (major == 3) + { + if (minor >= 5) + { + step = 10; + } + } + else if (major > 3) + { + step = 10; + } + + if (step == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Unable to set the protocol step value from " + << "the negotiated protocol version " << major << "." << minor + << "." << patch << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unable to set the protocol step value from " + << "the negotiated protocol version " << major << "." << minor + << "." << patch << ".\n"; + + #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 https://github.com/ArcticaProject/nx-libs/releases 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) + { + // + // Since ProtoStep8 (#issue 108) + // + // Now it's assumed that the remote is + // able to handle the selected pack + // method + // + + #ifdef TEST + *logofs << __FILE__ << " : " << __LINE__ << " - " + << "step = " << control -> getProtoStep() + << " packMethod = " << packMethod + << " packQuality = " << packQuality + << ".\n" << logofs_flush; + #endif + + // + // Update the pack method name. + // + + ParsePackMethod(packMethod, packQuality); + } + + // + // At the moment the image cache is not used by the + // agent. Proxy versions older than 3.0.0 assumed + // that it was enabled and sent specific bits as part + // of the encoding. Conversely, it is advisable to + // disable the cache right now. By not enabling the + // the image cache, the house-keeping process will + // only take care of cleaning up the "cache-" direc- + // tories. + // + + // + // Considering that compatibility with older versions + // has been set to cover as far as 3.5.0, the cache can + // be disabled at this point without any concern + // + + // Since ProtoStep8 (#issue 108) + #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 = 1536; + 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 = 1536; + 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, long &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 = strrchr(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 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 << endl; + + PrintVersionInfo(); + + cerr << endl; + + cerr << GetCopyrightInfo() + << endl + << GetOtherCopyrightInfo() + << endl + << "See https://github.com/ArcticaProject/nx-libs for more information." << endl << endl; + } + + // + // 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 ? "client" : "server") + << " 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.enabled()) + { + cerr << "Info" << ": Listening to CUPS connections " + << "on port '" << cupsPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server && + cupsPort.enabled()) + { + cerr << "Info" << ": Forwarding CUPS connections " + << "to port '" << cupsPort << "'.\n"; + } + + if (control -> ProxyMode == proxy_client && + useAuxSocket > 0 && auxPort.enabled()) + { + cerr << "Info" << ": Listening to auxiliary X11 connections " + << "on port '" << auxPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server && + auxPort.enabled()) + { + cerr << "Info" << ": Forwarding auxiliary X11 connections " + << "to display '" << displayHost << "'.\n"; + } + + if (control -> ProxyMode == proxy_client && + useSmbSocket > 0 && smbPort.enabled()) + { + cerr << "Info" << ": Listening to SMB connections " + << "on port '" << smbPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server && + smbPort.enabled()) + { + cerr << "Info" << ": Forwarding SMB connections " + << "to port '" << smbPort << "'.\n"; + } + + if (control -> ProxyMode == proxy_client && + useMediaSocket > 0 && mediaPort.enabled()) + { + cerr << "Info" << ": Listening to multimedia connections " + << "on port '" << mediaPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server && + mediaPort.enabled()) + { + cerr << "Info" << ": Forwarding multimedia connections " + << "to port '" << mediaPort << "'.\n"; + } + + if (control -> ProxyMode == proxy_client && + useHttpSocket > 0 && httpPort.enabled()) + { + cerr << "Info" << ": Listening to HTTP connections " + << "on port '" << httpPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server && + httpPort.enabled()) + { + 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.enabled()) + { + cerr << "Info" << ": Listening to slave connections " + << "on port '" << slavePort << "'.\n"; + } +} + +void PrintVersionInfo() +{ + cerr << "NXPROXY - " << "Version " + << control -> LocalVersionMajor << "." + << control -> LocalVersionMinor << "." + << control -> LocalVersionPatch << "." + << control -> LocalVersionMaintenancePatch; + + 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; +} + +void SetAndValidateChannelEndPointArg(const char *type, const char *name, const char *value, + ChannelEndPoint &endPoint) { + endPoint.setSpec(value); + if (!endPoint.validateSpec()) { + #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(); + } +} + + +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; + } + + // + // Since ProtoStep7 (#issue 108) + // + // Now the remote proxy should always + // be able to handle the alert + // + + 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 &lTs, T_timestamp &nTs) +{ + // + // If need to limit the size of the + // log file, check the size at each + // loop. + // + + #ifndef QUOTA + + if (diffTimestamp(lTs, nTs) > 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. + // + + lTs = nTs; + } +} + +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 + } +} diff --git a/nxcomp/src/MD5.c b/nxcomp/src/MD5.c new file mode 100644 index 000000000..7255ca5c0 --- /dev/null +++ b/nxcomp/src/MD5.c @@ -0,0 +1,403 @@ +/* + 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 + <ghost@aladdin.com>. 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 <string.h> + 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 <stdio.h> 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "MD5.h" + +#include <string.h> + +/* + * Try to determine the CPU endianess + * at compile time. + */ + +#if defined(__linux) || defined(__CYGWIN32__) + +#include <endian.h> + +#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)); +} diff --git a/nxcomp/src/Makefile.am b/nxcomp/src/Makefile.am new file mode 100644 index 000000000..2264cb347 --- /dev/null +++ b/nxcomp/src/Makefile.am @@ -0,0 +1,146 @@ +NULL = + +lib_LTLIBRARIES = libXcomp.la + +libXcomp_la_SOURCES = \ + ActionCache.cpp \ + Agent.cpp \ + Alpha.cpp \ + Auth.cpp \ + Bitmap.cpp \ + BlockCache.cpp \ + BlockCacheSet.cpp \ + ChangeGC.cpp \ + ChangeProperty.cpp \ + ChannelCache.cpp \ + Channel.cpp \ + ChannelEndPoint.cpp \ + CharCache.cpp \ + Children.cpp \ + ClearArea.cpp \ + ClientCache.cpp \ + ClientChannel.cpp \ + ClientProxy.cpp \ + ClientReadBuffer.cpp \ + ClientStore.cpp \ + Colormap.cpp \ + ConfigureWindow.cpp \ + Control.cpp \ + CopyArea.cpp \ + CreateGC.cpp \ + CreatePixmap.cpp \ + DecodeBuffer.cpp \ + EncodeBuffer.cpp \ + FillPoly.cpp \ + Fork.cpp \ + GenericChannel.cpp \ + GenericReadBuffer.cpp \ + GenericReply.cpp \ + GenericRequest.cpp \ + GetImage.cpp \ + GetImageReply.cpp \ + GetProperty.cpp \ + GetPropertyReply.cpp \ + ImageText16.cpp \ + ImageText8.cpp \ + IntCache.cpp \ + InternAtom.cpp \ + Jpeg.cpp \ + Keeper.cpp \ + List.cpp \ + ListFontsReply.cpp \ + Loop.cpp \ + Message.cpp \ + MD5.c \ + Misc.cpp \ + OpcodeStore.cpp \ + Pack.c \ + Pgn.cpp \ + Pipe.cpp \ + PolyArc.cpp \ + PolyFillArc.cpp \ + PolyFillRectangle.cpp \ + PolyLine.cpp \ + PolyPoint.cpp \ + PolySegment.cpp \ + PolyText16.cpp \ + PolyText8.cpp \ + Proxy.cpp \ + ProxyReadBuffer.cpp \ + PutImage.cpp \ + PutPackedImage.cpp \ + QueryFontReply.cpp \ + ReadBuffer.cpp \ + RenderAddGlyphs.cpp \ + RenderChangePicture.cpp \ + RenderComposite.cpp \ + RenderCompositeGlyphs.cpp \ + RenderCreateGlyphSet.cpp \ + RenderCreatePicture.cpp \ + RenderExtension.cpp \ + RenderFillRectangles.cpp \ + RenderFreeGlyphSet.cpp \ + RenderFreePicture.cpp \ + RenderGenericRequest.cpp \ + RenderPictureClip.cpp \ + RenderPictureFilter.cpp \ + RenderPictureTransform.cpp \ + RenderTrapezoids.cpp \ + RenderTriangles.cpp \ + Rgb.cpp \ + Rle.cpp \ + SendEvent.cpp \ + SequenceQueue.cpp \ + ServerCache.cpp \ + ServerChannel.cpp \ + ServerProxy.cpp \ + ServerReadBuffer.cpp \ + ServerStore.cpp \ + SetClipRectangles.cpp \ + SetUnpackAlpha.cpp \ + SetUnpackColormap.cpp \ + SetUnpackGeometry.cpp \ + ShapeExtension.cpp \ + Socket.cpp \ + Split.cpp \ + StaticCompressor.cpp \ + Statistics.cpp \ + Timestamp.cpp \ + TranslateCoords.cpp \ + Transport.cpp \ + Unpack.cpp \ + Vars.c \ + Version.c \ + WriteBuffer.cpp \ + XidCache.cpp \ + Z.cpp \ + $(NULL) + +libXcomp_la_LIBADD = \ + @JPEG_LIBS@ \ + @PNG_LIBS@ \ + @Z_LIBS@ \ + $(NULL) + +AM_CXXFLAGS = \ + $(BASE_CXXFLAGS) \ + $(JPEG_CFLAGS) \ + $(PNG_CFLAGS) \ + $(Z_CFLAGS) \ + $(NULL) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + $(NULL) + +libXcomp_la_LDFLAGS = -version-number @LT_COMP_VERSION@ -no-undefined + +libXcompincludedir = $(includedir)/nx +libXcompinclude_HEADERS = \ + $(top_srcdir)/include/MD5.h \ + $(top_srcdir)/include/NX.h \ + $(top_srcdir)/include/NXalert.h \ + $(top_srcdir)/include/NXpack.h \ + $(top_srcdir)/include/NXproto.h \ + $(top_srcdir)/include/NXvars.h \ + $(NULL) diff --git a/nxcomp/src/Message.cpp b/nxcomp/src/Message.cpp new file mode 100644 index 000000000..b75d90c24 --- /dev/null +++ b/nxcomp/src/Message.cpp @@ -0,0 +1,2343 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <cstdio> +#include <unistd.h> +#include <cstring> + +#include <algorithm> + +#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; + + // + // 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<T_checksums::iterator, bool> 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 +} diff --git a/nxcomp/src/Message.h b/nxcomp/src/Message.h new file mode 100644 index 000000000..30883f101 --- /dev/null +++ b/nxcomp/src/Message.h @@ -0,0 +1,1089 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Message_H +#define Message_H + +#include <X11/Xproto.h> + +#include "NXproto.h" + +#include "Misc.h" +#include "Control.h" + +#include "Types.h" +#include "Timestamp.h" + +#include "ActionCache.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; + + // + // Position in cache where next insertion + // is going to take place. + // + + short int lastRated; + + // + // 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 */ + diff --git a/nxcomp/src/Misc.cpp b/nxcomp/src/Misc.cpp new file mode 100644 index 000000000..b40e6409e --- /dev/null +++ b/nxcomp/src/Misc.cpp @@ -0,0 +1,1934 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <cstdio> +#include <cctype> +#include <cstdlib> +#include <unistd.h> +#include <csignal> + +#include <errno.h> +#include <string.h> + +#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 + +// +// By default nxproxy binds to all network interfaces, setting +// DEFAULT_LOOPBACK_BIND to 1 enables binding to the loopback +// device only. +// + +const int DEFAULT_LOOPBACK_BIND = 0; + +// +// 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\ + loopback=b Bind to the loopback device only.\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=s\n\ +streaming=n\n\ +backingstore=n\n\ +composite=n\n\ +xinerama=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\n\ +sleep=n\n\ +tolerancechecks=s\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\ + NX_SLAVE_CMD The full path to the slave channel handler. When the\n\ + slave channel is enabled, the agent will listen on a\n\ + port and forward the connection to the NX_SLAVE_CMD\n\ + program. This can be used to implement agent/proxy\n\ + communication for applications such as serial port and\n\ + USB forwarding.\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\ +Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de>\n\ +Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de>\n\ +Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de>\n\ +Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>\n\ +Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com)\n\ +\n\ +NXCOMP, NX protocol compression and NX extensions to this software\n\ +are copyright of the aforementioned persons and companies.\n\ +\n\ +Redistribution and use of the present software is allowed according\n\ +to terms specified in the file LICENSE.nxcomp which comes in the\n\ +source distribution.\n\ +\n\ +All rights reserved.\n\ +\n\ +NOTE: This software has received contributions from various other\n\ +contributors, only the core maintainers and supporters are listed as\n\ +copyright holders. Please contact us, if you feel you should be listed\n\ +as copyright holder, as well.\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; +} diff --git a/nxcomp/src/Misc.h b/nxcomp/src/Misc.h new file mode 100644 index 000000000..997630137 --- /dev/null +++ b/nxcomp/src/Misc.h @@ -0,0 +1,279 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Misc_H +#define Misc_H + +#include <iostream> +#include <fstream> + +#include <cerrno> +#include <cstring> + +#ifdef __sun + +#include <strings.h> + +#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; + +// +// NX proxy binds to all network interfaces by default +// With the -loopback parameter, you can switch +// over to binding to the loopback device only. +// + +extern const int DEFAULT_LOOPBACK_BIND; + +// +// 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)); + void HandleCleanupForReconnect(); +} + +// +// 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 */ diff --git a/nxcomp/src/NXmitshm.h b/nxcomp/src/NXmitshm.h new file mode 100644 index 000000000..939d488fb --- /dev/null +++ b/nxcomp/src/NXmitshm.h @@ -0,0 +1,56 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef NXmitshm_H +#define NXmitshm_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Import opcodes from <X11/extensions/XShm.h> + * 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 */ diff --git a/nxcomp/src/NXrender.h b/nxcomp/src/NXrender.h new file mode 100644 index 000000000..280715833 --- /dev/null +++ b/nxcomp/src/NXrender.h @@ -0,0 +1,78 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef NXrender_H +#define NXrender_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Import this from <X11/extensions/render.h> + * 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 */ diff --git a/nxcomp/src/OpcodeCache.h b/nxcomp/src/OpcodeCache.h new file mode 100644 index 000000000..e07a1b997 --- /dev/null +++ b/nxcomp/src/OpcodeCache.h @@ -0,0 +1,53 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/OpcodeStore.cpp b/nxcomp/src/OpcodeStore.cpp new file mode 100644 index 000000000..21b919c8a --- /dev/null +++ b/nxcomp/src/OpcodeStore.cpp @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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() +{ +} diff --git a/nxcomp/src/OpcodeStore.h b/nxcomp/src/OpcodeStore.h new file mode 100644 index 000000000..d041ed4b8 --- /dev/null +++ b/nxcomp/src/OpcodeStore.h @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Pack.c b/nxcomp/src/Pack.c new file mode 100644 index 000000000..97fb93b5f --- /dev/null +++ b/nxcomp/src/Pack.c @@ -0,0 +1,180 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> + +#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 diff --git a/nxcomp/src/Pgn.cpp b/nxcomp/src/Pgn.cpp new file mode 100644 index 000000000..649227f52 --- /dev/null +++ b/nxcomp/src/Pgn.cpp @@ -0,0 +1,809 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// This file obviously supports PNG +// decompression. It was renamed to +// avoid name clashes on Windows. +// + +#include <X11/Xmd.h> + +#ifdef ANDROID +#include <strings.h> +#endif +#include <unistd.h> +#include <stdio.h> +#include <png.h> + +#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 (png_get_color_type(pngPtr, infoPtr) == 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 (png_get_color_type(pngPtr, infoPtr) == 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 (png_get_color_type(pngPtr, infoPtr) == 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; +} diff --git a/nxcomp/src/Pgn.h b/nxcomp/src/Pgn.h new file mode 100644 index 000000000..e5ea36715 --- /dev/null +++ b/nxcomp/src/Pgn.h @@ -0,0 +1,42 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Pipe.cpp b/nxcomp/src/Pipe.cpp new file mode 100644 index 000000000..4fa149412 --- /dev/null +++ b/nxcomp/src/Pipe.cpp @@ -0,0 +1,433 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <pwd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <grp.h> + +#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. + // + + struct passwd *pwent = getpwuid(getuid()); + if (pwent) initgroups(pwent->pw_name,getgid()); + if (setgid(getgid()) == -1) + { + _exit(127); + } + if (setuid(getuid()) == -1) + { + _exit(127); + } + + 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); +} diff --git a/nxcomp/src/Pipe.h b/nxcomp/src/Pipe.h new file mode 100644 index 000000000..fd1061d30 --- /dev/null +++ b/nxcomp/src/Pipe.h @@ -0,0 +1,35 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +// +// 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); diff --git a/nxcomp/src/PolyArc.cpp b/nxcomp/src/PolyArc.cpp new file mode 100644 index 000000000..42dbb265d --- /dev/null +++ b/nxcomp/src/PolyArc.cpp @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} + + diff --git a/nxcomp/src/PolyArc.h b/nxcomp/src/PolyArc.h new file mode 100644 index 000000000..d744d6a10 --- /dev/null +++ b/nxcomp/src/PolyArc.h @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/PolyFillArc.cpp b/nxcomp/src/PolyFillArc.cpp new file mode 100644 index 000000000..35125f0d3 --- /dev/null +++ b/nxcomp/src/PolyFillArc.cpp @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} + + diff --git a/nxcomp/src/PolyFillArc.h b/nxcomp/src/PolyFillArc.h new file mode 100644 index 000000000..a4eff5b48 --- /dev/null +++ b/nxcomp/src/PolyFillArc.h @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/PolyFillRectangle.cpp b/nxcomp/src/PolyFillRectangle.cpp new file mode 100644 index 000000000..95694e582 --- /dev/null +++ b/nxcomp/src/PolyFillRectangle.cpp @@ -0,0 +1,160 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} diff --git a/nxcomp/src/PolyFillRectangle.h b/nxcomp/src/PolyFillRectangle.h new file mode 100644 index 000000000..7ebb9270d --- /dev/null +++ b/nxcomp/src/PolyFillRectangle.h @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/PolyLine.cpp b/nxcomp/src/PolyLine.cpp new file mode 100644 index 000000000..1b12bbe98 --- /dev/null +++ b/nxcomp/src/PolyLine.cpp @@ -0,0 +1,168 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +{ + // Since ProtoStep8 (#issue 108) + 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; + + #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; + + 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 +} + + diff --git a/nxcomp/src/PolyLine.h b/nxcomp/src/PolyLine.h new file mode 100644 index 000000000..66fa5df1a --- /dev/null +++ b/nxcomp/src/PolyLine.h @@ -0,0 +1,186 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/PolyPoint.cpp b/nxcomp/src/PolyPoint.cpp new file mode 100644 index 000000000..70151cc0d --- /dev/null +++ b/nxcomp/src/PolyPoint.cpp @@ -0,0 +1,168 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +{ + // Since ProtoStep8 (#issue 108) + 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; + + #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; + + 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 +} + + diff --git a/nxcomp/src/PolyPoint.h b/nxcomp/src/PolyPoint.h new file mode 100644 index 000000000..b8ea183bf --- /dev/null +++ b/nxcomp/src/PolyPoint.h @@ -0,0 +1,186 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/PolySegment.cpp b/nxcomp/src/PolySegment.cpp new file mode 100644 index 000000000..aa2d4efe3 --- /dev/null +++ b/nxcomp/src/PolySegment.cpp @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} + + diff --git a/nxcomp/src/PolySegment.h b/nxcomp/src/PolySegment.h new file mode 100644 index 000000000..53fd42c60 --- /dev/null +++ b/nxcomp/src/PolySegment.h @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/PolyText16.cpp b/nxcomp/src/PolyText16.cpp new file mode 100644 index 000000000..32be45482 --- /dev/null +++ b/nxcomp/src/PolyText16.cpp @@ -0,0 +1,312 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} + + diff --git a/nxcomp/src/PolyText16.h b/nxcomp/src/PolyText16.h new file mode 100644 index 000000000..805e1fa04 --- /dev/null +++ b/nxcomp/src/PolyText16.h @@ -0,0 +1,188 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/PolyText8.cpp b/nxcomp/src/PolyText8.cpp new file mode 100644 index 000000000..61fef5a4f --- /dev/null +++ b/nxcomp/src/PolyText8.cpp @@ -0,0 +1,310 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} diff --git a/nxcomp/src/PolyText8.h b/nxcomp/src/PolyText8.h new file mode 100644 index 000000000..3d5ff533a --- /dev/null +++ b/nxcomp/src/PolyText8.h @@ -0,0 +1,188 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Proxy.cpp b/nxcomp/src/Proxy.cpp new file mode 100644 index 000000000..5529de021 --- /dev/null +++ b/nxcomp/src/Proxy.cpp @@ -0,0 +1,6529 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <cstdio> +#include <unistd.h> +#include <cstdlib> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> + +#ifdef ANDROID +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#endif + +#include "Misc.h" + +#if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun) +#include <netinet/in_systm.h> +#endif + +#ifndef ANDROID +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#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" +#include "ChannelEndPoint.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(); + +extern int HandleChild(int); + +// +// 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; + slavePidMap_[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_); + + 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; + } + } + + // + // Kill all active slave channel children, and + // give them 5 seconds to exit nicely. + + #ifdef DEBUG + *logofs << "Proxy: Killing active slaves" << endl; + #endif + + int slave_count = 999; + int loop_count = 0; + + while(slave_count > 0 && loop_count < 50) + { + slave_count = 0; + + for (int channelId = 0; channelId<CONNECTIONS_LIMIT; channelId++) + { + int pid = slavePidMap_[channelId]; + + if (pid > 1) { + slave_count++; + + #ifdef DEBUG + *logofs << "Proxy: Active slave with pid " << pid << logofs_flush; + #endif + + if ( loop_count == 0 ) + { + #ifdef DEBUG + *logofs << "Proxy: Sending SIGTERM to " << pid << logofs_flush; + #endif + kill(pid, SIGTERM); + } + else if ( loop_count == 25 ) + { + #ifdef DEBUG + *logofs << "Proxy: Sending SIGKILL to " << pid << logofs_flush; + #endif + kill(pid, SIGKILL); + } + + if (HandleChild(pid)) + { + #ifdef DEBUG + *logofs << "Proxy: Slave " << pid << " terminated" << logofs_flush; + #endif + slavePidMap_[channelId] = nothing; + } + } + } + + if ( slave_count > 0 ) + { + cerr << "Proxy: Error: Failed to kill all slave channel processes. " << slave_count << " processes still remaining." << endl; + } + + usleep(200000); + loop_count++; + } + + 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() +{ + // Since ProtoStep7 (#issue 108) + 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; + } + + 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; + + // + // Set to false the indicator for cumulative store + // size too small + // + bool isTooSmall = false; + + if (control -> PersistentCacheEnableSave) + { + #ifdef TEST + *logofs << "Proxy: Going to save content of client store.\n" + << logofs_flush; + #endif + + cacheToAdopt = handleSaveAllStores(control -> PersistentCachePath, isTooSmall); + } + #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; + } + else + { + #ifdef TEST + *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. + // + + if (control -> PersistentCacheEnableSave && !isTooSmall) + { + return -1; + } + else + { + 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; + + // Since ProtoStep7 (#issue 108) + count = token.bytes / token.size; + + // + // Force a count of 1, for example + // if this is a ping. + // + + if (count < 1) + { + count = 1; + + token.bytes = 0; + } + else + { + // Since ProtoStep7 (#issue 108) + if (count > 255) + { + count = 255; + } + + // + // 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) + { + // + // Since ProtoStep7 (#issue 108) + // + + // 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 + { + 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 + + // + // Since ProtoStep7 (#issue 108) with no limitations + // concerning invalid token requests at this point + // + + // + // 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 + + // + // Since ProtoStep7 (#issue 108) with no limitations + // concerning invalid token requests at this point + // + + // + // Increment the available tokens. + // + + 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 +{ + // Since ProtoStep8 (#issue 108) + major = 3; + minor = 0; + 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. + // + + // Since ProtoStep8 (#issue 108) + if (major < 3) + { + return -1; + } + + return 1; +} + +char *Proxy::handleSaveAllStores(const char *savePath, bool & isTooSmall) const +{ + isTooSmall = false; + + 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 + + // + // Cumulative store size is smaller than threshold + // so the indicator is set to true + // + + isTooSmall = true; + + 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 md5String[MD5_LENGTH * 2 + 2]; + + char fullName[strlen(savePath) + MD5_LENGTH * 2 + 4]; + + // + // Prepare the template for the temporary file + // + + const char* const uniqueTemplate = "XXXXXX"; + char tempName[strlen(savePath) + strlen("/") + 4 + strlen(uniqueTemplate) + 1]; + + snprintf(tempName, sizeof tempName, "%s/%s%s", + savePath, + control -> ProxyMode == proxy_client ? + "Z-C-" : + "Z-S-", + uniqueTemplate); + + #ifdef TEST + *logofs << "Proxy: Generating temporary file with template '" + << tempName << "'.\n" << logofs_flush; + #endif + + // + // Change the mask to make the file only + // readable by the user, then restore the + // old mask. + // + + mode_t fileMode = umask(0077); + + // + // Generate a unique temporary filename from tempName + // and then create and open the file + // + + int fdTemp = mkstemp(tempName); + if (fdTemp == -1) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Can't create temporary file in '" + << savePath << "'. Cause = " << strerror(errno) << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't create temporary file in '" + << savePath << "'. Cause = " << strerror(errno) << ".\n"; + + umask(fileMode); + + EnableSignals(); + + return NULL; + } + + #ifdef TEST + *logofs << "Proxy: Saving cache to file '" + << tempName << "'.\n" << logofs_flush; + #endif + + // + // Create and open the output stream for the new temporary + // file + // + + cachefs = new (std::nothrow) ofstream(tempName, ios::out | ios::binary); + if ((cachefs == NULL) || cachefs->fail()) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Can't create stream for temporary file '" + << tempName << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't create stream for temporary file '" + << tempName << "'.\n"; + + close(fdTemp); + unlink(tempName); + + umask(fileMode); + + EnableSignals(); + + return NULL; + } + + // + // Close the file descriptor returned by mkstemp + // and restore the old mask + // + + close(fdTemp); + umask(fileMode); + + 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; + + 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; + + 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'; + + 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 == -1) + { + handleFailOnSave(tempName, "C"); + + delete cachefs; + + delete md5StateStream; + delete [] md5DigestStream; + + delete md5StateClient; + delete [] md5DigestClient; + + 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; + + 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; + + // + // 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 (unsigned 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 j = 0; j < MD5_LENGTH; j++) + { + sprintf(md5String + (j * 2), "%02X", md5FromFile[j]); + } + + *logofs << "Proxy: PANIC! Saved checksum is '" + << md5String << "'.\n" << logofs_flush; + + for (unsigned int j = 0; j < MD5_LENGTH; j++) + { + sprintf(md5String + (j * 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 number of available " + << "channels exceeded.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Maximum number 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) +{ + // Since ProtoStep7 (#issue 108) + return handleNewGenericConnection(clientFd, channel_slave, "slave"); +} + + + +int Proxy::handleNewGenericConnectionFromProxy(int channelId, T_channel_type type, + ChannelEndPoint &endPoint, const char *label) +{ + char *unixPath, *host; + long port; + + if (endPoint.getUnixPath(&unixPath)) { + return handleNewGenericConnectionFromProxyUnix(channelId, type, unixPath, label); + } + + if (endPoint.getTCPHostAndPort(&host, &port)) { + return handleNewGenericConnectionFromProxyTCP(channelId, type, host, port, label); + } + + #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; +} + +int Proxy::handleNewGenericConnectionFromProxyTCP(int channelId, T_channel_type type, + const char *hostname, long 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::handleNewGenericConnectionFromProxyUnix(int channelId, T_channel_type type, + 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) +{ + + cerr << "Info" << ": New slave connection on " + << "channel ID#" << channelId << "\n"; + + char *nx_slave_cmd = getenv("NX_SLAVE_CMD"); + if (nx_slave_cmd == NULL) { + return -1; + } + + int spair[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, spair) == -1) { + perror("socketpair"); + return -1; + } + + int serverFd = spair[0]; + int clientFd = spair[1]; + + if (handlePostConnectionFromProxy(channelId, serverFd, channel_slave, "slave") < 0) + { + close(serverFd); + close(clientFd); + return -1; + } + + + int pid = fork(); + if (pid == 0) + { + + if (dup2(clientFd, 0) == -1) + { + perror("dup2"); + exit(1); + } + + if (dup2(clientFd, 1) == -1) + { + perror("dup2"); + exit(1); + } + + close(serverFd); + close(clientFd); + + /* Close FDs used by NX, QVD #1208 */ + for (int fd = 3; fd < 256; fd++) { + close(fd); + } + + char *const argv[2] = {nx_slave_cmd, NULL}; + + if (execv(nx_slave_cmd, argv) == -1) + { + perror("execv"); + } + exit(1); + + } + else if (pid == -1) + { + // TODO Test this! + perror("fork"); + close(serverFd); + close(clientFd); + return -1; + } + + close(clientFd); + slavePidMap_[channelId] = pid; + + cerr << "Info" << ": slave channel ID#" << channelId << " handler has PID " << pid << endl; + + return 1; +} + +void Proxy::checkSlaves() +{ + for (int channelId = 0; channelId<CONNECTIONS_LIMIT; channelId++) + { + int pid = slavePidMap_[channelId]; + + if (pid > 1 && HandleChild(pid)) + { + slavePidMap_[channelId] = nothing; + cerr << "Info:" << " Handled death of slave with pid " << pid << endl; + } + } +} + +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; +} diff --git a/nxcomp/src/Proxy.h b/nxcomp/src/Proxy.h new file mode 100644 index 000000000..ea60c827a --- /dev/null +++ b/nxcomp/src/Proxy.h @@ -0,0 +1,1276 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Proxy_H +#define Proxy_H + +#include <sys/types.h> + +#ifdef _AIX +#include <sys/select.h> +#endif + +#include "Misc.h" +#include "Timestamp.h" + +#include "List.h" +#include "Channel.h" +#include "Transport.h" +#include "EncodeBuffer.h" +#include "ProxyReadBuffer.h" +#include "ChannelEndPoint.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(ChannelEndPoint &cupsServerPort, + ChannelEndPoint &smbServerPort, + ChannelEndPoint &mediaServerPort, + ChannelEndPoint &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, + ChannelEndPoint &endpoint, const char *label); + + int handleNewGenericConnectionFromProxyUnix(int channelId, T_channel_type type, + const char *path, const char *label); + + int handleNewGenericConnectionFromProxyTCP(int channelId, T_channel_type type, + const char *hostname, long port, const char *label); + + int handleNewSlaveConnection(int clientFd); + + int handleNewSlaveConnectionFromProxy(int channelId); + + void checkSlaves(); + + // + // 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 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, bool & isTooSmall) 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]; + int slavePidMap_[CONNECTIONS_LIMIT]; +}; + +#endif /* Proxy_H */ diff --git a/nxcomp/src/ProxyReadBuffer.cpp b/nxcomp/src/ProxyReadBuffer.cpp new file mode 100644 index 000000000..77f12771b --- /dev/null +++ b/nxcomp/src/ProxyReadBuffer.cpp @@ -0,0 +1,211 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 accommodate + // 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; + } +} diff --git a/nxcomp/src/ProxyReadBuffer.h b/nxcomp/src/ProxyReadBuffer.h new file mode 100644 index 000000000..68e9e95fa --- /dev/null +++ b/nxcomp/src/ProxyReadBuffer.h @@ -0,0 +1,57 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/PutImage.cpp b/nxcomp/src/PutImage.cpp new file mode 100644 index 000000000..d14f922c3 --- /dev/null +++ b/nxcomp/src/PutImage.cpp @@ -0,0 +1,411 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + + // Since ProtoStep7 (#issue 108) + 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; + + // Since ProtoStep8 (#issue 108) + 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 +} diff --git a/nxcomp/src/PutImage.h b/nxcomp/src/PutImage.h new file mode 100644 index 000000000..93dde9dfb --- /dev/null +++ b/nxcomp/src/PutImage.h @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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 */ diff --git a/nxcomp/src/PutPackedImage.cpp b/nxcomp/src/PutPackedImage.cpp new file mode 100644 index 000000000..0bae2c0d4 --- /dev/null +++ b/nxcomp/src/PutPackedImage.cpp @@ -0,0 +1,604 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + 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; + + // Since ProtoStep8 (#issue 108) + 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 +} + diff --git a/nxcomp/src/PutPackedImage.h b/nxcomp/src/PutPackedImage.h new file mode 100644 index 000000000..d28ad39eb --- /dev/null +++ b/nxcomp/src/PutPackedImage.h @@ -0,0 +1,218 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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 */ diff --git a/nxcomp/src/QueryFontReply.cpp b/nxcomp/src/QueryFontReply.cpp new file mode 100644 index 000000000..fde873140 --- /dev/null +++ b/nxcomp/src/QueryFontReply.cpp @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + + // Since ProtoStep7 (#issue 108) + 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 +{ +} diff --git a/nxcomp/src/QueryFontReply.h b/nxcomp/src/QueryFontReply.h new file mode 100644 index 000000000..e12fe4e4b --- /dev/null +++ b/nxcomp/src/QueryFontReply.h @@ -0,0 +1,136 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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 */ diff --git a/nxcomp/src/ReadBuffer.cpp b/nxcomp/src/ReadBuffer.cpp new file mode 100644 index 000000000..154225e75 --- /dev/null +++ b/nxcomp/src/ReadBuffer.cpp @@ -0,0 +1,639 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/ReadBuffer.h b/nxcomp/src/ReadBuffer.h new file mode 100644 index 000000000..a12bcc9b3 --- /dev/null +++ b/nxcomp/src/ReadBuffer.h @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderAddGlyphs.cpp b/nxcomp/src/RenderAddGlyphs.cpp new file mode 100644 index 000000000..ef13d6890 --- /dev/null +++ b/nxcomp/src/RenderAddGlyphs.cpp @@ -0,0 +1,233 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#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 diff --git a/nxcomp/src/RenderAddGlyphs.h b/nxcomp/src/RenderAddGlyphs.h new file mode 100644 index 000000000..d3c8cd158 --- /dev/null +++ b/nxcomp/src/RenderAddGlyphs.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderChangePicture.cpp b/nxcomp/src/RenderChangePicture.cpp new file mode 100644 index 000000000..d202b92ad --- /dev/null +++ b/nxcomp/src/RenderChangePicture.cpp @@ -0,0 +1,238 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#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 diff --git a/nxcomp/src/RenderChangePicture.h b/nxcomp/src/RenderChangePicture.h new file mode 100644 index 000000000..4bab1ef98 --- /dev/null +++ b/nxcomp/src/RenderChangePicture.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderComposite.cpp b/nxcomp/src/RenderComposite.cpp new file mode 100644 index 000000000..f82f80f06 --- /dev/null +++ b/nxcomp/src/RenderComposite.cpp @@ -0,0 +1,400 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#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 diff --git a/nxcomp/src/RenderComposite.h b/nxcomp/src/RenderComposite.h new file mode 100644 index 000000000..aafa1e776 --- /dev/null +++ b/nxcomp/src/RenderComposite.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderCompositeGlyphs.cpp b/nxcomp/src/RenderCompositeGlyphs.cpp new file mode 100644 index 000000000..0949d3e2c --- /dev/null +++ b/nxcomp/src/RenderCompositeGlyphs.cpp @@ -0,0 +1,629 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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); + + // Since ProtoStep8 (#issue 108) + encodeBuffer.encodeDiffCachedValue(src_x, + clientCache -> renderGlyphX, 16, + clientCache -> renderGlyphXCache, 11); + + encodeBuffer.encodeDiffCachedValue(src_y, + clientCache -> renderGlyphY, 16, + clientCache -> renderGlyphYCache, 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. + // + + // Since ProtoStep8 (#issue 108) + if (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; + + // Since ProtoStep8 (#issue 108) + decodeBuffer.decodeDiffCachedValue(src_x, + clientCache -> renderGlyphX, 16, + clientCache -> renderGlyphXCache, 11); + + decodeBuffer.decodeDiffCachedValue(src_y, + clientCache -> renderGlyphY, 16, + clientCache -> renderGlyphYCache, 11); + + PutUINT(src_x, buffer + 24, bigEndian); + PutUINT(src_y, buffer + 26, bigEndian); + + // Since ProtoStep8 (#issue 108) + if (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 +{ + // Since ProtoStep8 (#issue 108) + if (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 +{ + // Since ProtoStep8 (#issue 108) + if (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); + + // Since ProtoStep8 (#issue 108) + if (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); + + // Since ProtoStep8 (#issue 108) + if (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. + // + + // Since ProtoStep8 (#issue 108) + if (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; + + // Since ProtoStep8 (#issue 108) + 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; + + #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 + + // Since ProtoStep8 (#issue 108) + if (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; + + // Since ProtoStep8 (#issue 108) + 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; + + // Since ProtoStep8 (#issue 108) + if (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 diff --git a/nxcomp/src/RenderCompositeGlyphs.h b/nxcomp/src/RenderCompositeGlyphs.h new file mode 100644 index 000000000..1062ee781 --- /dev/null +++ b/nxcomp/src/RenderCompositeGlyphs.h @@ -0,0 +1,100 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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) + { + // Since ProtoStep8 (#issue 108) + unsigned int offset = MESSAGE_OFFSET_IF_PROTO_STEP_8; + + return (size >= offset ? offset : size); + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderCompositeGlyphs_H */ diff --git a/nxcomp/src/RenderCreateGlyphSet.cpp b/nxcomp/src/RenderCreateGlyphSet.cpp new file mode 100644 index 000000000..e3a842de9 --- /dev/null +++ b/nxcomp/src/RenderCreateGlyphSet.cpp @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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 diff --git a/nxcomp/src/RenderCreateGlyphSet.h b/nxcomp/src/RenderCreateGlyphSet.h new file mode 100644 index 000000000..10f5d6699 --- /dev/null +++ b/nxcomp/src/RenderCreateGlyphSet.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderCreatePicture.cpp b/nxcomp/src/RenderCreatePicture.cpp new file mode 100644 index 000000000..f3f9f8b8a --- /dev/null +++ b/nxcomp/src/RenderCreatePicture.cpp @@ -0,0 +1,278 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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 diff --git a/nxcomp/src/RenderCreatePicture.h b/nxcomp/src/RenderCreatePicture.h new file mode 100644 index 000000000..ae2f583a0 --- /dev/null +++ b/nxcomp/src/RenderCreatePicture.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderExtension.cpp b/nxcomp/src/RenderExtension.cpp new file mode 100644 index 000000000..64761bc05 --- /dev/null +++ b/nxcomp/src/RenderExtension.cpp @@ -0,0 +1,427 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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" + +// +// 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(); + + // Since ProtoStep7 (#issue 108) + 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(); + + dataLimit = RENDEREXTENSION_DATA_LIMIT; + dataOffset = RENDEREXTENSION_DATA_OFFSET; + + // Since ProtoStep7 (#issue 108) + cacheSlots = RENDEREXTENSION_CACHE_SLOTS_IF_PROTO_STEP_7; + + 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 +{ + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeLongData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - offset + << " bytes of long data.\n" << logofs_flush; + #endif +} + +void RenderMinorExtensionStore::encodeIntData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeIntData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - offset + << " bytes of int data.\n" << logofs_flush; + #endif +} + +void RenderMinorExtensionStore::encodeCharData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - offset + << " bytes of text data.\n" << logofs_flush; + #endif +} + +void RenderMinorExtensionStore::decodeLongData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeLongData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - offset + << " bytes of long data.\n" << logofs_flush; + #endif +} + +void RenderMinorExtensionStore::decodeIntData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeIntData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - offset + << " bytes of int data.\n" << logofs_flush; + #endif +} + +void RenderMinorExtensionStore::decodeCharData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - offset + << " bytes of text data.\n" << logofs_flush; + #endif +} + +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; + } +} diff --git a/nxcomp/src/RenderExtension.h b/nxcomp/src/RenderExtension.h new file mode 100644 index 000000000..aa9db1b55 --- /dev/null +++ b/nxcomp/src/RenderExtension.h @@ -0,0 +1,504 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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; + + 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 */ diff --git a/nxcomp/src/RenderFillRectangles.cpp b/nxcomp/src/RenderFillRectangles.cpp new file mode 100644 index 000000000..db05887ab --- /dev/null +++ b/nxcomp/src/RenderFillRectangles.cpp @@ -0,0 +1,237 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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 diff --git a/nxcomp/src/RenderFillRectangles.h b/nxcomp/src/RenderFillRectangles.h new file mode 100644 index 000000000..9efaeffa8 --- /dev/null +++ b/nxcomp/src/RenderFillRectangles.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderFreeGlyphSet.cpp b/nxcomp/src/RenderFreeGlyphSet.cpp new file mode 100644 index 000000000..88dfb791a --- /dev/null +++ b/nxcomp/src/RenderFreeGlyphSet.cpp @@ -0,0 +1,166 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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 diff --git a/nxcomp/src/RenderFreeGlyphSet.h b/nxcomp/src/RenderFreeGlyphSet.h new file mode 100644 index 000000000..8817e8d99 --- /dev/null +++ b/nxcomp/src/RenderFreeGlyphSet.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderFreePicture.cpp b/nxcomp/src/RenderFreePicture.cpp new file mode 100644 index 000000000..31662a7e2 --- /dev/null +++ b/nxcomp/src/RenderFreePicture.cpp @@ -0,0 +1,166 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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 diff --git a/nxcomp/src/RenderFreePicture.h b/nxcomp/src/RenderFreePicture.h new file mode 100644 index 000000000..b50191a72 --- /dev/null +++ b/nxcomp/src/RenderFreePicture.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderGenericRequest.cpp b/nxcomp/src/RenderGenericRequest.cpp new file mode 100644 index 000000000..cff34e61e --- /dev/null +++ b/nxcomp/src/RenderGenericRequest.cpp @@ -0,0 +1,270 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} diff --git a/nxcomp/src/RenderGenericRequest.h b/nxcomp/src/RenderGenericRequest.h new file mode 100644 index 000000000..fdf5ca876 --- /dev/null +++ b/nxcomp/src/RenderGenericRequest.h @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderMinorExtensionHeaders.h b/nxcomp/src/RenderMinorExtensionHeaders.h new file mode 100644 index 000000000..b7f6efc5a --- /dev/null +++ b/nxcomp/src/RenderMinorExtensionHeaders.h @@ -0,0 +1,42 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderMinorExtensionMethods.h b/nxcomp/src/RenderMinorExtensionMethods.h new file mode 100644 index 000000000..d272337e0 --- /dev/null +++ b/nxcomp/src/RenderMinorExtensionMethods.h @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +// +// 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; diff --git a/nxcomp/src/RenderMinorExtensionTags.h b/nxcomp/src/RenderMinorExtensionTags.h new file mode 100644 index 000000000..c24a99638 --- /dev/null +++ b/nxcomp/src/RenderMinorExtensionTags.h @@ -0,0 +1,194 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderPictureClip.cpp b/nxcomp/src/RenderPictureClip.cpp new file mode 100644 index 000000000..0d6b505eb --- /dev/null +++ b/nxcomp/src/RenderPictureClip.cpp @@ -0,0 +1,303 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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 diff --git a/nxcomp/src/RenderPictureClip.h b/nxcomp/src/RenderPictureClip.h new file mode 100644 index 000000000..bd811dfcd --- /dev/null +++ b/nxcomp/src/RenderPictureClip.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderPictureFilter.cpp b/nxcomp/src/RenderPictureFilter.cpp new file mode 100644 index 000000000..ce40e051a --- /dev/null +++ b/nxcomp/src/RenderPictureFilter.cpp @@ -0,0 +1,278 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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 diff --git a/nxcomp/src/RenderPictureFilter.h b/nxcomp/src/RenderPictureFilter.h new file mode 100644 index 000000000..a3e37538b --- /dev/null +++ b/nxcomp/src/RenderPictureFilter.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderPictureTransform.cpp b/nxcomp/src/RenderPictureTransform.cpp new file mode 100644 index 000000000..0048e61f4 --- /dev/null +++ b/nxcomp/src/RenderPictureTransform.cpp @@ -0,0 +1,214 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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 diff --git a/nxcomp/src/RenderPictureTransform.h b/nxcomp/src/RenderPictureTransform.h new file mode 100644 index 000000000..649cd05d3 --- /dev/null +++ b/nxcomp/src/RenderPictureTransform.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderTrapezoids.cpp b/nxcomp/src/RenderTrapezoids.cpp new file mode 100644 index 000000000..bf84509dc --- /dev/null +++ b/nxcomp/src/RenderTrapezoids.cpp @@ -0,0 +1,372 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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 diff --git a/nxcomp/src/RenderTrapezoids.h b/nxcomp/src/RenderTrapezoids.h new file mode 100644 index 000000000..faf524c76 --- /dev/null +++ b/nxcomp/src/RenderTrapezoids.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/RenderTriangles.cpp b/nxcomp/src/RenderTriangles.cpp new file mode 100644 index 000000000..a5eb4d83d --- /dev/null +++ b/nxcomp/src/RenderTriangles.cpp @@ -0,0 +1,362 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// +// Include 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 diff --git a/nxcomp/src/RenderTriangles.h b/nxcomp/src/RenderTriangles.h new file mode 100644 index 000000000..d73efb7b8 --- /dev/null +++ b/nxcomp/src/RenderTriangles.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Rgb.cpp b/nxcomp/src/Rgb.cpp new file mode 100644 index 000000000..c98fa5147 --- /dev/null +++ b/nxcomp/src/Rgb.cpp @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/Rgb.h b/nxcomp/src/Rgb.h new file mode 100644 index 000000000..ec088dd1b --- /dev/null +++ b/nxcomp/src/Rgb.h @@ -0,0 +1,36 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Rle.cpp b/nxcomp/src/Rle.cpp new file mode 100644 index 000000000..2a145aa0c --- /dev/null +++ b/nxcomp/src/Rle.cpp @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/Rle.h b/nxcomp/src/Rle.h new file mode 100644 index 000000000..cc5dab0e8 --- /dev/null +++ b/nxcomp/src/Rle.h @@ -0,0 +1,36 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/SendEvent.cpp b/nxcomp/src/SendEvent.cpp new file mode 100644 index 000000000..8867d0a9e --- /dev/null +++ b/nxcomp/src/SendEvent.cpp @@ -0,0 +1,304 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} diff --git a/nxcomp/src/SendEvent.h b/nxcomp/src/SendEvent.h new file mode 100644 index 000000000..a8841a706 --- /dev/null +++ b/nxcomp/src/SendEvent.h @@ -0,0 +1,195 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/SequenceQueue.cpp b/nxcomp/src/SequenceQueue.cpp new file mode 100644 index 000000000..a045875d7 --- /dev/null +++ b/nxcomp/src/SequenceQueue.cpp @@ -0,0 +1,174 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + } +} diff --git a/nxcomp/src/SequenceQueue.h b/nxcomp/src/SequenceQueue.h new file mode 100644 index 000000000..2a72bc3fe --- /dev/null +++ b/nxcomp/src/SequenceQueue.h @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/ServerCache.cpp b/nxcomp/src/ServerCache.cpp new file mode 100644 index 000000000..c4c088dca --- /dev/null +++ b/nxcomp/src/ServerCache.cpp @@ -0,0 +1,195 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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), + + 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) + +{ + 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]; + } +} diff --git a/nxcomp/src/ServerCache.h b/nxcomp/src/ServerCache.h new file mode 100644 index 000000000..c6e2b81b6 --- /dev/null +++ b/nxcomp/src/ServerCache.h @@ -0,0 +1,303 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef ServerCache_H +#define ServerCache_H + +#include "Misc.h" + +#include "IntCache.h" +#include "CharCache.h" +#include "OpcodeCache.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. + // + + 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; + 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; + + // + // 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 */ diff --git a/nxcomp/src/ServerChannel.cpp b/nxcomp/src/ServerChannel.cpp new file mode 100644 index 000000000..830985295 --- /dev/null +++ b/nxcomp/src/ServerChannel.cpp @@ -0,0 +1,7946 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <sys/types.h> +#include <sys/ipc.h> +#ifndef ANDROID +#include <sys/shm.h> +#endif + +#include <X11/X.h> +#include <X11/Xatom.h> + +#include "NXproto.h" +#include "NXalert.h" +#include "NXpack.h" +#include "NXmitshm.h" + +#include "ServerChannel.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "StaticCompressor.h" + +#include "Statistics.h" +#include "Proxy.h" + +#include "Auth.h" +#include "Unpack.h" + +// +// Available unpack methods. +// + +#include "Alpha.h" +#include "Colormap.h" +#include "Bitmap.h" +#include "Jpeg.h" +#include "Pgn.h" +#include "Rgb.h" +#include "Rle.h" + +extern Proxy *proxy; + +// +// Set the verbosity level. You also +// need to define OPCODES in Misc.cpp +// if you want literals instead of +// opcodes' numbers. +// + +#define PANIC +#define WARNING +#undef OPCODES +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Log the important tracepoints related +// to writing packets to the peer proxy. +// + +#undef FLUSH + +// +// Log the operations related to splits. +// + +#undef SPLIT + +// +// Define this to log when a channel +// is created or destroyed. +// + +#undef REFERENCES + +// +// Define this to exit and suspend the +// session after a given number of X +// messages decoded by the proxy. +// + +#undef SUSPEND + +// +// Define these to hide the server extensions. +// + +#define HIDE_MIT_SHM_EXTENSION +#define HIDE_BIG_REQUESTS_EXTENSION +#define HIDE_XFree86_Bigfont_EXTENSION +#undef HIDE_SHAPE_EXTENSION +#undef HIDE_XKEYBOARD_EXTENSION + +// +// Known reasons of connection failures. +// + +#define INVALID_COOKIE_DATA "Invalid MIT-MAGIC-COOKIE-1 key" +#define INVALID_COOKIE_SIZE ((int) sizeof(INVALID_COOKIE_DATA) - 1) + +#define NO_AUTH_PROTO_DATA "No protocol specified" +#define NO_AUTH_PROTO_SIZE ((int) sizeof(NO_AUTH_PROTO_DATA) - 1) + +// +// Here are the static members. +// + +#ifdef REFERENCES + +int ServerChannel::references_ = 0; + +#endif + +ServerChannel::ServerChannel(Transport *transport, StaticCompressor *compressor) + + : Channel(transport, compressor), readBuffer_(transport_, this) +{ + // + // Sequence number of the next message + // being encoded or decoded. + // + + clientSequence_ = 0; + serverSequence_ = 0; + + // + // Save the last motion event and flush + // it only when the timeout expires. + // + + lastMotion_[0] = '\0'; + + // + // Clear the queue of sequence numbers + // of split commits. Used to mask the + // errors. + // + + initCommitQueue(); + + // + // Do we enable or not sending of expose + // events to the X client. + // + + enableExpose_ = 1; + enableGraphicsExpose_ = 1; + enableNoExpose_ = 1; + + // + // Track data of image currently being + // decompressed. + // + + imageState_ = NULL; + + // + // Track MIT-SHM resources. + // + + shmemState_ = NULL; + + // + // Store the unpack state for each agent + // resource. + // + + for (int i = 0; i < CONNECTIONS_LIMIT; i++) + { + unpackState_[i] = NULL; + } + + // + // Data about the split parameters requested + // by the encoding side. + // + + splitState_.resource = nothing; + splitState_.current = 0; + splitState_.save = 1; + splitState_.load = 1; + splitState_.commit = 0; + + // + // It will be eventually set by + // the server proxy. + // + + fontPort_ = -1; + + #ifdef REFERENCES + *logofs << "ServerChannel: Created new object at " + << this << " for FD#" << fd_ << " out of " + << ++references_ << " allocated channels.\n" + << logofs_flush; + #endif +} + +ServerChannel::~ServerChannel() +{ + #ifdef TEST + *logofs << "ServerChannel: Freeing image state information.\n" + << logofs_flush; + #endif + + handleImageStateRemove(); + + #ifdef TEST + *logofs << "ServerChannel: Freeing shared memory information.\n" + << logofs_flush; + #endif + + handleShmemStateRemove(); + + #ifdef TEST + *logofs << "ServerChannel: Freeing unpack state information.\n" + << logofs_flush; + #endif + + for (int i = 0; i < CONNECTIONS_LIMIT; i++) + { + handleUnpackStateRemove(i); + } + + #ifdef TEST + *logofs << "ServerChannel: Freeing channel caches.\n" + << logofs_flush; + #endif + + #ifdef REFERENCES + *logofs << "ServerChannel: Deleted object at " + << this << " for FD#" << fd_ << " out of " + << --references_ << " allocated channels.\n" + << logofs_flush; + #endif +} + +// +// Beginning of handleRead(). +// + +int ServerChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, + unsigned int length) +{ + #ifdef DEBUG + *logofs << "handleRead: Called for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Pointer to located message and + // its size in bytes. + // + + const unsigned char *inputMessage; + unsigned int inputLength; + + // + // Set when message is found in + // cache. + // + + int hit; + + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: Trying to read from FD#" + << fd_ << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + int result = readBuffer_.readMessage(); + + #if defined(DEBUG) || defined(INFO) + *logofs << "handleRead: Read result on FD#" << fd_ + << " is " << result << ".\n" + << logofs_flush; + #endif + + if (result < 0) + { + // + // Let the proxy close the channel. + // + + return -1; + } + else if (result == 0) + { + #if defined(TEST) || defined(INFO) + + // + // This can happen because we have the descriptor + // selected in the read set but we already read + // the data asynchronously, while decoding data + // read from the proxy. + // + + *logofs << "handleRead: WARNING! No data read from FD#" + << fd_ << " while encoding messages.\n" + << logofs_flush; + + #endif + + return 0; + } + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "handleRead: Encoding messages for FD#" << fd_ + << " with " << readBuffer_.getLength() << " bytes " + << "in the buffer.\n" << logofs_flush; + #endif + + // + // Extract any complete message which + // is available in the buffer. + // + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL) + { + hit = 0; + + if (firstReply_) + { + // + // Handle the X server's authorization reply. + // + + if (handleAuthorization(inputMessage, inputLength) < 0) + { + return -1; + } + + imageByteOrder_ = inputMessage[30]; + bitmapBitOrder_ = inputMessage[31]; + scanlineUnit_ = inputMessage[32]; + scanlinePad_ = inputMessage[33]; + + encodeBuffer.encodeValue((unsigned int) inputMessage[0], 8); + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 8); + encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_), 16); + encodeBuffer.encodeValue(GetUINT(inputMessage + 4, bigEndian_), 16); + encodeBuffer.encodeValue(GetUINT(inputMessage + 6, bigEndian_), 16); + + if (ServerCache::lastInitReply.compare(inputLength - 8, inputMessage + 8)) + { + encodeBuffer.encodeBoolValue(1); + } + else + { + encodeBuffer.encodeBoolValue(0); + + for (unsigned int i = 8; i < inputLength; i++) + { + encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8); + } + } + + firstReply_ = 0; + + #if defined(TEST) || defined(OPCODES) + + int bits = encodeBuffer.diffBits(); + + *logofs << "handleRead: Handled first reply. " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + + #endif + + priority_++; + + } + else + { + // + // NX client needs this line to consider + // the initialization phase successfully + // completed. + // + + if (firstClient_ == -1) + { + cerr << "Info" << ": Established X server connection.\n" ; + + firstClient_ = fd_; + } + + // + // Check if this is a reply. + // + + if (*inputMessage == X_Reply) + { + int bits = 0; + + unsigned char inputOpcode = *inputMessage; + + unsigned short int requestSequenceNum; + unsigned char requestOpcode; + unsigned int requestData[3]; + + unsigned int sequenceNum = GetUINT(inputMessage + 2, bigEndian_); + + #ifdef SUSPEND + + if (sequenceNum >= 1000) + { + cerr << "Warning" << ": Exiting to test the resilience of the agent.\n"; + + sleep(2); + + HandleAbort(); + } + + #endif + + // + // We managed all the events and errors caused + // by the previous requests. We can now reset + // the queue of split commits. + // + + clearCommitQueue(); + + // + // Encode opcode and difference between + // current sequence and the last one. + // + + encodeBuffer.encodeOpcodeValue(inputOpcode, serverCache_ -> opcodeCache); + + unsigned int sequenceDiff = sequenceNum - serverSequence_; + + serverSequence_ = sequenceNum; + + #ifdef DEBUG + *logofs << "handleRead: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sequenceDiff, 16, + serverCache_ -> replySequenceCache, 7); + + // + // Now handle the data part. + // + + if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) && + requestSequenceNum == sequenceNum) + { + // + // We've found the request that generated this reply. + // It is possible to compress the reply based on the + // specific request type. + // + + sequenceQueue_.pop(requestSequenceNum, requestOpcode, + requestData[0], requestData[1], requestData[2]); + + // + // If differential compression is disabled + // then use the most simple encoding. + // + + if (control -> LocalDeltaCompression == 0) + { + int _result = handleFastReadReply(encodeBuffer, requestOpcode, + inputMessage, inputLength); + if (_result < 0) + { + return -1; + } + else if (_result > 0) + { + continue; + } + } + + switch (requestOpcode) + { + case X_AllocColor: + { + const unsigned char *nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + unsigned int colorValue = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + if (colorValue == requestData[i]) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(colorValue - colorValue, 16, 6); + } + } + unsigned int pixel = GetULONG(inputMessage + 16, bigEndian_); + encodeBuffer.encodeValue(pixel, 32, 9); + + priority_++; + } + break; + case X_GetAtomName: + { + unsigned int nameLength = GetUINT(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(nameLength, 16, 6); + const unsigned char *nextSrc = inputMessage + 32; + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, nameLength); + + priority_++; + } + break; + case X_GetGeometry: + { + // + // TODO: This obtains a satisfactory 10:1, but + // could be cached to leverage the big amount + // of such requests issued by QT clients. + // + + encodeBuffer.encodeCachedValue(inputMessage[1], 8, + serverCache_ -> depthCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> getGeometryRootCache, 9); + const unsigned char *nextSrc = inputMessage + 12; + for (unsigned int i = 0; i < 5; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *serverCache_ -> getGeometryGeomCache[i], 8); + nextSrc += 2; + } + + priority_++; + } + break; + case X_GetInputFocus: + { + // + // Is it a real X_GetInputFocus or a + // masqueraded reply? + // + + if (requestData[0] == X_GetInputFocus) + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> getInputFocusWindowCache); + + priority_++; + } + else + { + // + // TODO: We are not setting priority in case + // of replies other than real X_GetInputFocus + // or X_NXGetUnpackParameters. We should check + // once again that this is OK. + // + + #ifdef TEST + *logofs << "handleRead: Received tainted X_GetInputFocus reply " + << "for request OPCODE#" << requestData[0] << " with " + << "sequence " << sequenceNum << ".\n" + << logofs_flush; + #endif + + // + // Don't encode any data in case of sync + // messages or any other reply for which + // opcode is enough. + // + + if (requestData[0] == opcodeStore_ -> getUnpackParameters) + { + for (int i = 0; i < PACK_METHOD_LIMIT; i++) + { + encodeBuffer.encodeBoolValue(control -> LocalUnpackMethods[i]); + } + + priority_++; + } + else if (requestData[0] == opcodeStore_ -> getShmemParameters) + { + if (handleShmemReply(encodeBuffer, requestOpcode, requestData[1], + inputMessage, inputLength) < 0) + { + return -1; + } + + priority_++; + } + else if (requestData[0] == opcodeStore_ -> getFontParameters) + { + if (handleFontReply(encodeBuffer, requestOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + + // + // Account this data to the original opcode. + // + + requestOpcode = requestData[0]; + } + } + break; + case X_GetKeyboardMapping: + { + unsigned int keysymsPerKeycode = (unsigned int) inputMessage[1]; + if (ServerCache::getKeyboardMappingLastMap.compare(inputLength - 32, + inputMessage + 32) && (keysymsPerKeycode == + ServerCache::getKeyboardMappingLastKeysymsPerKeycode)) + { + encodeBuffer.encodeBoolValue(1); + + priority_++; + + break; + } + ServerCache::getKeyboardMappingLastKeysymsPerKeycode = keysymsPerKeycode; + encodeBuffer.encodeBoolValue(0); + unsigned int numKeycodes = + (((inputLength - 32) / keysymsPerKeycode) >> 2); + encodeBuffer.encodeValue(numKeycodes, 8); + encodeBuffer.encodeValue(keysymsPerKeycode, 8, 4); + const unsigned char *nextSrc = inputMessage + 32; + unsigned char previous = 0; + for (unsigned int count = numKeycodes * keysymsPerKeycode; + count; --count) + { + unsigned int keysym = GetULONG(nextSrc, bigEndian_); + nextSrc += 4; + if (keysym == NoSymbol) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + unsigned int first3Bytes = (keysym >> 8); + encodeBuffer.encodeCachedValue(first3Bytes, 24, + serverCache_ -> getKeyboardMappingKeysymCache, 9); + unsigned char lastByte = (unsigned char) (keysym & 0xff); + encodeBuffer.encodeCachedValue(lastByte - previous, 8, + serverCache_ -> getKeyboardMappingLastByteCache, 5); + previous = lastByte; + } + } + + priority_++; + } + break; + case X_GetModifierMapping: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 8); + const unsigned char *nextDest = inputMessage + 32; + if (ServerCache::getModifierMappingLastMap.compare(inputLength - 32, + nextDest)) + { + encodeBuffer.encodeBoolValue(1); + + priority_++; + + break; + } + encodeBuffer.encodeBoolValue(0); + for (unsigned int count = inputLength - 32; count; count--) + { + unsigned char next = *nextDest++; + if (next == 0) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(next, 8); + } + } + + priority_++; + } + break; + case X_GetProperty: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_GetProperty); + + hit = handleEncode(encodeBuffer, serverCache_, messageStore, + requestOpcode, inputMessage, inputLength); + + priority_++; + } + break; + case X_GetSelectionOwner: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> getSelectionOwnerCache, 9); + priority_++; + } + break; + case X_GetWindowAttributes: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> visualCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), + 16, serverCache_ -> getWindowAttributesClassCache, 3); + encodeBuffer.encodeCachedValue(inputMessage[14], 8, + serverCache_ -> getWindowAttributesBitGravityCache); + encodeBuffer.encodeCachedValue(inputMessage[15], 8, + serverCache_ -> getWindowAttributesWinGravityCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), + 32, serverCache_ -> getWindowAttributesPlanesCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 20, bigEndian_), + 32, serverCache_ -> getWindowAttributesPixelCache, 9); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[24]); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[25]); + encodeBuffer.encodeValue((unsigned int) inputMessage[26], 2); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[27]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 28, bigEndian_), + 29, serverCache_ -> colormapCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 32, bigEndian_), + 32, serverCache_ -> getWindowAttributesAllEventsCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 36, bigEndian_), + 32, serverCache_ -> getWindowAttributesYourEventsCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 40, bigEndian_), + 16, serverCache_ -> getWindowAttributesDontPropagateCache); + + priority_++; + } + break; + case X_GrabKeyboard: + case X_GrabPointer: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 3); + + priority_++; + } + break; + case X_InternAtom: + { + encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 29, 9); + + priority_++; + } + break; + case X_ListExtensions: + { + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8); + unsigned int numExtensions = (unsigned int) inputMessage[1]; + encodeBuffer.encodeValue(numExtensions, 8); + const unsigned char *nextSrc = inputMessage + 32; + + for (; numExtensions; numExtensions--) + { + unsigned int _length = (unsigned int) (*nextSrc++); + + encodeBuffer.encodeValue(_length, 8); + + #ifdef HIDE_MIT_SHM_EXTENSION + + if (!strncmp((char *) nextSrc, "MIT-SHM", 7)) + { + #ifdef TEST + *logofs << "handleRead: Hiding MIT-SHM extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-MIT-", 7); + } + + #endif + + #ifdef HIDE_BIG_REQUESTS_EXTENSION + + if (!strncmp((char *) nextSrc, "BIG-REQUESTS", 12)) + { + #ifdef TEST + *logofs << "handleRead: Hiding BIG-REQUESTS extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-BIG-REQUE", 12); + } + + #endif + + #ifdef HIDE_XKEYBOARD_EXTENSION + + if (!strncmp((char *) nextSrc, "XKEYBOARD", 9)) + { + #ifdef TEST + *logofs << "handleRead: Hiding XKEYBOARD extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-XKEYBO", 9); + } + + #endif + + #ifdef HIDE_XFree86_Bigfont_EXTENSION + + if (!strncmp((char *) nextSrc, "XFree86-Bigfont", 15)) + { + #ifdef TEST + *logofs << "handleRead: Hiding XFree86-Bigfont extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-XFree86-Bigf", 15); + } + + #endif + + #ifdef HIDE_SHAPE_EXTENSION + + if (!strncmp((char *) nextSrc, "SHAPE", 5)) + { + #ifdef TEST + *logofs << "handleRead: Hiding SHAPE extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-SH", 5); + } + + #endif + + // + // Check if user disabled RENDER extension. + // + + if (control -> HideRender == 1 && + !strncmp((char *) nextSrc, "RENDER", 6)) + { + #ifdef TEST + *logofs << "handleRead: Hiding RENDER extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-REN", 6); + } + + for (; length; length--) + { + encodeBuffer.encodeValue((unsigned int) (*nextSrc++), 8); + } + } + + priority_++; + } + break; + case X_ListFonts: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_ListFonts); + + if (handleEncodeCached(encodeBuffer, serverCache_, messageStore, + inputMessage, inputLength)) + { + priority_++; + + hit = 1; + + break; + } + + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8); + unsigned int numFonts = GetUINT(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(numFonts, 16, 6); + + // Differential encoding. + encodeBuffer.encodeBoolValue(1); + + const unsigned char* nextSrc = inputMessage + 32; + for (; numFonts; numFonts--) + { + unsigned int _length = (unsigned int) (*nextSrc++); + encodeBuffer.encodeValue(_length, 8); + + // Since ProtoStep7 (#issue 108) + encodeBuffer.encodeTextData(nextSrc, _length); + + nextSrc += _length; + } + + priority_++; + } + break; + case X_LookupColor: + case X_AllocNamedColor: + { + const unsigned char *nextSrc = inputMessage + 8; + if (requestOpcode == X_AllocNamedColor) + { + encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 9); + nextSrc += 4; + } + unsigned int count = 3; + do + { + unsigned int exactColor = GetUINT(nextSrc, bigEndian_); + encodeBuffer.encodeValue(exactColor, 16, 9); + unsigned int visualColor = GetUINT(nextSrc + 6, bigEndian_) - + exactColor; + encodeBuffer.encodeValue(visualColor, 16, 5); + nextSrc += 2; + } + while (--count); + + priority_++; + } + break; + case X_QueryBestSize: + { + encodeBuffer.encodeValue(GetUINT(inputMessage + 8, bigEndian_), 16, 8); + encodeBuffer.encodeValue(GetUINT(inputMessage + 10, bigEndian_), 16, 8); + + priority_++; + } + break; + case X_QueryColors: + { + // Differential encoding. + encodeBuffer.encodeBoolValue(1); + + unsigned int numColors = ((inputLength - 32) >> 3); + const unsigned char *nextSrc1 = 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++ = *nextSrc1++; + nextSrc1 += 2; + } + unsigned int colorsLength = numColors * 6; + if (serverCache_ -> queryColorsLastReply.compare(colorsLength, + inputMessage + 32)) + encodeBuffer.encodeBoolValue(1); + else + { + const unsigned char *nextSrc2 = inputMessage + 32; + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(numColors, 16, 5); + for (numColors *= 3; numColors; numColors--) + { + encodeBuffer.encodeValue(GetUINT(nextSrc2, bigEndian_), 16); + nextSrc2 += 2; + } + } + + priority_++; + } + break; + case X_QueryExtension: + { + if (requestData[0] == X_QueryExtension) + { + // + // Value in requestData[0] will be nonzero + // if the request is for an extension that + // we should hide to the X client. + // + + if (requestData[1]) + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(0, 8); + } + else + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[8]); + encodeBuffer.encodeValue((unsigned int) inputMessage[9], 8); + } + + encodeBuffer.encodeValue((unsigned int) inputMessage[10], 8); + encodeBuffer.encodeValue((unsigned int) inputMessage[11], 8); + + if (requestData[2] == X_NXInternalShapeExtension) + { + opcodeStore_ -> shapeExtension = inputMessage[9]; + + #ifdef TEST + *logofs << "handleRead: Shape extension opcode for FD#" << fd_ + << " is " << (unsigned int) opcodeStore_ -> shapeExtension + << ".\n" << logofs_flush; + #endif + } + else if (requestData[2] == X_NXInternalRenderExtension) + { + opcodeStore_ -> renderExtension = inputMessage[9]; + + #ifdef TEST + *logofs << "handleRead: Render extension opcode for FD#" << fd_ + << " is " << (unsigned int) opcodeStore_ -> renderExtension + << ".\n" << logofs_flush; + #endif + } + + priority_++; + } + else + { + #ifdef TEST + *logofs << "handleRead: Received tainted X_QueryExtension reply " + << "for request OPCODE#" << requestData[0] << " with " + << "sequence " << sequenceNum << ".\n" + << logofs_flush; + #endif + + if (requestData[0] == opcodeStore_ -> getShmemParameters) + { + if (handleShmemReply(encodeBuffer, requestOpcode, requestData[1], + inputMessage, inputLength) < 0) + { + return -1; + } + + priority_++; + } + // + // Account this data to the original opcode. + // + + requestOpcode = requestData[0]; + } + } + break; + case X_QueryFont: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_QueryFont); + + if (handleEncodeCached(encodeBuffer, serverCache_, messageStore, + inputMessage, inputLength)) + { + priority_++; + + hit = 1; + + break; + } + + // Differential encoding. + encodeBuffer.encodeBoolValue(1); + + unsigned int numProperties = GetUINT(inputMessage + 46, bigEndian_); + unsigned int numCharInfos = GetULONG(inputMessage + 56, bigEndian_); + encodeBuffer.encodeValue(numProperties, 16, 8); + encodeBuffer.encodeValue(numCharInfos, 32, 10); + handleEncodeCharInfo(inputMessage + 8, encodeBuffer); + handleEncodeCharInfo(inputMessage + 24, encodeBuffer); + encodeBuffer.encodeValue(GetUINT(inputMessage + 40, bigEndian_), 16, 9); + encodeBuffer.encodeValue(GetUINT(inputMessage + 42, bigEndian_), 16, 9); + encodeBuffer.encodeValue(GetUINT(inputMessage + 44, bigEndian_), 16, 9); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[48]); + encodeBuffer.encodeValue((unsigned int) inputMessage[49], 8); + encodeBuffer.encodeValue((unsigned int) inputMessage[50], 8); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[51]); + encodeBuffer.encodeValue(GetUINT(inputMessage + 52, bigEndian_), 16, 9); + encodeBuffer.encodeValue(GetUINT(inputMessage + 54, bigEndian_), 16, 9); + const unsigned char *nextSrc = inputMessage + 60; + unsigned int index; + + int end = 0; + + if (ServerCache::queryFontFontCache.lookup(numProperties * 8 + + numCharInfos * 12, nextSrc, index)) + { + encodeBuffer.encodeBoolValue(1); + encodeBuffer.encodeValue(index, 4); + + end = 1; + } + + if (end == 0) + { + encodeBuffer.encodeBoolValue(0); + for (; numProperties; numProperties--) + { + encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 9); + encodeBuffer.encodeValue(GetULONG(nextSrc + 4, bigEndian_), 32, 9); + nextSrc += 8; + } + for (; numCharInfos; numCharInfos--) + { + handleEncodeCharInfo(nextSrc, encodeBuffer); + + nextSrc += 12; + } + } + + priority_++; + } + break; + case X_QueryPointer: + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> queryPointerRootCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), + 29, serverCache_ -> queryPointerChildCache, 9); + unsigned int rootX = GetUINT(inputMessage + 16, bigEndian_); + unsigned int rootY = GetUINT(inputMessage + 18, bigEndian_); + unsigned int eventX = GetUINT(inputMessage + 20, bigEndian_); + unsigned int eventY = GetUINT(inputMessage + 22, bigEndian_); + eventX -= rootX; + eventY -= rootY; + encodeBuffer.encodeCachedValue( + rootX - serverCache_ -> motionNotifyLastRootX, 16, + serverCache_ -> motionNotifyRootXCache, 8); + serverCache_ -> motionNotifyLastRootX = rootX; + encodeBuffer.encodeCachedValue( + rootY - serverCache_ -> motionNotifyLastRootY, 16, + serverCache_ -> motionNotifyRootYCache, 8); + serverCache_ -> motionNotifyLastRootY = rootY; + encodeBuffer.encodeCachedValue(eventX, 16, + serverCache_ -> motionNotifyEventXCache, 8); + encodeBuffer.encodeCachedValue(eventY, 16, + serverCache_ -> motionNotifyEventYCache, 8); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 24, bigEndian_), + 16, serverCache_ -> motionNotifyStateCache); + priority_++; + } + break; + case X_QueryTree: + { + // + // This was very inefficient. In practice + // it just copied data on the output. Now + // it obtains an average 7:1 compression + // and could optionally be cached. + // + + unsigned int children = GetUINT(inputMessage + 16, bigEndian_); + + encodeBuffer.encodeValue(children, 16, 8); + + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, + serverCache_ -> queryTreeWindowCache); + + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29, + serverCache_ -> queryTreeWindowCache); + + const unsigned char *next = inputMessage + 32; + + for (unsigned int i = 0; i < children; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(next + (i * 4), bigEndian_), 29, + serverCache_ -> queryTreeWindowCache); + } + + priority_++; + } + break; + case X_TranslateCoords: + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> translateCoordsChildCache, 9); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), + 16, serverCache_ -> translateCoordsXCache, 8); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 14, bigEndian_), + 16, serverCache_ -> translateCoordsYCache, 8); + priority_++; + } + break; + case X_GetImage: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_GetImage); + + if (handleEncodeCached(encodeBuffer, serverCache_, messageStore, + inputMessage, inputLength)) + { + priority_++; + + hit = 1; + + break; + } + + // Depth. + encodeBuffer.encodeCachedValue(inputMessage[1], 8, + serverCache_ -> depthCache); + // Reply length. + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 9); + + // Visual. + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, + serverCache_ -> visualCache); + + // Since ProtoStep8 (#issue 108) + handleCopy(encodeBuffer, requestOpcode, messageStore -> + dataOffset, inputMessage, inputLength); + + priority_++; + } + break; + case X_GetPointerMapping: + { + encodeBuffer.encodeValue(inputMessage[1], 8, 4); + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 4); + for (unsigned int i = 32; i < inputLength; i++) + encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8, 4); + + priority_++; + } + break; + case X_GetKeyboardControl: + { + encodeBuffer.encodeValue(inputMessage[1], 8, 2); + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8); + for (unsigned int i = 8; i < inputLength; i++) + encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8, 4); + + priority_++; + } + break; + default: + { + #ifdef PANIC + *logofs << "ServerChannel: PANIC! No matching request with " + << "OPCODE#" << (unsigned int) requestOpcode + << " for reply with sequence number " + << requestSequenceNum << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": No matching request with OPCODE#" + << (unsigned int) requestOpcode << " for reply with " + << "sequence number " << requestSequenceNum << ".\n"; + + return -1; + } + } + + bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + const char *cacheString = (hit ? "cached " : ""); + + *logofs << "handleRead: Handled " << cacheString << "reply to OPCODE#" + << (unsigned int) requestOpcode << " (" << DumpOpcode(requestOpcode) + << ") for FD#" << fd_ << " sequence " << serverSequence_ + << ". " << inputLength << " bytes in, " << bits << " bits (" + << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; + + #endif + + } // End of if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) && ... + else + { + // + // We didn't push the request opcode. + // Check if fast encoding is required. + // + + requestOpcode = X_Reply; + + if (control -> LocalDeltaCompression == 0) + { + int _result = handleFastReadReply(encodeBuffer, requestOpcode, + inputMessage, inputLength); + if (_result < 0) + { + return -1; + } + else if (_result > 0) + { + continue; + } + } + + // + // Group all replies whose opcode was not + // pushed in sequence number queue under + // the category 'generic reply'. + // + + #ifdef DEBUG + *logofs << "handleRead: Identified generic reply.\n" + << logofs_flush; + #endif + + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_NXInternalGenericReply); + + hit = handleEncode(encodeBuffer, serverCache_, messageStore, + requestOpcode, inputMessage, inputLength); + + priority_++; + + bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + const char *cacheString = (hit ? "cached " : ""); + + *logofs << "handleRead: Handled " << cacheString << "generic reply " + << "OPCODE#" << X_NXInternalGenericReply << " for FD#" << fd_ + << " sequence " << serverSequence_ << ". " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + + #endif + } + + if (hit) + { + statistics -> addCachedReply(requestOpcode); + } + + statistics -> addReplyBits(requestOpcode, inputLength << 3, bits); + + } // End of if (inputMessage[0] == 1) ... + else + { + // + // Event or error. + // + + unsigned char inputOpcode = *inputMessage; + + unsigned int inputSequence = GetUINT(inputMessage + 2, bigEndian_); + + // + // Check if this is an event which we can discard. + // + + if ((inputOpcode == Expose && enableExpose_ == 0) || + (inputOpcode == GraphicsExpose && enableGraphicsExpose_ == 0) || + (inputOpcode == NoExpose && enableNoExpose_ == 0)) + { + continue; + } + else if (shmemState_ != NULL && shmemState_ -> enabled == 1 && + inputOpcode == shmemState_ -> event && + checkShmemEvent(inputOpcode, inputSequence, + inputMessage) > 0) + { + continue; + } + else if (inputOpcode == MotionNotify) + { + // + // Save the motion event and send when another + // event or error is received or the motion ti- + // meout is elapsed. If a previous motion event + // was already saved, we replace it with the + // new one and don't reset the timeout, so we + // still have a motion event every given ms. + // + + memcpy(lastMotion_, inputMessage, 32); + + #ifdef TEST + *logofs << "handleRead: Saved suppressed motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + continue; + } + else if (inputOpcode == X_Error) + { + // + // Check if this is an error that matches a + // sequence number for which we are expecting + // a reply. + // + + unsigned short int errorSequenceNum; + unsigned char errorOpcode; + + if (sequenceQueue_.peek(errorSequenceNum, errorOpcode) && + ((unsigned int) errorSequenceNum == inputSequence)) + { + sequenceQueue_.pop(errorSequenceNum, errorOpcode); + } + + // + // Check if error is due to an image commit + // generated at the end of a split. + // + + if (checkCommitError(*(inputMessage + 1), inputSequence, inputMessage) > 0) + { + #ifdef TEST + *logofs << "handleRead: Skipping error on image commit for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + continue; + } + + // + // Check if it's an error generated by a request + // concerning shared memory support. + // + + else if (shmemState_ != NULL && (shmemState_ -> sequence == + inputSequence || (shmemState_ -> enabled == 1 && + (shmemState_ -> opcode == *(inputMessage + 10) || + shmemState_ -> error == *(inputMessage + 1)))) && + checkShmemError(*(inputMessage + 1), inputSequence, + inputMessage) > 0) + { + #ifdef TEST + *logofs << "handleRead: Skipping error on shmem operation for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + continue; + } + } + // + // Check if user pressed the CTRL+ALT+SHIFT+ESC key + // sequence because was unable to kill the session + // through the normal procedure. + // + + if (inputOpcode == KeyPress) + { + if (checkKeyboardEvent(inputOpcode, inputSequence, inputMessage) == 1) + { + #ifdef TEST + *logofs << "handleRead: Removing the key sequence from the " + << "event stream for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + continue; + } + } + + // + // We are going to handle an event or error + // that's not a mouse motion. Prepend any + // saved motion to it. + // + + if (lastMotion_[0] != '\0') + { + if (handleMotion(encodeBuffer) < 0) + { + #ifdef PANIC + *logofs << "handleRead: PANIC! Can't encode motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't encode motion event for FD#" + << fd_ << ".\n"; + + return -1; + } + } + + // + // Encode opcode and difference between + // current sequence and the last one. + // + + encodeBuffer.encodeOpcodeValue(inputOpcode, serverCache_ -> opcodeCache); + + unsigned int sequenceDiff = inputSequence - serverSequence_; + + serverSequence_ = inputSequence; + + #ifdef DEBUG + *logofs << "handleRead: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sequenceDiff, 16, + serverCache_ -> eventSequenceCache, 7); + + // + // If differential compression is disabled + // then use the most simple encoding. + // + + if (control -> LocalDeltaCompression == 0) + { + int _result = handleFastReadEvent(encodeBuffer, inputOpcode, + inputMessage, inputLength); + if (_result < 0) + { + return -1; + } + else if (_result > 0) + { + continue; + } + } + + switch (inputOpcode) + { + case X_Error: + { + // + // Set the priority flag in the case of + // a X protocol error. This may restart + // the client if it was waiting for the + // reply. + // + + priority_++; + + unsigned char errorCode = *(inputMessage + 1); + + encodeBuffer.encodeCachedValue(errorCode, 8, + serverCache_ -> errorCodeCache); + + if (errorCode != 11 && errorCode != 8 && + errorCode != 15 && errorCode != 1) + { + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 16); + } + + if (errorCode >= 18) + { + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, + serverCache_ -> errorMinorCache); + } + + encodeBuffer.encodeCachedValue(inputMessage[10], 8, + serverCache_ -> errorMajorCache); + + if (errorCode >= 18) + { + const unsigned char *nextSrc = inputMessage + 11; + for (unsigned int i = 11; i < 32; i++) + encodeBuffer.encodeValue(*nextSrc++, 8); + } + } + break; + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + case MotionNotify: + case EnterNotify: + case LeaveNotify: + { + // + // Set the priority in the case this is + // an event that the remote side may + // care to receive as soon as possible. + // + + switch (inputOpcode) + { + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + { + priority_++; + } + } + + unsigned char detail = inputMessage[1]; + if (*inputMessage == MotionNotify) + encodeBuffer.encodeBoolValue((unsigned int) detail); + else if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) detail, 3); + else if (*inputMessage == KeyRelease) + { + if (detail == serverCache_ -> keyPressLastKey) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue((unsigned int) detail, 8); + } + } + else if ((*inputMessage == ButtonPress) || (*inputMessage == ButtonRelease)) + encodeBuffer.encodeCachedValue(detail, 8, + serverCache_ -> buttonCache); + else + encodeBuffer.encodeValue((unsigned int) detail, 8); + unsigned int _timestamp = GetULONG(inputMessage + 4, bigEndian_); + unsigned int timestampDiff = + _timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = _timestamp; + encodeBuffer.encodeCachedValue(timestampDiff, 32, + serverCache_ -> motionNotifyTimestampCache, 9); + int skipRest = 0; + if (*inputMessage == KeyRelease) + { + skipRest = 1; + for (unsigned int i = 8; i < 31; i++) + { + if (inputMessage[i] != serverCache_ -> keyPressCache[i - 8]) + { + skipRest = 0; + break; + } + } + encodeBuffer.encodeBoolValue(skipRest); + } + + if (!skipRest) + { + const unsigned char *nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, + *serverCache_ -> motionNotifyWindowCache[i], 6); + nextSrc += 4; + } + unsigned int rootX = GetUINT(inputMessage + 20, bigEndian_); + unsigned int rootY = GetUINT(inputMessage + 22, bigEndian_); + unsigned int eventX = GetUINT(inputMessage + 24, bigEndian_); + unsigned int eventY = GetUINT(inputMessage + 26, bigEndian_); + eventX -= rootX; + eventY -= rootY; + encodeBuffer.encodeCachedValue(rootX - + serverCache_ -> motionNotifyLastRootX, 16, + serverCache_ -> motionNotifyRootXCache, 6); + serverCache_ -> motionNotifyLastRootX = rootX; + encodeBuffer.encodeCachedValue(rootY - + serverCache_ -> motionNotifyLastRootY, 16, + serverCache_ -> motionNotifyRootYCache, 6); + serverCache_ -> motionNotifyLastRootY = rootY; + encodeBuffer.encodeCachedValue(eventX, 16, + serverCache_ -> motionNotifyEventXCache, 6); + encodeBuffer.encodeCachedValue(eventY, 16, + serverCache_ -> motionNotifyEventYCache, 6); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 28, bigEndian_), + 16, serverCache_ -> motionNotifyStateCache); + if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) inputMessage[30], 2); + else + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[30]); + if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) inputMessage[31], 2); + else if (*inputMessage == KeyPress) + { + serverCache_ -> keyPressLastKey = detail; + for (unsigned int i = 8; i < 31; i++) + { + serverCache_ -> keyPressCache[i - 8] = inputMessage[i]; + } + } + } + } + break; + case ColormapNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> colormapNotifyWindowCache, 8); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> colormapNotifyColormapCache, 8); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]); + } + break; + case ConfigureNotify: + { + const unsigned char *nextSrc = inputMessage + 4; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, + *serverCache_ -> configureNotifyWindowCache[i], 9); + nextSrc += 4; + } + for (unsigned int j = 0; j < 5; j++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *serverCache_ -> configureNotifyGeomCache[j], 8); + nextSrc += 2; + } + encodeBuffer.encodeBoolValue(*nextSrc); + } + break; + case CreateNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> createNotifyWindowCache, 9); + unsigned int window = GetULONG(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(window - + serverCache_ -> createNotifyLastWindow, 29, 5); + serverCache_ -> createNotifyLastWindow = window; + const unsigned char* nextSrc = inputMessage + 12; + for (unsigned int i = 0; i < 5; i++) + { + encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16, 9); + nextSrc += 2; + } + encodeBuffer.encodeBoolValue(*nextSrc); + } + break; + case Expose: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, + serverCache_ -> exposeWindowCache, 9); + const unsigned char *nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 5; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *serverCache_ -> exposeGeomCache[i], 6); + nextSrc += 2; + } + } + break; + case FocusIn: + case FocusOut: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 3); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> focusInWindowCache, 9); + encodeBuffer.encodeValue((unsigned int) inputMessage[8], 2); + } + break; + case KeymapNotify: + { + if (ServerCache::lastKeymap.compare(31, inputMessage + 1)) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + const unsigned char *nextSrc = inputMessage + 1; + for (unsigned int i = 1; i < 32; i++) + encodeBuffer.encodeValue((unsigned int) *nextSrc++, 8); + } + } + break; + case MapNotify: + case UnmapNotify: + case DestroyNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> mapNotifyEventCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> mapNotifyWindowCache, 9); + if ((*inputMessage == MapNotify) || (*inputMessage == UnmapNotify)) + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]); + } + break; + case NoExpose: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> noExposeDrawableCache, 9); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, + serverCache_ -> noExposeMinorCache); + encodeBuffer.encodeCachedValue(inputMessage[10], 8, + serverCache_ -> noExposeMajorCache); + } + break; + case PropertyNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> propertyNotifyWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> propertyNotifyAtomCache, 9); + unsigned int _timestamp = GetULONG(inputMessage + 12, bigEndian_); + unsigned int timestampDiff = + _timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = _timestamp; + encodeBuffer.encodeValue(timestampDiff, 32, 9); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[16]); + } + break; + case ReparentNotify: + { + const unsigned char* nextSrc = inputMessage + 4; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), + 29, serverCache_ -> reparentNotifyWindowCache, 9); + nextSrc += 4; + } + encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16, 6); + encodeBuffer.encodeValue(GetUINT(nextSrc + 2, bigEndian_), 16, 6); + encodeBuffer.encodeBoolValue((unsigned int)inputMessage[20]); + } + break; + case SelectionClear: + { + unsigned int _timestamp = GetULONG(inputMessage + 4, bigEndian_); + unsigned int timestampDiff = _timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = _timestamp; + encodeBuffer.encodeValue(timestampDiff, 32, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> selectionClearWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + } + break; + case SelectionRequest: + { + unsigned int _timestamp = GetULONG(inputMessage + 4, bigEndian_); + unsigned int timestampDiff = _timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = _timestamp; + encodeBuffer.encodeValue(timestampDiff, 32, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> selectionClearWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), + 29, serverCache_ -> selectionClearWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 20, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 24, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + } + break; + case VisibilityNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> visibilityNotifyWindowCache, 9); + encodeBuffer.encodeValue((unsigned int) inputMessage[8], 2); + } + break; + default: + { + #ifdef TEST + *logofs << "handleRead: Using generic event compression " + << "for OPCODE#" << (unsigned int) inputOpcode + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8, + serverCache_ -> genericEventCharCache); + + for (unsigned int i = 0; i < 14; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + i * 2 + 4, bigEndian_), + 16, *serverCache_ -> genericEventIntCache[i]); + } + } + + } // switch (inputOpcode)... + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + if (*inputMessage == X_Error) + { + unsigned char code = *(inputMessage + 1); + + *logofs << "handleRead: Handled error ERR_CODE#" + << (unsigned int) code << " for FD#" << fd_; + + *logofs << " RES_ID#" << GetULONG(inputMessage + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(inputMessage + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(inputMessage + 10); + + *logofs << " sequence " << inputSequence << ". " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + else + { + *logofs << "handleRead: Handled event OPCODE#" + << (unsigned int) *inputMessage << " for FD#" << fd_ + << " sequence " << inputSequence << ". " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + + #endif + + statistics -> addEventBits(*inputMessage, inputLength << 3, bits); + + } // End of if (inputMessage[0] == X_Reply) ... else ... + + } // End of if (firstReply_) ... else ... + + } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != 0) ... + + // + // Check if we need to flush because of + // prioritized data. + // + + if (priority_ > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: WARNING! Requesting flush " + << "because of " << priority_ << " prioritized " + << "messages for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncPriority() < 0) + { + return -1; + } + + // + // Reset the priority flag. + // + + priority_ = 0; + } + + // + // Flush if we produced enough data. + // + + if (proxy -> canAsyncFlush() == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: WARNING! Requesting flush " + << "because of token length exceeded.\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + } + + #if defined(TEST) || defined(INFO) + + if (transport_ -> pending() != 0 || + readBuffer_.checkMessage() != 0) + { + *logofs << "handleRead: PANIC! Buffer for X descriptor FD#" + << fd_ << " has " << transport_ -> pending() + << " bytes to read.\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // Reset the read buffer. + // + + readBuffer_.fullReset(); + + return 1; +} + +// +// End of handleRead(). +// + +// +// Beginning of handleWrite(). +// + +int ServerChannel::handleWrite(const unsigned char *message, unsigned int length) +{ + #ifdef TEST + *logofs << "handleWrite: Called for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Create the buffer from which to + // decode messages. + // + + DecodeBuffer decodeBuffer(message, length); + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "handleWrite: Decoding messages for FD#" << fd_ + << " with " << length << " bytes in the buffer.\n" + << logofs_flush; + #endif + + if (firstRequest_) + { + // + // Need to add the length of the first request + // because it was not present in the previous + // versions. Length of the first request was + // assumed to be the same as the encode buffer + // but this may be not the case if a different + // encoding is used. + // + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeValue(length, 8); + + unsigned int nextByte; + unsigned char *outputMessage = writeBuffer_.addMessage(length); + unsigned char *nextDest = outputMessage; + + for (unsigned int i = 0; i < length; i++) + { + decodeBuffer.decodeValue(nextByte, 8); + + *nextDest++ = (unsigned char) nextByte; + } + + if (*outputMessage == 0x42) + { + setBigEndian(1); + } + else + { + setBigEndian(0); + } + + #ifdef TEST + *logofs << "handleWrite: First request detected.\n" << logofs_flush; + #endif + + // + // Handle the fake authorization cookie. + // + + if (handleAuthorization(outputMessage) < 0) + { + return -1; + } + + firstRequest_ = 0; + + } // End of if (firstRequest_) + + // + // This was previously in a 'else' block. + // Due to the way the first request was + // handled, we could not decode multiple + // messages in the first frame. + // + + { // Start of the decoding block. + + unsigned char outputOpcode; + + unsigned char *outputMessage; + unsigned int outputLength; + + // + // Set when message is found in cache. + // + + int hit; + + while (decodeBuffer.decodeOpcodeValue(outputOpcode, clientCache_ -> opcodeCache, 1)) + { + hit = 0; + + // + // Splits are sent by client proxy outside the + // normal read loop. As we 'insert' splits in + // the real client-server X protocol, we must + // avoid to increment the sequence number or + // our clients would get confused. + // + + if (outputOpcode != opcodeStore_ -> splitData) + { + clientSequence_++; + clientSequence_ &= 0xffff; + + #ifdef DEBUG + *logofs << "handleWrite: Last client sequence number for FD#" + << fd_ << " is " << clientSequence_ << ".\n" + << logofs_flush; + #endif + } + else + { + // + // It's a split, not a normal + // burst of proxy data. + // + + handleSplit(decodeBuffer); + + continue; + } + + #ifdef SUSPEND + + if (clientSequence_ == 1000) + { + cerr << "Warning" << ": Exiting to test the resilience of the agent.\n"; + + sleep(2); + + HandleAbort(); + } + + #endif + + // + // Is differential encoding disabled? + // + + if (control -> RemoteDeltaCompression == 0) + { + int result = handleFastWriteRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + // + // General-purpose temp variables for + // decoding ints and chars. + // + + unsigned int value; + unsigned char cValue; + + #ifdef DEBUG + *logofs << "handleWrite: Going to handle request OPCODE#" + << (unsigned int) outputOpcode << " (" << DumpOpcode(outputOpcode) + << ") for FD#" << fd_ << " sequence " << clientSequence_ + << ".\n" << logofs_flush; + #endif + + switch (outputOpcode) + { + case X_AllocColor: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + unsigned int colorData[3]; + + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *(clientCache_ -> allocColorRGBCache[i]), 4); + PutUINT(value, nextDest, bigEndian_); + colorData[i] = value; + nextDest += 2; + } + + sequenceQueue_.push(clientSequence_, outputOpcode, + colorData[0], colorData[1], colorData[2]); + } + break; + case X_ReparentWindow: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 16, 11); + PutUINT(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeValue(value, 16, 11); + PutUINT(value, outputMessage + 14, bigEndian_); + } + break; + case X_ChangeProperty: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ChangeProperty); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned char format; + decodeBuffer.decodeCachedValue(format, 8, + clientCache_ -> changePropertyFormatCache); + unsigned int dataLength; + decodeBuffer.decodeValue(dataLength, 32, 6); + outputLength = 24 + RoundUp4(dataLength * (format >> 3)); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> changePropertyPropertyCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> changePropertyTypeCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + outputMessage[16] = format; + PutULONG(dataLength, outputMessage + 20, bigEndian_); + unsigned char *nextDest = outputMessage + 24; + + if (format == 8) + { + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, dataLength); + } + else if (format == 32) + { + for (unsigned int i = 0; i < dataLength; i++) + { + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> changePropertyData32Cache); + + PutULONG(value, nextDest, bigEndian_); + + nextDest += 4; + } + } + else + { + for (unsigned int i = 0; i < dataLength; i++) + { + decodeBuffer.decodeValue(value, 16); + + PutUINT(value, nextDest, bigEndian_); + + nextDest += 2; + } + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_SendEvent: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_SendEvent); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 44; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + *(outputMessage + 1) = value; + decodeBuffer.decodeBoolValue(value); + if (value) + { + decodeBuffer.decodeBoolValue(value); + } + else + { + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + } + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> sendEventMaskCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(*(outputMessage + 12), 8, + clientCache_ -> sendEventCodeCache); + decodeBuffer.decodeCachedValue(*(outputMessage + 13), 8, + clientCache_ -> sendEventByteDataCache); + decodeBuffer.decodeValue(value, 16, 4); + clientCache_ -> sendEventLastSequence += value; + clientCache_ -> sendEventLastSequence &= 0xffff; + PutUINT(clientCache_ -> sendEventLastSequence, outputMessage + 14, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> sendEventIntDataCache); + PutULONG(value, outputMessage + 16, bigEndian_); + + for (unsigned int i = 20; i < 44; i++) + { + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> sendEventEventCache); + *(outputMessage + i) = cValue; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ChangeWindowAttributes: + { + unsigned int numAttrs; + decodeBuffer.decodeValue(numAttrs, 4); + outputLength = 12 + (numAttrs << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 15, + clientCache_ -> createWindowBitmaskCache); + PutULONG(bitmask, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 15; i++) + { + if (bitmask & mask) + { + decodeBuffer.decodeCachedValue(value, 32, + *clientCache_ -> createWindowAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + mask <<= 1; + } + } + break; + case X_ClearArea: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ClearArea); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + for (unsigned int i = 0; i < 4; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> clearAreaGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_CloseFont: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 29, 5); + clientCache_ -> lastFont += value; + clientCache_ -> lastFont &= 0x1fffffff; + PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_); + } + break; + case X_ConfigureWindow: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ConfigureWindow); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 7, + clientCache_ -> configureWindowBitmaskCache); + PutUINT(bitmask, outputMessage + 8, bigEndian_); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 7; i++) + { + if (bitmask & mask) + { + unsigned char* nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + decodeBuffer.decodeCachedValue(value, CONFIGUREWINDOW_FIELD_WIDTH[i], + *clientCache_ -> configureWindowAttrCache[i], 8); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ConvertSelection: + { + outputLength = 24; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> convertSelectionRequestorCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char* nextDest = outputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeCachedValue(value, 29, + *(clientCache_ -> convertSelectionAtomCache[i]), 9); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + decodeBuffer.decodeValue(value, 32, 4); + clientCache_ -> convertSelectionLastTimestamp += value; + PutULONG(clientCache_ -> convertSelectionLastTimestamp, + nextDest, bigEndian_); + } + break; + case X_CopyArea: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CopyArea); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 28; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 12, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + for (unsigned int i = 0; i < 6; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> copyAreaGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_CopyGC: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 23, + clientCache_ -> createGCBitmaskCache); + PutULONG(value, outputMessage + 12, bigEndian_); + } + break; + case X_CopyPlane: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 12, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + for (unsigned int i = 0; i < 6; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> copyPlaneGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> copyPlaneBitPlaneCache, 10); + PutULONG(value, outputMessage + 28, bigEndian_); + } + break; + case X_CreateGC: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CreateGC); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId, + clientCache_ -> lastIdCache, clientCache_ -> gcCache, + clientCache_ -> freeGCCache); + + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int offset = 8; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + offset, bigEndian_); + offset += 4; + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 23, + clientCache_ -> createGCBitmaskCache); + PutULONG(bitmask, outputMessage + offset, bigEndian_); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 23; i++) + { + if (bitmask & mask) + { + unsigned char* nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i]; + if (fieldWidth <= 4) + decodeBuffer.decodeValue(value, fieldWidth); + else + decodeBuffer.decodeCachedValue(value, fieldWidth, + *clientCache_ -> createGCAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ChangeGC: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ChangeGC); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int offset = 8; + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 23, + clientCache_ -> createGCBitmaskCache); + PutULONG(bitmask, outputMessage + offset, bigEndian_); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 23; i++) + { + if (bitmask & mask) + { + unsigned char* nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i]; + if (fieldWidth <= 4) + decodeBuffer.decodeValue(value, fieldWidth); + else + decodeBuffer.decodeCachedValue(value, fieldWidth, + *clientCache_ -> createGCAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_CreatePixmap: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CreatePixmap); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + break; + case X_CreateWindow: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeCachedValue(cValue, 8, clientCache_ -> depthCache); + outputMessage[1] = cValue; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId, + clientCache_ -> lastIdCache, clientCache_ -> windowCache, + clientCache_ -> freeWindowCache); + + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + unsigned int i; + for (i = 0; i < 6; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> createWindowGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> visualCache); + PutULONG(value, outputMessage + 24, bigEndian_); + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 15, + clientCache_ -> createWindowBitmaskCache); + PutULONG(bitmask, outputMessage + 28, bigEndian_); + unsigned int mask = 0x1; + for (i = 0; i < 15; i++) + { + if (bitmask & mask) + { + nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + decodeBuffer.decodeCachedValue(value, 32, + *clientCache_ -> createWindowAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + } + break; + case X_DeleteProperty: + { + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 29, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + } + break; + case X_FillPoly: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_FillPoly); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numPoints; + + // Since ProtoStep10 (#issue 108) + decodeBuffer.decodeCachedValue(numPoints, 16, + clientCache_ -> fillPolyNumPointsCache, 4); + + outputLength = 16 + (numPoints << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 2); + outputMessage[12] = (unsigned char) value; + unsigned int relativeCoordMode; + decodeBuffer.decodeBoolValue(relativeCoordMode); + outputMessage[13] = (unsigned char) relativeCoordMode; + unsigned char *nextDest = outputMessage + 16; + unsigned int pointIndex = 0; + for (unsigned int i = 0; i < numPoints; i++) + { + if (relativeCoordMode) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> fillPolyXRelCache[pointIndex], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> fillPolyYRelCache[pointIndex], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + else + { + unsigned int x, y; + decodeBuffer.decodeBoolValue(value); + if (value) + { + decodeBuffer.decodeValue(value, 3); + x = clientCache_ -> fillPolyRecentX[value]; + y = clientCache_ -> fillPolyRecentY[value]; + } + else + { + decodeBuffer.decodeCachedValue(x, 16, + *clientCache_ -> fillPolyXAbsCache[pointIndex], 8); + decodeBuffer.decodeCachedValue(y, 16, + *clientCache_ -> fillPolyYAbsCache[pointIndex], 8); + clientCache_ -> fillPolyRecentX[clientCache_ -> fillPolyIndex] = x; + clientCache_ -> fillPolyRecentY[clientCache_ -> fillPolyIndex] = y; + clientCache_ -> fillPolyIndex++; + if (clientCache_ -> fillPolyIndex == 8) + clientCache_ -> fillPolyIndex = 0; + } + PutUINT(x, nextDest, bigEndian_); + nextDest += 2; + PutUINT(y, nextDest, bigEndian_); + nextDest += 2; + } + + if (++pointIndex == 10) pointIndex = 0; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_FreeColors: + { + unsigned int numPixels; + decodeBuffer.decodeValue(numPixels, 16, 4); + outputLength = 12 + (numPixels << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 32, 4); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char* nextDest = outputMessage + 12; + while (numPixels) + { + decodeBuffer.decodeValue(value, 32, 8); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + numPixels--; + } + } + break; + case X_FreeCursor: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> cursorCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + } + break; + case X_FreeGC: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeGCCache); + + PutULONG(value, outputMessage + 4, bigEndian_); + } + break; + case X_FreePixmap: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeDrawableCache); + + PutULONG(value, outputMessage + 4, bigEndian_); + } + break; + case X_GetAtomName: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 29, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetGeometry: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetInputFocus: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode, outputOpcode); + } + break; + case X_GetModifierMapping: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetKeyboardMapping: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 8); + outputMessage[4] = value; + decodeBuffer.decodeValue(value, 8); + outputMessage[5] = value; + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetProperty: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_GetProperty); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + // + // Save a reference to identify the reply. + // + + unsigned int property = GetULONG(outputMessage + 8, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode, property); + + break; + } + + outputLength = 24; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int property; + decodeBuffer.decodeValue(property, 29, 9); + PutULONG(property, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 29, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeValue(value, 32, 2); + PutULONG(value, outputMessage + 16, bigEndian_); + decodeBuffer.decodeValue(value, 32, 8); + PutULONG(value, outputMessage + 20, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode, property); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_GetSelectionOwner: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> getSelectionOwnerSelectionCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GrabButton: + case X_GrabPointer: + { + outputLength = 24; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> grabButtonEventMaskCache); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[10] = (unsigned char) value; + decodeBuffer.decodeBoolValue(value); + outputMessage[11] = (unsigned char) value; + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> grabButtonConfineCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> cursorCache, 9); + PutULONG(value, outputMessage + 16, bigEndian_); + if (outputOpcode == X_GrabButton) + { + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> grabButtonButtonCache); + outputMessage[20] = cValue; + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> grabButtonModifierCache); + PutUINT(value, outputMessage + 22, bigEndian_); + } + else + { + decodeBuffer.decodeValue(value, 32, 4); + clientCache_ -> grabKeyboardLastTimestamp += value; + PutULONG(clientCache_ -> grabKeyboardLastTimestamp, + outputMessage + 20, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + } + break; + case X_GrabKeyboard: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 32, 4); + clientCache_ -> grabKeyboardLastTimestamp += value; + PutULONG(clientCache_ -> grabKeyboardLastTimestamp, outputMessage + 8, + bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[12] = (unsigned char) value; + decodeBuffer.decodeBoolValue(value); + outputMessage[13] = (unsigned char) value; + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GrabServer: + case X_UngrabServer: + case X_NoOperation: + { + #ifdef DEBUG + *logofs << "handleWrite: Managing (probably tainted) X_NoOperation request for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + } + break; + case X_PolyText8: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyText8); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheX); + clientCache_ -> polyTextLastX += value; + clientCache_ -> polyTextLastX &= 0xffff; + PutUINT(clientCache_ -> polyTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheY); + clientCache_ -> polyTextLastY += value; + clientCache_ -> polyTextLastY &= 0xffff; + PutUINT(clientCache_ -> polyTextLastY, outputMessage + 14, bigEndian_); + unsigned int addedLength = 0; + writeBuffer_.registerPointer(&outputMessage); + for (;;) + { + decodeBuffer.decodeBoolValue(value); + if (!value) + break; + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 8); + if (textLength == 255) + { + addedLength += 5; + unsigned char *nextSegment = writeBuffer_.addMessage(5); + *nextSegment = (unsigned char) textLength; + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> polyTextFontCache); + PutULONG(value, nextSegment + 1, 1); + } + else + { + addedLength += (textLength + 2); + unsigned char *nextSegment = + writeBuffer_.addMessage(textLength + 2); + *nextSegment = (unsigned char) textLength; + unsigned char *nextDest = nextSegment + 1; + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> polyTextDeltaCache); + *nextDest++ = cValue; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength); + + nextDest += textLength; + } + } + outputLength += addedLength; + unsigned int mod4 = (addedLength & 0x3); + if (mod4) + { + unsigned int extra = 4 - mod4; + unsigned char *nextDest = writeBuffer_.addMessage(extra); + for (unsigned int i = 0; i < extra; i++) + *nextDest++ = 0; + outputLength += extra; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyText16: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyText16); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheX); + clientCache_ -> polyTextLastX += value; + clientCache_ -> polyTextLastX &= 0xffff; + PutUINT(clientCache_ -> polyTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheY); + clientCache_ -> polyTextLastY += value; + clientCache_ -> polyTextLastY &= 0xffff; + PutUINT(clientCache_ -> polyTextLastY, outputMessage + 14, bigEndian_); + unsigned int addedLength = 0; + writeBuffer_.registerPointer(&outputMessage); + for (;;) + { + decodeBuffer.decodeBoolValue(value); + if (!value) + break; + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 8); + if (textLength == 255) + { + addedLength += 5; + unsigned char *nextSegment = writeBuffer_.addMessage(5); + *nextSegment = (unsigned char) textLength; + decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> polyTextFontCache); + PutULONG(value, nextSegment + 1, 1); + } + else + { + addedLength += (textLength * 2 + 2); + unsigned char *nextSegment = + writeBuffer_.addMessage(textLength * 2 + 2); + *nextSegment = (unsigned char) textLength; + unsigned char *nextDest = nextSegment + 1; + decodeBuffer.decodeCachedValue(cValue, 8, clientCache_ -> polyTextDeltaCache); + *nextDest++ = cValue; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength * 2); + + nextDest += textLength * 2; + } + } + outputLength += addedLength; + + unsigned int mod4 = (addedLength & 0x3); + if (mod4) + { + unsigned int extra = 4 - mod4; + unsigned char *nextDest = writeBuffer_.addMessage(extra); + for (unsigned int i = 0; i < extra; i++) + *nextDest++ = 0; + outputLength += extra; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ImageText8: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ImageText8); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int textLength; + decodeBuffer.decodeCachedValue(textLength, 8, + clientCache_ -> imageTextLengthCache, 4); + outputLength = 16 + RoundUp4(textLength); + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) textLength; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheX); + clientCache_ -> imageTextLastX += value; + clientCache_ -> imageTextLastX &= 0xffff; + PutUINT(clientCache_ -> imageTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheY); + clientCache_ -> imageTextLastY += value; + clientCache_ -> imageTextLastY &= 0xffff; + PutUINT(clientCache_ -> imageTextLastY, outputMessage + 14, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ImageText16: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ImageText16); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int textLength; + decodeBuffer.decodeCachedValue(textLength, 8, + clientCache_ -> imageTextLengthCache, 4); + outputLength = 16 + RoundUp4(textLength * 2); + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) textLength; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheX); + clientCache_ -> imageTextLastX += value; + clientCache_ -> imageTextLastX &= 0xffff; + PutUINT(clientCache_ -> imageTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheY); + clientCache_ -> imageTextLastY += value; + clientCache_ -> imageTextLastY &= 0xffff; + PutUINT(clientCache_ -> imageTextLastY, outputMessage + 14, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength * 2); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_InternAtom: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_InternAtom); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + + break; + } + + unsigned int nameLength; + decodeBuffer.decodeValue(nameLength, 16, 6); + outputLength = RoundUp4(nameLength) + 8; + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(nameLength, outputMessage + 4, bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + unsigned char *nextDest = outputMessage + 8; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, nameLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ListExtensions: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_ListFonts: + { + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 16, 6); + outputLength = 8 + RoundUp4(textLength); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(textLength, outputMessage + 6, bigEndian_); + decodeBuffer.decodeValue(value, 16, 6); + PutUINT(value, outputMessage + 4, bigEndian_); + unsigned char* nextDest = outputMessage + 8; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_LookupColor: + case X_AllocNamedColor: + { + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 16, 6); + outputLength = 12 + RoundUp4(textLength); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + PutUINT(textLength, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, textLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_MapWindow: + case X_UnmapWindow: + case X_MapSubwindows: + case X_GetWindowAttributes: + case X_DestroyWindow: + case X_DestroySubwindows: + case X_QueryPointer: + case X_QueryTree: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + + if (outputOpcode == X_DestroyWindow) // Since ProtoStep7 (#issue 108) + { + decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeWindowCache); + } + else + { + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + } + + PutULONG(value, outputMessage + 4, bigEndian_); + if (outputOpcode == X_QueryPointer || + outputOpcode == X_GetWindowAttributes || + outputOpcode == X_QueryTree) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + } + } + break; + case X_OpenFont: + { + unsigned int nameLength; + decodeBuffer.decodeValue(nameLength, 16, 7); + outputLength = RoundUp4(12 + nameLength); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(nameLength, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 29, 5); + clientCache_ -> lastFont += value; + clientCache_ -> lastFont &= 0x1fffffff; + PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeTextData(nextDest, nameLength); + } + break; + case X_PolyFillRectangle: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyFillRectangle); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0, lastWidth = 0, lastHeight = 0; + unsigned int numRectangles = 0; + + for (;;) + { + outputLength += 8; + writeBuffer_.addMessage(8); + unsigned char *nextDest = outputMessage + 12 + + (numRectangles << 3); + numRectangles++; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheX[index], 8); + value += lastX; + PutUINT(value, nextDest, bigEndian_); + lastX = value; + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheY[index], 8); + value += lastY; + PutUINT(value, nextDest, bigEndian_); + lastY = value; + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheWidth[index], 8); + value += lastWidth; + PutUINT(value, nextDest, bigEndian_); + lastWidth = value; + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheHeight[index], 8); + value += lastHeight; + PutUINT(value, nextDest, bigEndian_); + lastHeight = value; + nextDest += 2; + + if (++index == 4) index = 0; + + decodeBuffer.decodeBoolValue(value); + + if (!value) break; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyFillArc: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyFillArc); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0, + lastWidth = 0, lastHeight = 0, + lastAngle1 = 0, lastAngle2 = 0; + + unsigned int numArcs = 0; + + for (;;) + { + outputLength += 12; + writeBuffer_.addMessage(12); + + unsigned char *nextDest = outputMessage + 12 + + (numArcs * 12); + numArcs++; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheX[index], 8); + value += lastX; + PutUINT(value, nextDest, bigEndian_); + lastX = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheY[index], 8); + value += lastY; + PutUINT(value, nextDest, bigEndian_); + lastY = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheWidth[index], 8); + value += lastWidth; + PutUINT(value, nextDest, bigEndian_); + lastWidth = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheHeight[index], 8); + value += lastHeight; + PutUINT(value, nextDest, bigEndian_); + lastHeight = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheAngle1[index], 8); + value += lastAngle1; + PutUINT(value, nextDest, bigEndian_); + lastAngle1 = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheAngle2[index], 8); + value += lastAngle2; + PutUINT(value, nextDest, bigEndian_); + lastAngle2 = value; + nextDest += 2; + + if (++index == 2) index = 0; + + decodeBuffer.decodeBoolValue(value); + + if (!value) break; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyArc: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyArc); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0, + lastWidth = 0, lastHeight = 0, + lastAngle1 = 0, lastAngle2 = 0; + + unsigned int numArcs = 0; + + for (;;) + { + outputLength += 12; + writeBuffer_.addMessage(12); + + unsigned char *nextDest = outputMessage + 12 + + (numArcs * 12); + numArcs++; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheX[index], 8); + value += lastX; + PutUINT(value, nextDest, bigEndian_); + lastX = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheY[index], 8); + value += lastY; + PutUINT(value, nextDest, bigEndian_); + lastY = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheWidth[index], 8); + value += lastWidth; + PutUINT(value, nextDest, bigEndian_); + lastWidth = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheHeight[index], 8); + value += lastHeight; + PutUINT(value, nextDest, bigEndian_); + lastHeight = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheAngle1[index], 8); + value += lastAngle1; + PutUINT(value, nextDest, bigEndian_); + lastAngle1 = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheAngle2[index], 8); + value += lastAngle2; + PutUINT(value, nextDest, bigEndian_); + lastAngle2 = value; + nextDest += 2; + + if (++index == 2) index = 0; + + decodeBuffer.decodeBoolValue(value); + + if (!value) break; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyPoint: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyPoint); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numPoints; + decodeBuffer.decodeValue(numPoints, 16, 4); + outputLength = (numPoints << 2) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + unsigned int relativeCoordMode; + decodeBuffer.decodeBoolValue(relativeCoordMode); + outputMessage[1] = (unsigned char) relativeCoordMode; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + + for (unsigned int i = 0; i < numPoints; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyPointCacheX[index], 8); + lastX += value; + PutUINT(lastX, nextDest, bigEndian_); + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyPointCacheY[index], 8); + lastY += value; + PutUINT(lastY, nextDest, bigEndian_); + nextDest += 2; + + if (++index == 2) index = 0; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyLine: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyLine); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numPoints; + decodeBuffer.decodeValue(numPoints, 16, 4); + outputLength = (numPoints << 2) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + unsigned int relativeCoordMode; + decodeBuffer.decodeBoolValue(relativeCoordMode); + outputMessage[1] = (unsigned char) relativeCoordMode; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + + for (unsigned int i = 0; i < numPoints; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyLineCacheX[index], 8); + lastX += value; + PutUINT(lastX, nextDest, bigEndian_); + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyLineCacheY[index], 8); + lastY += value; + PutUINT(lastY, nextDest, bigEndian_); + nextDest += 2; + + if (++index == 2) index = 0; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyRectangle: + { + unsigned int numRectangles; + decodeBuffer.decodeValue(numRectangles, 16, 3); + outputLength = (numRectangles << 3) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + for (unsigned int i = 0; i < numRectangles; i++) + for (unsigned int k = 0; k < 4; k++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyRectangleGeomCache[k], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + } + break; + case X_PolySegment: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolySegment); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numSegments; + decodeBuffer.decodeValue(numSegments, 16, 4); + outputLength = (numSegments << 3) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + for (numSegments *= 2; numSegments; numSegments--) + { + unsigned int index; + decodeBuffer.decodeBoolValue(index); + unsigned int x; + decodeBuffer.decodeCachedValue(x, 16, + clientCache_ -> polySegmentCacheX, 6); + x += clientCache_ -> polySegmentLastX[index]; + PutUINT(x, nextDest, bigEndian_); + nextDest += 2; + + unsigned int y; + decodeBuffer.decodeCachedValue(y, 16, + clientCache_ -> polySegmentCacheY, 6); + y += clientCache_ -> polySegmentLastY[index]; + PutUINT(y, nextDest, bigEndian_); + nextDest += 2; + + clientCache_ -> polySegmentLastX[clientCache_ -> polySegmentCacheIndex] = x; + clientCache_ -> polySegmentLastY[clientCache_ -> polySegmentCacheIndex] = y; + + if (clientCache_ -> polySegmentCacheIndex == 1) + clientCache_ -> polySegmentCacheIndex = 0; + else + clientCache_ -> polySegmentCacheIndex = 1; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PutImage: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PutImage); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + + if (outputOpcode == X_PutImage) + { + handleImage(outputOpcode, outputMessage, outputLength); + } + } + break; + case X_QueryBestSize: + { + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char)value; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 16, 8); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 16, 8); + PutUINT(value, outputMessage + 10, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_QueryColors: + { + // Differential or plain data compression? + decodeBuffer.decodeBoolValue(value); + + if (value) + { + unsigned int numColors; + decodeBuffer.decodeValue(numColors, 16, 5); + outputLength = (numColors << 2) + 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + unsigned int predictedPixel = clientCache_ -> queryColorsLastPixel; + for (unsigned int i = 0; i < numColors; i++) + { + unsigned int pixel; + decodeBuffer.decodeBoolValue(value); + if (value) + pixel = predictedPixel; + else + decodeBuffer.decodeValue(pixel, 32, 9); + PutULONG(pixel, nextDest, bigEndian_); + if (i == 0) + clientCache_ -> queryColorsLastPixel = pixel; + predictedPixel = pixel + 1; + nextDest += 4; + } + } + else + { + // Request length. + unsigned int requestLength; + decodeBuffer.decodeValue(requestLength, 16, 10); + outputLength = (requestLength << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + + const unsigned char *compressedData = NULL; + unsigned int compressedDataSize = 0; + + int decompressed = handleDecompress(decodeBuffer, outputOpcode, 4, + outputMessage, outputLength, compressedData, + compressedDataSize); + if (decompressed < 0) + { + return -1; + } + } + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_QueryExtension: + { + unsigned int nameLength; + decodeBuffer.decodeValue(nameLength, 16, 6); + outputLength = 8 + RoundUp4(nameLength); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(nameLength, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + for (unsigned int i = 0; i < nameLength; i++) + { + decodeBuffer.decodeValue(value, 8); + *nextDest++ = (unsigned char) value; + } + + unsigned int hide = 0; + + #ifdef HIDE_MIT_SHM_EXTENSION + + if (!strncmp((char *) outputMessage + 8, "MIT-SHM", 7)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide MIT-SHM extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + #ifdef HIDE_BIG_REQUESTS_EXTENSION + + if (!strncmp((char *) outputMessage + 8, "BIG-REQUESTS", 12)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide BIG-REQUESTS extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + #ifdef HIDE_XKEYBOARD_EXTENSION + + else if (!strncmp((char *) outputMessage + 8, "XKEYBOARD", 9)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide XKEYBOARD extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + #ifdef HIDE_XFree86_Bigfont_EXTENSION + + else if (!strncmp((char *) outputMessage + 8, "XFree86-Bigfont", 15)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide XFree86-Bigfont extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + // + // This is if you want to experiment disabling SHAPE extensions. + // + + #ifdef HIDE_SHAPE_EXTENSION + + if (!strncmp((char *) outputMessage + 8, "SHAPE", 5)) + { + #ifdef DEBUG + *logofs << "handleWrite: Going to hide SHAPE extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + // + // Check if user disabled RENDER extension. + // + + if (control -> HideRender == 1 && + strncmp((char *) outputMessage + 8, "RENDER", 6) == 0) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide RENDER extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + unsigned int extension = 0; + + if (strncmp((char *) outputMessage + 8, "SHAPE", 5) == 0) + { + extension = X_NXInternalShapeExtension; + } + else if (strncmp((char *) outputMessage + 8, "RENDER", 6) == 0) + { + extension = X_NXInternalRenderExtension; + } + + sequenceQueue_.push(clientSequence_, outputOpcode, + outputOpcode, hide, extension); + } + break; + case X_QueryFont: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 29, 5); + clientCache_ -> lastFont += value; + clientCache_ -> lastFont &= 0x1fffffff; + PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_SetClipRectangles: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_SetClipRectangles); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numRectangles; + + // Since ProtoStep9 (#issue 108) + decodeBuffer.decodeValue(numRectangles, 15, 4); + + outputLength = (numRectangles << 3) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> setClipRectanglesXCache, 8); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> setClipRectanglesYCache, 8); + PutUINT(value, outputMessage + 10, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + for (unsigned int i = 0; i < numRectangles; i++) + { + for (unsigned int k = 0; k < 4; k++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> setClipRectanglesGeomCache[k], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_SetDashes: + { + unsigned int numDashes; + decodeBuffer.decodeCachedValue(numDashes, 16, + clientCache_ -> setDashesLengthCache, 5); + outputLength = 12 + RoundUp4(numDashes); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(numDashes, outputMessage + 10, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> setDashesOffsetCache, 5); + PutUINT(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + for (unsigned int i = 0; i < numDashes; i++) + { + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> setDashesDashCache_[i & 1], 5); + *nextDest++ = cValue; + } + } + break; + case X_SetSelectionOwner: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> setSelectionOwnerCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> getSelectionOwnerSelectionCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> setSelectionOwnerTimestampCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + } + break; + case X_TranslateCoords: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_TranslateCoords); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> translateCoordsSrcCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> translateCoordsDstCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> translateCoordsXCache, 8); + PutUINT(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> translateCoordsYCache, 8); + PutUINT(value, outputMessage + 14, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_GetImage: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_GetImage); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + + break; + } + + outputLength = 20; + outputMessage = writeBuffer_.addMessage(outputLength); + // Format. + unsigned int format; + decodeBuffer.decodeValue(format, 2); + outputMessage[1] = (unsigned char) format; + // Drawable. + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + // X. + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> putImageXCache, 8); + clientCache_ -> putImageLastX += value; + clientCache_ -> putImageLastX &= 0xffff; + PutUINT(clientCache_ -> putImageLastX, outputMessage + 8, bigEndian_); + // Y. + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> putImageYCache, 8); + clientCache_ -> putImageLastY += value; + clientCache_ -> putImageLastY &= 0xffff; + PutUINT(clientCache_ -> putImageLastY, outputMessage + 10, bigEndian_); + // Width. + unsigned int width; + decodeBuffer.decodeCachedValue(width, 16, + clientCache_ -> putImageWidthCache, 8); + PutUINT(width, outputMessage + 12, bigEndian_); + // Height. + unsigned int height; + decodeBuffer.decodeCachedValue(height, 16, + clientCache_ -> putImageHeightCache, 8); + PutUINT(height, outputMessage + 14, bigEndian_); + // Plane mask. + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> getImagePlaneMaskCache, 5); + PutULONG(value, outputMessage + 16, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_GetPointerMapping: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetKeyboardControl: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + default: + { + if (outputOpcode == opcodeStore_ -> renderExtension) + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalRenderExtension); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> shapeExtension) + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalShapeExtension); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> putPackedImage) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding packed image request for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXPutPackedImage); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + + if (outputOpcode == opcodeStore_ -> putPackedImage) + { + handleImage(outputOpcode, outputMessage, outputLength); + } + } + else if (outputOpcode == opcodeStore_ -> setUnpackColormap) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding set unpack colormap request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackColormap); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + // + // Message could have been split. + // + + if (outputOpcode == opcodeStore_ -> setUnpackColormap) + { + handleColormap(outputOpcode, outputMessage, outputLength); + } + } + else if (outputOpcode == opcodeStore_ -> setUnpackAlpha) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding unpack alpha request for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackAlpha); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + // + // Message could have been split. + // + + if (outputOpcode == opcodeStore_ -> setUnpackAlpha) + { + handleAlpha(outputOpcode, outputMessage, outputLength); + } + } + else if (outputOpcode == opcodeStore_ -> setUnpackGeometry) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding set unpack geometry request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackGeometry); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + + handleGeometry(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> startSplit) + { + handleStartSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> endSplit) + { + handleEndSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> commitSplit) + { + int result = handleCommitSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + + // + // Check if message has been successfully + // extracted from the split store. In this + // case post-process it in the usual way. + // + + if (result > 0) + { + if (outputOpcode == opcodeStore_ -> putPackedImage || + outputOpcode == X_PutImage) + { + handleImage(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> setUnpackColormap) + { + handleColormap(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> setUnpackAlpha) + { + handleAlpha(outputOpcode, outputMessage, outputLength); + } + } + else if (result < 0) + { + return -1; + } + } + else if (outputOpcode == opcodeStore_ -> abortSplit) + { + handleAbortSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> finishSplit) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding finish split request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> resourceCache); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> freeSplit) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding free split request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> resourceCache); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> freeUnpack) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding free unpack request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> resourceCache); + + #ifdef DEBUG + *logofs << "handleWrite: Freeing unpack state for resource " + << (unsigned int) cValue << ".\n" << logofs_flush; + #endif + + handleUnpackStateRemove(cValue); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> setExposeParameters) + { + // + // Send expose events according to agent's wish. + // + + decodeBuffer.decodeBoolValue(enableExpose_); + decodeBuffer.decodeBoolValue(enableGraphicsExpose_); + decodeBuffer.decodeBoolValue(enableNoExpose_); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> getUnpackParameters) + { + // + // Client proxy needs the list of supported + // unpack methods. We would need an encode + // buffer, but this is in proxy, not here in + // channel. + // + + #ifdef TEST + *logofs << "handleWrite: Sending X_GetInputFocus request for FD#" + << fd_ << " due to OPCODE#" << (unsigned int) outputOpcode + << ".\n" << logofs_flush; + #endif + + outputOpcode = X_GetInputFocus; + + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode, + opcodeStore_ -> getUnpackParameters); + } + else if (outputOpcode == opcodeStore_ -> getControlParameters || + outputOpcode == opcodeStore_ -> getCleanupParameters || + outputOpcode == opcodeStore_ -> getImageParameters) + { + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> getShmemParameters) + { + if (handleShmemRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else if (outputOpcode == opcodeStore_ -> setCacheParameters) + { + if (handleCacheRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else if (outputOpcode == opcodeStore_ -> getFontParameters) + { + if (handleFontRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalGenericRequest); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + } + } // End of switch on opcode. + + // + // TODO: at the moment the variable hit was being set + // but not used, so to avoid the corresponding warning + // this logging block has been added. + // This code will probably be optimized away when none + // of the defines is set, but if there is no additional + // use for the hit variable in the future, then maybe + // it could be removed completely. + // + + if (hit) + { + #if defined(TEST) || defined(OPCODES) + *logofs << "handleWrite: Cached flag enabled in handled request.\n" + << logofs_flush; + #endif + } + + // + // A packed image request can generate more than just + // a single X_PutImage. Write buffer is handled inside + // handleUnpack(). Cannot simply assume that the final + // opcode and size must be put at the buffer offset as + // as buffer could have been grown or could have been + // replaced by a scratch buffer. The same is true in + // the case of a shared memory image. + // + + if (outputOpcode != 0) + { + // + // Commit opcode and size to the buffer. + // + + *outputMessage = (unsigned char) outputOpcode; + + PutUINT(outputLength >> 2, outputMessage + 2, bigEndian_); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleWrite: Handled request OPCODE#" + << (unsigned int) outputOpcode << " (" + << DumpOpcode(outputOpcode) << ") for FD#" + << fd_ << " sequence " << clientSequence_ + << ". " << outputLength << " bytes out.\n" + << logofs_flush; + #endif + } + #if defined(TEST) || defined(OPCODES) + else + { + // + // In case of shared memory images the log doesn't + // reflect the actual opcode of the request that is + // going to be written. It would be possible to find + // the opcode of the original request received from + // the remote proxy in member imageState_ -> opcode, + // but we have probably already deleted the struct. + // + + *logofs << "handleWrite: Handled image request for FD#" + << fd_ << " new sequence " << clientSequence_ + << ". " << outputLength << " bytes out.\n" + << logofs_flush; + } + #endif + + // + // Check if we produced enough data. We need to + // decode all the proxy messages or the decode + // buffer will be left in an inconsistent state, + // so we just update the finish flag in case of + // failure. + // + + handleFlush(flush_if_needed); + + } // End of while (decodeBuffer.decodeOpcodeValue(outputOpcode, 8, ... + + } // End of the decoding block. + + // + // Write any remaining data to the X connection. + // + + if (handleFlush(flush_if_any) < 0) + { + return -1; + } + + // + // Reset offset at which we read the + // last event looking for the shared + // memory completion. + // + + if (shmemState_ != NULL) + { + shmemState_ -> checked = 0; + } + + return 1; +} + +// +// End of handleWrite(). +// + +// +// Other members. +// + +int ServerChannel::handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, + T_store_action action, int position, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // Since ProtoStep7 (#issue 108) + splitState_.current = splitState_.resource; + + handleSplitStoreAlloc(&splitResources_, splitState_.current); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Message OPCODE#" + << (unsigned int) store -> opcode() << " of size " << size + << " [split] with resource " << splitState_.current + << " position " << position << " and action [" + << DumpAction(action) << "] at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + // + // Get the MD5 of the message being + // split. + // + + T_checksum checksum = NULL; + + if (action != IS_HIT) + { + handleSplitChecksum(decodeBuffer, checksum); + } + + // + // The method must abort the connection + // if it can't allocate the split. + // + + Split *splitMessage = clientStore_ -> getSplitStore(splitState_.current) -> + add(store, splitState_.current, position, + action, checksum, buffer, size); + + // + // If the encoding side didn't provide + // a checksum, then don't send the split + // report. + // + + if (checksum == NULL) + { + if (action == IS_HIT) + { + splitMessage -> setState(split_loaded); + } + else + { + splitMessage -> setState(split_missed); + } + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleSplit: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.current); + + #endif + + return 1; + } + + delete [] checksum; + + // + // Tell the split store if it must use + // the disk cache to retrieve and save + // the message. + // + + splitMessage -> setPolicy(splitState_.load, splitState_.save); + + // + // Try to locate the message on disk. + // + + if (clientStore_ -> getSplitStore(splitState_.current) -> + load(splitMessage) == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Loaded the message " + << "from the image cache.\n" << logofs_flush; + #endif + + splitMessage -> setState(split_loaded); + } + else + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: WARNING! SPLIT! Can't find the message " + << "in the image cache.\n" << logofs_flush; + #endif + + splitMessage -> setState(split_missed); + } + + #if defined(TEST) || defined(SPLIT) + + T_timestamp startTs = getTimestamp(); + + *logofs << "handleSplit: SPLIT! Encoding abort " + << "split events for FD#" << fd_ << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0) + { + return -1; + } + + // + // Send the encoded data immediately. We + // want the abort split message to reach + // the remote proxy as soon as possible. + // + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleSplit: SPLIT! Spent " + << diffTimestamp(startTs, getTimestamp()) << " Ms " + << "handling abort split events for FD#" << fd_ + << ".\n" << logofs_flush; + + *logofs << "handleSplit: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.current); + + #endif + + return 1; +} + +int ServerChannel::handleSplit(DecodeBuffer &decodeBuffer) +{ + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Going to handle splits " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + unsigned char resource; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + splitState_.current = resource; + + handleSplitStoreAlloc(&splitResources_, splitState_.current); + + SplitStore *splitStore = clientStore_ -> getSplitStore(splitState_.current); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Handling splits for " + << "resource [" << splitState_.current << "] with " + << splitStore -> getSize() << " elements " + << "in the split store.\n" << logofs_flush; + #endif + + int result = splitStore -> receive(decodeBuffer); + + if (result < 0) + { + #ifdef PANIC + *logofs << "handleSplit: PANIC! Receive of split for FD#" << fd_ + << " failed.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Receive of split for FD#" << fd_ + << " failed.\n"; + + return -1; + } + else if (result == 0) + { + // + // The split is still incomplete. It's time + // to check if we need to start the house- + // keeping process to take care of the image + // cache. + // + + if (proxy -> handleAsyncKeeperCallback() < 0) + { + return -1; + } + } + else + { + // + // Note that we don't need the resource id at the + // X server side and, thus, we don't provide it + // at the time we add split to the split store. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Remote agent should " + << "now commit a new split for resource [" + << splitState_.current << "].\n" + << logofs_flush; + + clientStore_ -> dumpCommitStore(); + + #endif + + if (splitStore -> getSize() == 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Removing split store " + << "for resource [" << splitState_.current + << "] at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + handleSplitStoreRemove(&splitResources_, splitState_.current); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! There are [" << clientStore_ -> + getSplitTotalSize() << "] messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + #endif + } + else + { + // + // If the next split is discarded, it can be + // that, since the beginning of the split, we + // have saved the message on the disk, due to + // a more recent split operation. This is also + // the case when we had to discard the message + // because it was locked but, since then, we + // completed the transferral of the split. + // + + Split *splitMessage = splitStore -> getFirstSplit(); + + if (splitMessage -> getAction() == is_discarded && + splitMessage -> getState() == split_missed && + splitStore -> load(splitMessage) == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: WARNING! SPLIT! Asynchronously " + << "loaded the message from the image cache.\n" + << logofs_flush; + #endif + + splitMessage -> setState(split_loaded); + + #if defined(TEST) || defined(SPLIT) + + T_timestamp startTs = getTimestamp(); + + *logofs << "handleSplit: WARNING! SPLIT! Asynchronously " + << "encoding abort split events for FD#" << fd_ + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0) + { + return -1; + } + + // + // Send the encoded data immediately. We + // want the abort split message to reach + // the remote proxy as soon as possible. + // + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleSplit: WARNING! SPLIT! Spent " + << diffTimestamp(startTs, getTimestamp()) << " Ms " + << "handling asynchronous abort split events for " + << "FD#" << fd_ << ".\n" << logofs_flush; + + *logofs << "handleSplit: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.current); + + #endif + } + } + } + + return 1; +} + +int ServerChannel::handleSplitEvent(EncodeBuffer &encodeBuffer, Split *splitMessage) +{ + int resource = splitMessage -> getResource(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Going to send a " + << "split report for resource " << resource + << ".\n" << logofs_flush; + #endif + + // + // This function is called only after the message + // has been searched in the disk cache. We need to + // inform the other side if the data transfer can + // start or it must be aborted to let the local + // side use the copy that was found on the disk. + // + + #if defined(TEST) || defined(INFO) + + if (splitMessage -> getState() != split_loaded && + splitMessage -> getState() != split_missed) + { + *logofs << "handleSplitEvent: PANIC! Can't find the split to be aborted.\n" + << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // We need to send a boolean telling if the split + // was found or not, followed by the checksum of + // message we are referencing. + // + + T_checksum checksum = splitMessage -> getChecksum(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Sending split report for " + << "checksum [" << DumpChecksum(checksum) << "].\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + encodeBuffer.encodeOpcodeValue(opcodeStore_ -> splitEvent, + serverCache_ -> opcodeCache); + + // + // The encoding in older protocol versions + // is different but we will never try to + // send a split report if the remote does + // not support our version. + // + + encodeBuffer.encodeCachedValue(resource, 8, + serverCache_ -> resourceCache); + + if (splitMessage -> getState() == split_loaded) + { + encodeBuffer.encodeBoolValue(1); + + encodeBuffer.encodeOpcodeValue(splitMessage -> getStore() -> opcode(), + serverCache_ -> abortOpcodeCache); + + encodeBuffer.encodeValue(splitMessage -> compressedSize(), 32, 14); + } + else + { + encodeBuffer.encodeBoolValue(0); + } + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + encodeBuffer.encodeValue((unsigned int) checksum[i], 8); + } + + // + // Update statistics for this special opcode. + // + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) || defined(INFO) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Handled event OPCODE#" + << (unsigned int) opcodeStore_ -> splitEvent << " (" + << DumpOpcode(opcodeStore_ -> splitEvent) << ")" << " for FD#" + << fd_ << " sequence none. 0 bytes in, " << bits << " bits (" + << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; + #endif + + statistics -> addEventBits(opcodeStore_ -> splitEvent, 0, bits); + + return 1; +} + +int ServerChannel::handleAbortSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + unsigned char resource; + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Handling abort split " + << "request for FD#" << fd_ << " and resource " + << (unsigned) resource << ".\n" + << logofs_flush; + #endif + + int splits = 0; + + SplitStore *splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore != NULL) + { + // + // Discard from the memory cache the messages + // that are still incomplete and then get rid + // of the splits in the store. + // + + #if defined(TEST) || defined(SPLIT) + + clientStore_ -> dumpSplitStore(resource); + + #endif + + Split *splitMessage; + + for (;;) + { + splitMessage = splitStore -> getFirstSplit(); + + if (splitMessage == NULL) + { + // + // Check if we had created the store + // but no message was added yet. + // + + #ifdef WARNING + + if (splits == 0) + { + *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The " + << "split store for resource [" << (unsigned int) + resource << "] is unexpectedly empty.\n" + << logofs_flush; + } + + #endif + + break; + } + + // + // Splits already aborted can't be in the + // split store. + // + + #if defined(TEST) || defined(SPLIT) + + if (splitMessage -> getState() == split_aborted) + { + *logofs << "handleAbortSplitRequest: PANIC! SPLIT! Found an " + << "aborted split in store [" << (unsigned int) resource + << "].\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + if (splitMessage -> getAction() == IS_HIT) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Removing the " + << "split from the memory cache.\n" + << logofs_flush; + #endif + + splitMessage -> getStore() -> remove(splitMessage -> getPosition(), + discard_checksum, use_data); + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Removing the " + << "split from the split store.\n" + << logofs_flush; + #endif + + splitMessage = splitStore -> pop(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Freeing up the " + << "aborted split.\n" << logofs_flush; + #endif + + delete splitMessage; + + splits++; + } + } + #ifdef WARNING + else + { + *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The " + << "split store for resource [" << (unsigned int) + resource << "] is already empty.\n" + << logofs_flush; + } + #endif + + handleNullRequest(opcode, buffer, size); + + return (splits > 0); +} + +int ServerChannel::handleCommitSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Get request type and position of the image + // to commit. + // + + unsigned char request; + + decodeBuffer.decodeOpcodeValue(request, clientCache_ -> opcodeCache); + + unsigned int diffCommit; + + decodeBuffer.decodeValue(diffCommit, 32, 5); + + splitState_.commit += diffCommit; + + unsigned char resource = 0; + unsigned int commit = 1; + + // + // Send the resource id and the commit flag. + // The resource id is ignored at the moment. + // The message will be handled based on the + // resource id that was sent together with + // the original message. + // + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + decodeBuffer.decodeBoolValue(commit); + + Split *split = handleSplitCommitRemove(request, resource, splitState_.commit); + + if (split == NULL) + { + return -1; + } + + clientStore_ -> getCommitStore() -> update(split); + + if (commit == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleCommitSplitRequest: SPLIT! Handling split commit " + << "for FD#" << fd_ << " with commit " << commit + << " request " << (unsigned) request << " resource " + << (unsigned) resource << " and position " + << splitState_.commit << ".\n" + << logofs_flush; + #endif + + // + // Allocate as many bytes in the write + // buffer as the final length of the + // message in uncompressed form. + // + + size = split -> plainSize(); + + buffer = writeBuffer_.addMessage(size); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleCommitSplitRequest: SPLIT! Prepared an " + << "outgoing buffer of " << size << " bytes.\n" + << logofs_flush; + #endif + + if (clientStore_ -> getCommitStore() -> expand(split, buffer, size) < 0) + { + writeBuffer_.removeMessage(size); + + commit = 0; + } + } + + // + // Free the split. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleCommitSplitRequest: SPLIT! Freeing up the " + << "committed split.\n" << logofs_flush; + #endif + + delete split; + + // + // Discard the operation and send a null + // message. + // + + if (commit == 0) + { + handleNullRequest(opcode, buffer, size); + } + else + { + // + // Save the sequence number to be able + // to mask any error generated by the + // request. + // + + updateCommitQueue(clientSequence_); + + // + // Now in the write buffer there is + // a copy of this request. + // + + opcode = request; + } + + return commit; +} + +int ServerChannel::handleGeometry(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + // + // Replace the old geometry and taint + // the message into a X_NoOperation. + // + + int resource = *(buffer + 1); + + #ifdef TEST + *logofs << "handleGeometry: Setting new unpack geometry " + << "for resource " << resource << ".\n" + << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + handleUnpackAllocGeometry(resource); + + unpackState_[resource] -> geometry -> depth1_bpp = *(buffer + 4); + unpackState_[resource] -> geometry -> depth4_bpp = *(buffer + 5); + unpackState_[resource] -> geometry -> depth8_bpp = *(buffer + 6); + unpackState_[resource] -> geometry -> depth16_bpp = *(buffer + 7); + unpackState_[resource] -> geometry -> depth24_bpp = *(buffer + 8); + unpackState_[resource] -> geometry -> depth32_bpp = *(buffer + 9); + + unpackState_[resource] -> geometry -> red_mask = GetULONG(buffer + 12, bigEndian_); + unpackState_[resource] -> geometry -> green_mask = GetULONG(buffer + 16, bigEndian_); + unpackState_[resource] -> geometry -> blue_mask = GetULONG(buffer + 20, bigEndian_); + + handleCleanAndNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleColormap(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + // + // Replace the old colormap and taint + // the message into a X_NoOperation. + // + + int resource = *(buffer + 1); + + #ifdef TEST + *logofs << "handleColormap: Setting new unpack colormap " + << "for resource " << resource << ".\n" + << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + handleUnpackAllocColormap(resource); + + // + // New protocol versions send the alpha + // data in compressed form. + // + + // + // Since ProtoStep7 (#issue 108) + // + + { // An anonymous block is used here to limit the scope of local variables + unsigned int packed = GetULONG(buffer + 8, bigEndian_); + unsigned int unpacked = GetULONG(buffer + 12, bigEndian_); + + validateSize("colormap", packed, unpacked, 16, size); + + if (unpackState_[resource] -> colormap -> entries != unpacked >> 2 && + unpackState_[resource] -> colormap -> data != NULL) + { + #ifdef TEST + *logofs << "handleColormap: Freeing previously allocated " + << "unpack colormap data.\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> colormap -> data; + + unpackState_[resource] -> colormap -> data = NULL; + unpackState_[resource] -> colormap -> entries = 0; + } + + #ifdef TEST + *logofs << "handleColormap: Setting " << unpacked + << " bytes of unpack colormap data for resource " + << resource << ".\n" << logofs_flush; + #endif + + if (unpackState_[resource] -> colormap -> data == NULL) + { + unpackState_[resource] -> colormap -> data = + (unsigned int *) new unsigned char[unpacked]; + + if (unpackState_[resource] -> colormap -> data == NULL) + { + #ifdef PANIC + *logofs << "handleColormap: PANIC! Can't allocate " + << unpacked << " entries for unpack colormap data " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + goto handleColormapEnd; + } + + #ifdef DEBUG + *logofs << "handleColormap: Size of new colormap data is " + << unpacked << ".\n" << logofs_flush; + #endif + } + + unsigned int method = *(buffer + 4); + + if (method == PACK_COLORMAP) + { + if (UnpackColormap(method, buffer + 16, packed, + (unsigned char *) unpackState_[resource] -> + colormap -> data, unpacked) < 0) + { + #ifdef PANIC + *logofs << "handleColormap: PANIC! Can't unpack " << packed + << " bytes to " << unpacked << " entries for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> colormap -> data; + + unpackState_[resource] -> colormap -> data = NULL; + unpackState_[resource] -> colormap -> entries = 0; + + goto handleColormapEnd; + } + } + else + { + memcpy((unsigned char *) unpackState_[resource] -> + colormap -> data, buffer + 16, unpacked); + } + + unpackState_[resource] -> colormap -> entries = unpacked >> 2; + + #if defined(DEBUG) && defined(DUMP) + + *logofs << "handleColormap: Dumping colormap entries:\n" + << logofs_flush; + + const unsigned char *p = (const unsigned char *) unpackState_[resource] -> colormap -> data; + + for (unsigned int i = 0; i < unpackState_[resource] -> + colormap -> entries; i++) + { + *logofs << "handleColormap: [" << i << "] [" + << (void *) ((int) p[i]) << "].\n" + << logofs_flush; + } + + #endif + } // end anonymous block + +handleColormapEnd: + + handleCleanAndNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleAlpha(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + int resource = *(buffer + 1); + + #ifdef TEST + *logofs << "handleAlpha: Setting new unpack alpha " + << "for resource " << resource << ".\n" + << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + handleUnpackAllocAlpha(resource); + + // + // New protocol versions send the alpha + // data in compressed form. + // + + // + // Since ProtoStep7 (#issue 108) + // + + { // An anonymous block is used here to limit the scope of local variables + unsigned int packed = GetULONG(buffer + 8, bigEndian_); + unsigned int unpacked = GetULONG(buffer + 12, bigEndian_); + + validateSize("alpha", packed, unpacked, 16, size); + + if (unpackState_[resource] -> alpha -> entries != unpacked && + unpackState_[resource] -> alpha -> data != NULL) + { + #ifdef TEST + *logofs << "handleAlpha: Freeing previously allocated " + << "unpack alpha data.\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> alpha -> data; + + unpackState_[resource] -> alpha -> data = NULL; + unpackState_[resource] -> alpha -> entries = 0; + } + + #ifdef TEST + *logofs << "handleAlpha: Setting " << unpacked + << " bytes of unpack alpha data for resource " + << resource << ".\n" << logofs_flush; + #endif + + if (unpackState_[resource] -> alpha -> data == NULL) + { + unpackState_[resource] -> alpha -> data = new unsigned char[unpacked]; + + if (unpackState_[resource] -> alpha -> data == NULL) + { + #ifdef PANIC + *logofs << "handleAlpha: PANIC! Can't allocate " + << unpacked << " entries for unpack alpha data " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + goto handleAlphaEnd; + } + + #ifdef DEBUG + *logofs << "handleAlpha: Size of new alpha data is " + << unpacked << ".\n" << logofs_flush; + #endif + } + + unsigned int method = *(buffer + 4); + + if (method == PACK_ALPHA) + { + if (UnpackAlpha(method, buffer + 16, packed, + unpackState_[resource] -> alpha -> + data, unpacked) < 0) + { + #ifdef PANIC + *logofs << "handleAlpha: PANIC! Can't unpack " << packed + << " bytes to " << unpacked << " entries for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> alpha -> data; + + unpackState_[resource] -> alpha -> data = NULL; + unpackState_[resource] -> alpha -> entries = 0; + + goto handleAlphaEnd; + } + } + else + { + memcpy((unsigned char *) unpackState_[resource] -> + alpha -> data, buffer + 16, unpacked); + } + + unpackState_[resource] -> alpha -> entries = unpacked; + + #if defined(DEBUG) && defined(DUMP) + + *logofs << "handleAlpha: Dumping alpha entries:\n" + << logofs_flush; + + const unsigned char *p = unpackState_[resource] -> alpha -> data; + + for (unsigned int i = 0; i < unpackState_[resource] -> + alpha -> entries; i++) + { + *logofs << "handleAlpha: [" << i << "] [" + << (void *) ((int) p[i]) << "].\n" + << logofs_flush; + } + + #endif + } //end anonymous block + +handleAlphaEnd: + + handleCleanAndNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleImage(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + int result = 1; + + // + // Save the original opcode together with + // the image state so we can later find if + // this is a plain or a packed image when + // moving data to the shared memory area. + // + + handleImageStateAlloc(opcode); + + if (opcode == opcodeStore_ -> putPackedImage) + { + // + // Unpack the image and put a X_PutImage in a + // new buffer. Save the expected output size, + // so, in the case of a decoding error we can + // still update the statistics. + // + + int length = GetULONG(buffer + 20, bigEndian_); + + #ifdef TEST + *logofs << "handleImage: Sending image for FD#" << fd_ + << " due to OPCODE#" << (unsigned int) opcode << " with " + << GetULONG(buffer + 16, bigEndian_) << " bytes packed " + << "and " << GetULONG(buffer + 20, bigEndian_) + << " bytes unpacked.\n" << logofs_flush; + #endif + + statistics -> addPackedBytesIn(size); + + result = handleUnpack(opcode, buffer, size); + + if (result < 0) + { + // + // Recover from the error. Send a X_NoOperation + // to keep the sequence counter in sync with + // the remote peer. + // + + size = 4; + buffer = writeBuffer_.addMessage(size); + + *buffer = X_NoOperation; + + PutUINT(size >> 2, buffer + 2, bigEndian_); + + #ifdef PANIC + *logofs << "handleImage: PANIC! Sending X_NoOperation for FD#" + << fd_ << " to recover from failed unpack.\n" + << logofs_flush; + #endif + + // + // Set the output length to reflect the amount of + // data that would have been produced by unpacking + // the image. This is advisable to keep the count- + // ers in sync with those at remote proxy. Setting + // the size here doesn't have any effect on the + // size of data sent to the X server as the actual + // size will be taken from the content of the write + // buffer. + // + + size = length; + } + + statistics -> addPackedBytesOut(size); + + // + // Refrain the write loop from putting + // opcode and size in the output buffer. + // + + opcode = 0; + } + + // + // Now image is unpacked as a X_PutImage + // in write buffer. Check if we can send + // the image using the MIT-SHM extension. + // + + if (result > 0) + { + result = handleShmem(opcode, buffer, size); + + // + // We already put opcode and size in + // the resulting buffer. + // + + if (result > 0) + { + opcode = 0; + } + } + + return 1; +} + +int ServerChannel::handleMotion(EncodeBuffer &encodeBuffer) +{ + #if defined(TEST) || defined(INFO) + + if (lastMotion_[0] == '\0') + { + *logofs << "handleMotion: PANIC! No motion events to send " + << "for FD#" << fd_ << ".\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + #if defined(TEST) || defined(INFO) + *logofs << "handleMotion: Sending motion events for FD#" + << fd_ << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + // + // Replicate code from read loop. When have + // time and wish, try to split everything + // in functions. + // + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + const unsigned char *buffer = lastMotion_; + unsigned char opcode = *lastMotion_; + unsigned int size = 32; + + if (GetUINT(buffer + 2, bigEndian_) < serverSequence_) + { + PutUINT(serverSequence_, (unsigned char *) buffer + 2, bigEndian_); + } + + encodeBuffer.encodeOpcodeValue(opcode, serverCache_ -> opcodeCache); + + unsigned int sequenceNum = GetUINT(buffer + 2, bigEndian_); + + unsigned int sequenceDiff = sequenceNum - serverSequence_; + + serverSequence_ = sequenceNum; + + #ifdef DEBUG + *logofs << "handleMotion: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sequenceDiff, 16, + serverCache_ -> eventSequenceCache, 7); + + // + // If we fast encoded the message + // then skip the rest. + // + + if (control -> LocalDeltaCompression == 0) + { + int result = handleFastReadEvent(encodeBuffer, opcode, + buffer, size); + + #ifdef DEBUG + *logofs << "handleMotion: Sent saved motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + lastMotion_[0] = '\0'; + + #ifdef DEBUG + *logofs << "handleMotion: Reset last motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + if (result < 0) + { + return -1; + } + else if (result > 0) + { + return 1; + } + } + + // + // This should be just the part specific + // for motion events but is currently a + // copy-paste of code from the read loop. + // + + unsigned char detail = buffer[1]; + if (*buffer == MotionNotify) + encodeBuffer.encodeBoolValue((unsigned int) detail); + else if ((*buffer == EnterNotify) || (*buffer == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) detail, 3); + else if (*buffer == KeyRelease) + { + if (detail == serverCache_ -> keyPressLastKey) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue((unsigned int) detail, 8); + } + } + else if ((*buffer == ButtonPress) || (*buffer == ButtonRelease)) + encodeBuffer.encodeCachedValue(detail, 8, + serverCache_ -> buttonCache); + else + encodeBuffer.encodeValue((unsigned int) detail, 8); + unsigned int _timestamp = GetULONG(buffer + 4, bigEndian_); + unsigned int timestampDiff = _timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = _timestamp; + encodeBuffer.encodeCachedValue(timestampDiff, 32, + serverCache_ -> motionNotifyTimestampCache, 9); + int skipRest = 0; + if (*buffer == KeyRelease) + { + skipRest = 1; + for (unsigned int i = 8; i < 31; i++) + { + if (buffer[i] != serverCache_ -> keyPressCache[i - 8]) + { + skipRest = 0; + break; + } + } + encodeBuffer.encodeBoolValue(skipRest); + } + if (!skipRest) + { + const unsigned char *nextSrc = buffer + 8; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, + *serverCache_ -> motionNotifyWindowCache[i], 6); + nextSrc += 4; + } + unsigned int rootX = GetUINT(buffer + 20, bigEndian_); + unsigned int rootY = GetUINT(buffer + 22, bigEndian_); + unsigned int eventX = GetUINT(buffer + 24, bigEndian_); + unsigned int eventY = GetUINT(buffer + 26, bigEndian_); + eventX -= rootX; + eventY -= rootY; + encodeBuffer.encodeCachedValue(rootX - + serverCache_ -> motionNotifyLastRootX, 16, + serverCache_ -> motionNotifyRootXCache, 6); + serverCache_ -> motionNotifyLastRootX = rootX; + encodeBuffer.encodeCachedValue(rootY - + serverCache_ -> motionNotifyLastRootY, 16, + serverCache_ -> motionNotifyRootYCache, 6); + serverCache_ -> motionNotifyLastRootY = rootY; + encodeBuffer.encodeCachedValue(eventX, 16, + serverCache_ -> motionNotifyEventXCache, 6); + encodeBuffer.encodeCachedValue(eventY, 16, + serverCache_ -> motionNotifyEventYCache, 6); + encodeBuffer.encodeCachedValue(GetUINT(buffer + 28, bigEndian_), + 16, serverCache_ -> motionNotifyStateCache); + if ((*buffer == EnterNotify) || (*buffer == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) buffer[30], 2); + else + encodeBuffer.encodeBoolValue((unsigned int) buffer[30]); + if ((*buffer == EnterNotify) || (*buffer == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) buffer[31], 2); + else if (*buffer == KeyPress) + { + serverCache_ -> keyPressLastKey = detail; + for (unsigned int i = 8; i < 31; i++) + { + serverCache_ -> keyPressCache[i - 8] = buffer[i]; + } + } + } + + // + // Print info about achieved compression + // and update the statistics. + // + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleMotion: Handled event OPCODE#" << (unsigned int) buffer[0] + << " for FD#" << fd_ << " sequence " << sequenceNum << ". " + << size << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + #endif + + statistics -> addEventBits(*buffer, size << 3, bits); + + #ifdef DEBUG + *logofs << "handleMotion: Sent saved motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + lastMotion_[0] = '\0'; + + #ifdef DEBUG + *logofs << "handleMotion: Reset last motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ServerChannel::handleConfiguration() +{ + #ifdef TEST + *logofs << "ServerChannel: Setting new buffer parameters " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + readBuffer_.setSize(control -> ServerInitialReadSize, + control -> ServerMaximumBufferSize); + + writeBuffer_.setSize(control -> TransportXBufferSize, + control -> TransportXBufferThreshold, + control -> TransportMaximumBufferSize); + + transport_ -> setSize(control -> TransportXBufferSize, + control -> TransportXBufferThreshold, + control -> TransportMaximumBufferSize); + return 1; +} + +int ServerChannel::handleFinish() +{ + #ifdef TEST + *logofs << "ServerChannel: Finishing connection for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + congestion_ = 0; + priority_ = 0; + + finish_ = 1; + + // + // Reset the motion event. + // + + lastMotion_[0] = '\0'; + + transport_ -> fullReset(); + + return 1; +} + +int ServerChannel::handleAsyncEvents() +{ + // + // Encode more events while decoding the + // proxy messages. + // + + if (transport_ -> readable() > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAsyncEvents: WARNING! Encoding events " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + #if defined(TEST) || defined(INFO) + + T_timestamp startTs = getTimestamp(); + + #endif + + if (proxy -> handleAsyncRead(fd_) < 0) + { + return -1; + } + + #if defined(TEST) || defined(INFO) + *logofs << "handleAsyncEvents: Spent " << diffTimestamp(startTs, + getTimestamp()) << " Ms handling events for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 1; + } + + return 0; +} + +int ServerChannel::handleUnpack(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + int resource = *(buffer + 1); + + #ifdef DEBUG + *logofs << "handleUnpack: Unpacking image for resource " << resource + << " with method " << (unsigned int) *(buffer + 12) + << ".\n" << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + T_geometry *geometryState = unpackState_[resource] -> geometry; + T_colormap *colormapState = unpackState_[resource] -> colormap; + T_alpha *alphaState = unpackState_[resource] -> alpha; + + if (geometryState == NULL) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Missing geometry unpacking " + << "image for resource " << resource << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Missing geometry unpacking " + << "image for resource " << resource << ".\n"; + + return -1; + } + + // + // Get the image data from the buffer. + // + + imageState_ -> drawable = GetULONG(buffer + 4, bigEndian_); + imageState_ -> gcontext = GetULONG(buffer + 8, bigEndian_); + + imageState_ -> method = *(buffer + 12); + + imageState_ -> format = *(buffer + 13); + imageState_ -> srcDepth = *(buffer + 14); + imageState_ -> dstDepth = *(buffer + 15); + + imageState_ -> srcLength = GetULONG(buffer + 16, bigEndian_); + imageState_ -> dstLength = GetULONG(buffer + 20, bigEndian_); + + imageState_ -> srcX = GetUINT(buffer + 24, bigEndian_); + imageState_ -> srcY = GetUINT(buffer + 26, bigEndian_); + imageState_ -> srcWidth = GetUINT(buffer + 28, bigEndian_); + imageState_ -> srcHeight = GetUINT(buffer + 30, bigEndian_); + + imageState_ -> dstX = GetUINT(buffer + 32, bigEndian_); + imageState_ -> dstY = GetUINT(buffer + 34, bigEndian_); + imageState_ -> dstWidth = GetUINT(buffer + 36, bigEndian_); + imageState_ -> dstHeight = GetUINT(buffer + 38, bigEndian_); + + #ifdef TEST + *logofs << "handleUnpack: Source X is " << imageState_ -> srcX + << " Y is " << imageState_ -> srcY << " width is " + << imageState_ -> srcWidth << " height is " + << imageState_ -> srcHeight << ".\n" + << logofs_flush; + #endif + + #ifdef TEST + *logofs << "handleUnpack: Destination X is " << imageState_ -> dstX + << " Y is " << imageState_ -> dstY << " width is " + << imageState_ -> dstWidth << " height is " + << imageState_ -> dstHeight << ".\n" + << logofs_flush; + #endif + + if (imageState_ -> srcX != 0 || imageState_ -> srcY != 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Unsupported source coordinates " + << "in unpack request.\n" << logofs_flush; + #endif + + return -1; + } + else if (imageState_ -> method == PACK_COLORMAP_256_COLORS && + (colormapState == NULL || colormapState -> data == NULL)) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Cannot find any unpack colormap.\n" + << logofs_flush; + #endif + + return -1; + } + + // + // Field srcLength carries size of image data in + // packed format. Field dstLength is size of the + // image in the original X bitmap format. + // + + unsigned int srcDataOffset = 40; + + unsigned int srcSize = imageState_ -> srcLength; + + unsigned int removeSize = size; + + unsigned char *srcData = buffer + srcDataOffset; + + // + // Get source and destination bits per pixel. + // + + int srcBitsPerPixel = MethodBitsPerPixel(imageState_ -> method); + + if (srcBitsPerPixel <= 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Can't identify source " + << "bits per pixel for method " << (unsigned int) + imageState_ -> method << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify source bits " + << "per pixel for method " << (unsigned int) + imageState_ -> method << ".\n"; + + writeBuffer_.removeMessage(removeSize); + + return -1; + } + + #ifdef TEST + *logofs << "handleUnpack: Source bits per pixel are " + << srcBitsPerPixel << " source data size is " + << srcSize << ".\n" << logofs_flush; + #endif + + int dstBitsPerPixel = UnpackBitsPerPixel(geometryState, imageState_ -> dstDepth); + + if (dstBitsPerPixel <= 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Can't identify " + << "destination bits per pixel for depth " + << (unsigned int) imageState_ -> dstDepth + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify " + << "destination bits per pixel for depth " + << (unsigned int) imageState_ -> dstDepth + << ".\n"; + + writeBuffer_.removeMessage(removeSize); + + return -1; + } + + // + // Destination is a PutImage request. + // + + unsigned int dstDataOffset = 24; + + // + // Output buffer size must match the number of input + // pixels multiplied by the number of bytes per pixel + // of current geometry. + // + + size = (RoundUp4(imageState_ -> dstWidth * dstBitsPerPixel / 8) * + imageState_ -> dstHeight) + dstDataOffset; + + #ifdef TEST + *logofs << "handleUnpack: Destination bits per pixel are " + << dstBitsPerPixel << " destination data size is " + << size - dstDataOffset << ".\n" << logofs_flush; + #endif + + unsigned int dstSize = size - dstDataOffset; + + imageState_ -> dstLines = imageState_ -> dstHeight; + + unsigned char *dstData; + + // + // Size of the final output buffer had to be stored + // in the offset field of XImage/NXPackedImage. + // + + #ifdef WARNING + + if (dstSize != imageState_ -> dstLength) + { + *logofs << "handleUnpack: WARNING! Destination size mismatch " + << "with reported " << imageState_ -> dstLength + << " and actual " << dstSize << ".\n" + << logofs_flush; + } + + #endif + + // + // The decoding algorithm has put the packed image + // in the plain write buffer. Let's use the scratch + // buffer to uncompress the image. + // + + buffer = writeBuffer_.addScratchMessage(size); + + dstData = buffer + dstDataOffset; + + // + // Unpack image into the buffer. + // + + *buffer = (unsigned char) X_PutImage; + + *(buffer + 1) = imageState_ -> format; + + PutUINT(size >> 2, buffer + 2, bigEndian_); + + PutULONG(imageState_ -> drawable, buffer + 4, bigEndian_); + PutULONG(imageState_ -> gcontext, buffer + 8, bigEndian_); + + PutUINT(imageState_ -> dstWidth, buffer + 12, bigEndian_); + PutUINT(imageState_ -> dstLines, buffer + 14, bigEndian_); + + PutUINT(imageState_ -> dstX, buffer + 16, bigEndian_); + PutUINT(imageState_ -> dstY, buffer + 18, bigEndian_); + + *(buffer + 20) = 0; + *(buffer + 21) = imageState_ -> dstDepth; + + #ifdef TEST + *logofs << "handleUnpack: Write buffer size is " + << writeBuffer_.getLength() << " scratch size is " + << writeBuffer_.getScratchLength() << ".\n" + << logofs_flush; + #endif + + int result = 0; + + switch (imageState_ -> method) + { + case PACK_JPEG_8_COLORS: + case PACK_JPEG_64_COLORS: + case PACK_JPEG_256_COLORS: + case PACK_JPEG_512_COLORS: + case PACK_JPEG_4K_COLORS: + case PACK_JPEG_32K_COLORS: + case PACK_JPEG_64K_COLORS: + case PACK_JPEG_256K_COLORS: + case PACK_JPEG_2M_COLORS: + case PACK_JPEG_16M_COLORS: + { + result = UnpackJpeg(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_PNG_8_COLORS: + case PACK_PNG_64_COLORS: + case PACK_PNG_256_COLORS: + case PACK_PNG_512_COLORS: + case PACK_PNG_4K_COLORS: + case PACK_PNG_32K_COLORS: + case PACK_PNG_64K_COLORS: + case PACK_PNG_256K_COLORS: + case PACK_PNG_2M_COLORS: + case PACK_PNG_16M_COLORS: + { + result = UnpackPng(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_RGB_16M_COLORS: + { + result = UnpackRgb(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_RLE_16M_COLORS: + { + result = UnpackRle(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_BITMAP_16M_COLORS: + { + result = UnpackBitmap(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_COLORMAP_256_COLORS: + { + result = Unpack8(geometryState, colormapState, srcBitsPerPixel, + imageState_ -> srcWidth, imageState_ -> srcHeight, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + + break; + } + default: + { + const T_colormask *colorMask = MethodColorMask(imageState_ -> method); + + switch (imageState_ -> method) + { + case PACK_MASKED_8_COLORS: + case PACK_MASKED_64_COLORS: + case PACK_MASKED_256_COLORS: + { + result = Unpack8(geometryState, colorMask, imageState_ -> srcDepth, + imageState_ -> srcWidth, imageState_ -> srcHeight, + srcData, srcSize, imageState_ -> dstDepth, + imageState_ -> dstWidth, imageState_ -> dstHeight, + dstData, dstSize); + break; + } + case PACK_MASKED_512_COLORS: + case PACK_MASKED_4K_COLORS: + case PACK_MASKED_32K_COLORS: + case PACK_MASKED_64K_COLORS: + { + result = Unpack16(geometryState, colorMask, imageState_ -> srcDepth, + imageState_ -> srcWidth, imageState_ -> srcHeight, + srcData, srcSize, imageState_ -> dstDepth, + imageState_ -> dstWidth, imageState_ -> dstHeight, + dstData, dstSize); + break; + } + case PACK_MASKED_256K_COLORS: + case PACK_MASKED_2M_COLORS: + case PACK_MASKED_16M_COLORS: + { + result = Unpack24(geometryState, colorMask, imageState_ -> srcDepth, + imageState_ -> srcWidth, imageState_ -> srcHeight, + srcData, srcSize, imageState_ -> dstDepth, + imageState_ -> dstWidth, imageState_ -> dstHeight, + dstData, dstSize); + break; + } + default: + { + break; + } + } + } + } + + writeBuffer_.removeMessage(removeSize); + + if (result <= 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Failed to unpack image " + << "with method '" << (unsigned int) imageState_ -> method + << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Failed to unpack image " + << "with method '" << (unsigned int) imageState_ -> method + << "'.\n"; + + // + // TODO: We should mark the image somehow, + // and force the remote to remove it from + // the cache. + // + + writeBuffer_.removeScratchMessage(); + + return -1; + } + + // + // Alpha channel is used only on some 32 bits pixmaps + // and only if render extension is in use. Presently + // we don't have an efficient way to know in advance + // if mask must be applied or not to the image. If an + // alpha channel is set, the function will check if + // the size of the alpha data matches the size of the + // image. In the worst case we'll create an useless + // alpha plane for a pixmap that doesn't need it. + // + + if (alphaState != NULL && alphaState -> data != NULL && + imageState_ -> dstDepth == 32) + { + UnpackAlpha(alphaState, dstData, dstSize, imageByteOrder_); + } + + return 1; +} + +int ServerChannel::handleAuthorization(unsigned char *buffer) +{ + // + // At the present moment we don't support more than + // a single display for each proxy, so authorization + // data is shared among all the channels. + // + // Use the following code to simulate authentication + // failures on a LSB machine: + // + // memcpy(buffer + 12 + (((buffer[6] + 256 * + // buffer[7]) + 3) & ~3), "1234567890123456", 16); + // + + if (auth == NULL) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAuthorization: Forwarding the real cookie " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (auth -> checkCookie(buffer) == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAuthorization: Matched the fake cookie " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 1; + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAuthorization: WARNING! Failed to match " + << "the fake cookie for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + return -1; + } +} + +int ServerChannel::handleAuthorization(const unsigned char *buffer, int size) +{ + // + // Check the X server's response and, in the case of + // an error, print the textual information reported + // by the server. + // + + if (*buffer != 1) + { + const char *reason = NULL; + + // + // At the moment we don't take into account the end- + // ianess of the reply. This should work in any case + // because we simply try to match a few well-known + // error strings. + // + + if (size >= INVALID_COOKIE_SIZE + 8 && + memcmp(buffer + 8, INVALID_COOKIE_DATA, + INVALID_COOKIE_SIZE) == 0) + { + reason = INVALID_COOKIE_DATA; + } + else if (size >= NO_AUTH_PROTO_SIZE + 8 && + memcmp(buffer + 8, NO_AUTH_PROTO_DATA, + NO_AUTH_PROTO_SIZE) == 0) + { + reason = NO_AUTH_PROTO_DATA; + } + else + { + reason = "Unknown"; + } + + #ifdef WARNING + *logofs << "handleAuthorization: WARNING! X connection failed " + << "with error '" << reason << "' on FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + cerr << "Warning" << ": X connection failed " + << "with error '" << reason << "'.\n"; + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "handleAuthorization: X connection successful " + << "on FD#" << fd_ << ".\n" << logofs_flush; + } + #endif + + return 1; +} + +// +// Use a simple encoding. Need to handle the image +// requests in the usual way and the X_ListExtensions +// and X_QueryExtension to hide MIT-SHM and RENDER +// in the reply. +// + +int ServerChannel::handleFastWriteRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // All the NX requests are handled in the + // main message loop. + // + + // + // Since ProtoStep7 (#issue 108) + // + // The X_PutImage can be handled here only + // if a split was not requested. + // + + if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) || + (opcode == X_PutImage && splitState_.resource != nothing) || + opcode == X_ListExtensions || + opcode == X_QueryExtension) + { + return 0; + } + + #ifdef DEBUG + *logofs << "handleFastWriteRequest: Decoding raw request OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + buffer = writeBuffer_.addMessage(4); + + #ifndef __sun + + unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(4); + + *((unsigned int *) buffer) = *next; + + #else /* #ifndef __sun */ + + memcpy(buffer, decodeBuffer.decodeMemory(4), 4); + + #endif /* #ifndef __sun */ + + size = GetUINT(buffer + 2, bigEndian_) << 2; + + if (size < 4) + { + #ifdef WARNING + *logofs << "handleFastWriteRequest: WARNING! Assuming size 4 " + << "for suspicious message of size " << size + << ".\n" << logofs_flush; + #endif + + size = 4; + } + + writeBuffer_.registerPointer(&buffer); + + if (writeBuffer_.getAvailable() < size - 4 || + (int) size >= control -> TransportFlushBufferSize) + { + #ifdef DEBUG + *logofs << "handleFastWriteRequest: Using scratch buffer for OPCODE#" + << (unsigned int) opcode << " with size " << size << " and " + << writeBuffer_.getLength() << " bytes in buffer.\n" + << logofs_flush; + #endif + + // + // The procedure moving data to shared memory + // assumes that the full message is stored in + // the scratch buffer. We can safely let the + // scratch buffer inherit the decode buffer + // at the next offset. + // + + writeBuffer_.removeMessage(4); + + buffer = writeBuffer_.addScratchMessage(((unsigned char *) + decodeBuffer.decodeMemory(size - 4)) - 4, size); + } + else + { + writeBuffer_.addMessage(size - 4); + + #ifndef __sun + + if (size <= 32) + { + next = (unsigned int *) decodeBuffer.decodeMemory(size - 4); + + for (unsigned int i = 4; i < size; i += sizeof(unsigned int)) + { + *((unsigned int *) (buffer + i)) = *next++; + } + } + else + { + memcpy(buffer + 4, decodeBuffer.decodeMemory(size - 4), size - 4); + } + + #else /* #ifndef __sun */ + + memcpy(buffer + 4, decodeBuffer.decodeMemory(size - 4), size - 4); + + #endif /* #ifndef __sun */ + } + + // + // Opcode could have been tainted by the client + // proxy. Replace the original opcode with the + // one sent in the decode buffer. + // + + *buffer = opcode; + + writeBuffer_.unregisterPointer(); + + if (opcode == X_PutImage) + { + handleImage(opcode, buffer, size); + } + + #if defined(TEST) || defined(OPCODES) + + if (opcode != 0) + { + *logofs << "handleFastWriteRequest: Handled request " + << "OPCODE#" << (unsigned int) opcode << " (" + << DumpOpcode(opcode) << ") for FD#" << fd_ + << " sequence " << clientSequence_ << ". " + << size << " bytes out.\n" << logofs_flush; + } + else + { + *logofs << "handleFastWriteRequest: Handled image or " + << "other request for FD#" << fd_ + << " sequence " << clientSequence_ << ". " + << size << " bytes out.\n" << logofs_flush; + } + + #endif + + handleFlush(flush_if_needed); + + return 1; +} + +// +// Use the simplest encoding except for replies that +// need to be managed some way. +// + +int ServerChannel::handleFastReadReply(EncodeBuffer &encodeBuffer, const unsigned char &opcode, + const unsigned char *&buffer, const unsigned int &size) +{ + // + // If we pushed a X_GetInputFocus in the sequence + // queue this means that the original message was + // a NX request for which we have to provide a NX + // reply. + // + + if ((opcode >= X_NXFirstOpcode && + opcode <= X_NXLastOpcode) || + opcode == X_QueryExtension || + opcode == X_ListExtensions || + opcode == X_GetInputFocus) + { + return 0; + } + + #ifdef DEBUG + *logofs << "handleFastReadReply: Encoding raw reply OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << " with size " << size << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeMemory(buffer, size); + + // + // Send back the reply as soon + // as possible. + // + + priority_++; + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleFastReadReply: Handled raw reply OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ << " sequence " + << serverSequence_ << ". " << size << " bytes in, " + << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + #endif + + statistics -> addReplyBits(opcode, size << 3, bits); + + return 1; +} + +int ServerChannel::handleFastReadEvent(EncodeBuffer &encodeBuffer, const unsigned char &opcode, + const unsigned char *&buffer, const unsigned int &size) +{ + #ifdef DEBUG + *logofs << "handleFastReadEvent: Encoding raw " + << (opcode == X_Error ? "error" : "event") << " OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << " with size " << size << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeMemory(buffer, size); + + switch (opcode) + { + case X_Error: + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + { + priority_++; + } + } + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + if (opcode == X_Error) + { + unsigned char code = *(buffer + 1); + + *logofs << "handleFastReadEvent: Handled error ERR_CODE#" + << (unsigned int) code << " for FD#" << fd_; + + *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + *logofs << " sequence " << serverSequence_ << ". " << size + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + else + { + *logofs << "handleFastReadEvent: Handled event OPCODE#" + << (unsigned int) *buffer << " for FD#" << fd_ + << " sequence " << serverSequence_ << ". " << size + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + + #endif + + statistics -> addEventBits(opcode, size << 3, bits); + + return 1; +} + +void ServerChannel::initCommitQueue() +{ + #ifdef TEST + *logofs << "initCommitQueue: Resetting the queue of split commits " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE; i++) + { + commitSequenceQueue_[i] = 0; + } +} + +void ServerChannel::updateCommitQueue(unsigned short sequence) +{ + for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE - 1; i++) + { + commitSequenceQueue_[i + 1] = commitSequenceQueue_[i]; + } + + #ifdef TEST + *logofs << "updateCommitQueue: Saved " << sequence + << " as last sequence number of image to commit.\n" + << logofs_flush; + #endif + + commitSequenceQueue_[0] = sequence; +} + +int ServerChannel::checkCommitError(unsigned char error, unsigned short sequence, + const unsigned char *buffer) +{ + // + // Check if error is due to an image commit + // generated at the end of a split. + // + // TODO: It should zero the head of the list + // when an event comes with a sequence number + // greater than the value of the last element + // added. + // + + for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE && + commitSequenceQueue_[i] != 0; i++) + { + #ifdef TEST + *logofs << "checkCommitError: Checking committed image's " + << "sequence number " << commitSequenceQueue_[i] + << " with input sequence " << sequence << ".\n" + << logofs_flush; + #endif + + if (commitSequenceQueue_[i] == sequence) + { + #ifdef WARNING + + *logofs << "checkCommitError: WARNING! Failed operation for " + << "FD#" << fd_ << " with ERR_CODE#" + << (unsigned int) *(buffer + 1); + + *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + *logofs << " sequence " << sequence << ".\n"; + + *logofs << logofs_flush; + + #endif + + cerr << "Warning" << ": Failed commit operation " + << "with ERR_CODE#" << (unsigned int) error; + + cerr << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + cerr << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + cerr << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + cerr << ".\n"; + + #ifdef WARNING + *logofs << "checkCommitError: WARNING! Suppressing error on " + << "OPCODE#" << (unsigned int) opcodeStore_ -> commitSplit + << " for FD#" << fd_ << " with sequence " << sequence + << " at position " << i << ".\n" << logofs_flush; + #endif + + return 0; + } + } + + return 0; +} + +// +// Check if the user pressed the CTRL+ALT+SHIFT+ESC +// keystroke. At the present moment it uses different +// keycodes based on the client OS. This should be +// implemented in a way that is platform independent +// (that's not an easy task, considered that we don't +// have access to the higher level X libraries). +// + +int ServerChannel::checkKeyboardEvent(unsigned char event, unsigned short sequence, + const unsigned char *buffer) +{ + #ifdef TEST + *logofs << "checkKeyboardEvent: Checking escape sequence with byte [1] " + << (void *) ((unsigned) *(buffer + 1)) << " and bytes [28-29] " + << (void *) ((unsigned) GetUINT(buffer + 28, bigEndian_)) + << " for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + #ifdef __APPLE__ + + int alert = (*(buffer + 1) == 0x3d && + GetUINT(buffer + 28, bigEndian_) == 0x2005); + + #else + + int alert = (*(buffer + 1) == 0x09 && + ((GetUINT(buffer + 28, bigEndian_) & + 0x0d) == 0x0d)); + + #endif + + if (alert == 1) + { + #ifdef PANIC + *logofs << "checkKeyboardEvent: PANIC! Received sequence " + << "CTRL+ALT+SHIFT+ESC " << "for FD#"<< fd_ + << ". Showing the abort dialog.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Received sequence CTRL+ALT+SHIFT+ESC. " + << "Showing the abort dialog.\n"; + + HandleAlert(CLOSE_UNRESPONSIVE_X_SERVER_ALERT, 1); + } + + return alert; +} + +// +// Handle the MIT-SHM initialization +// messages exchanged with the remote +// proxy. +// + +int ServerChannel::handleShmemReply(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned int stage, const unsigned char *buffer, + const unsigned int size) +{ + #ifdef TEST + *logofs << "handleShmemReply: Returning shmem reply for " + << "stage " << stage << ".\n" << logofs_flush; + #endif + + if (opcode == X_QueryExtension) + { + encodeBuffer.encodeValue(stage, 2); + +#ifndef ANDROID + shmemState_ -> present = *(buffer + 8); +#else + shmemState_ -> present = 0; + cerr << "Info: handleShmemReply: In android no shared memory. Setting present to 0 hardcoded\n"; +#endif + shmemState_ -> opcode = *(buffer + 9); + shmemState_ -> event = *(buffer + 10); + shmemState_ -> error = *(buffer + 11); + + #ifdef TEST + *logofs << "handleShmemReply: Extension present is " + << shmemState_ -> present << " with base OPCODE#" + << (unsigned int) shmemState_ -> opcode << " base event " + << (unsigned int) shmemState_ -> event << " base error " + << (unsigned int) shmemState_ -> error << ".\n" + << logofs_flush; + #endif + } + else if (opcode == X_GetInputFocus) + { + encodeBuffer.encodeValue(stage, 2); + + encodeBuffer.encodeBoolValue(0); + + if (shmemState_ -> present == 1 && + shmemState_ -> address != NULL && + shmemState_ -> segment > 0 && + shmemState_ -> id > 0) + { + cerr << "Info" << ": Using shared memory parameters 1/" + << (shmemState_ -> size / 1024) << "K.\n"; + +#ifndef ANDROID + shmemState_ -> enabled = 1; +#else + cerr << "Info: handleShmemReply: In android no shared memory. Setting enabled to -1. This should not be displayed\n"; + shmemState_ -> enabled = -1; +#endif + + encodeBuffer.encodeBoolValue(1); + } + else + { + #ifdef TEST + *logofs << "handleShmemReply: WARNING! Not using shared memory " + << "support in X server for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Info" << ": Using shared memory parameters 0/0K.\n"; + + handleShmemStateRemove(); + + encodeBuffer.encodeBoolValue(0); + } + } + else + { + #ifdef PANIC + *logofs << "handleShmemReply: PANIC! Conversation error " + << "handling shared memory support for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Conversation error handling " + << "shared memory support.\n"; + + return -1; + } + + return 1; +} + +int ServerChannel::handleShmemRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // We need to query and initialize MIT-SHM on + // the real X server. To do this we'll need 3 + // requests. At the end we'll have to encode + // the final reply for the X client side. + // + + handleShmemStateAlloc(); + + unsigned int stage; + + decodeBuffer.decodeValue(stage, 2); + + unsigned int expected = shmemState_ -> stage + 1; + + if (stage != expected || stage > 2) + { + #ifdef PANIC + *logofs << "handleShmemRequest: PANIC! Unexpected stage " + << stage << " in handling shared memory " + << "support for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Unexpected stage " + << stage << " in handling shared memory " + << "support for FD#" << fd_ << ".\n"; + + return -1; + } + + switch (stage) + { + case 0: + { + unsigned int enableClient; + unsigned int enableServer; + + decodeBuffer.decodeBoolValue(enableClient); + decodeBuffer.decodeBoolValue(enableServer); + + unsigned int clientSegment; + unsigned int serverSegment; + + decodeBuffer.decodeValue(clientSegment, 29, 9); + decodeBuffer.decodeValue(serverSegment, 29, 9); + + shmemState_ -> segment = serverSegment; + + #ifdef TEST + *logofs << "handleShmemRequest: Size of the shared memory " + << "segment will be " << control -> ShmemServerSize + << ".\n" << logofs_flush; + #endif + + #ifdef TEST + *logofs << "handleShmemRequest: Sending X_QueryExtension request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getShmemParameters << " in stage " + << stage << ".\n" << logofs_flush; + #endif + + opcode = X_QueryExtension; + + size = 16; + buffer = writeBuffer_.addMessage(size); + + PutUINT(7, buffer + 4, bigEndian_); + + // + // Simply make the query fail if shared + // memory support is disabled by the + // user. + // +#ifndef ANDROID + if (control -> ShmemServer == 1 && + control -> ShmemServerSize > 0 && + enableServer == 1) + { + memcpy(buffer + 8, "MIT-SHM", 7); + } + else + { + memcpy(buffer + 8, "NO-MIT-", 7); + } +#else + cerr << "Info: handleShmemRequest: In android no shared memory. Returning NO-MIT- answer\n"; + + memcpy(buffer + 8, "NO-MIT-", 7); +#endif + sequenceQueue_.push(clientSequence_, opcode, + opcodeStore_ -> getShmemParameters, stage); + + // + // Save the sequence number so we can + // later identify any matching X error + // received from server. + // + + shmemState_ -> sequence = clientSequence_; + + break; + } + case 1: + { + if (shmemState_ -> present == 1) + { + // + // Make the segment read-write for everybody on + // Cygwin (to avoid any lack of support or any + // performance issue) and on MacOS/X (where the + // 0600 mask doesn't seem to work). + // + + #if defined(__CYGWIN32__) || defined(__APPLE__) + + int permissions = 0777; + + #else + + int permissions = 0600; + + #endif + + shmemState_ -> size = control -> ShmemServerSize; + +#ifndef ANDROID + shmemState_ -> id = shmget(IPC_PRIVATE, shmemState_ -> size, + IPC_CREAT | permissions); +#else + cerr << "Info: handleShmemReqyest: In android no shared memory (shmget). This message should not be displayed present should never be 1 in android\n"; + shmemState_ -> id = -1; +#endif + if (shmemState_ -> id >= 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemRequest: Allocated shared memory " + << "segment of " << shmemState_ -> size + << " bytes with id " << shmemState_ -> id + << ".\n" << logofs_flush; + #endif + + +#ifndef ANDROID + shmemState_ -> address = shmat(shmemState_ -> id, 0, 0); +#else + cerr << "Info: handleShmemReqyest: In android no shared memory (shmat). This message should not be displayed. present should never be 1 in android\n"; + shmemState_ -> address = NULL; +#endif + if (shmemState_ -> address != NULL) + { + #ifdef TEST + *logofs << "handleShmemRequest: Sending X_ShmAttach request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getShmemParameters << " in stage " + << stage << ".\n" << logofs_flush; + #endif + + opcode = shmemState_ -> opcode; + + size = 16; + buffer = writeBuffer_.addMessage(size); + + *(buffer + 1) = X_ShmAttach; + + PutULONG(shmemState_ -> segment, buffer + 4, bigEndian_); + PutULONG(shmemState_ -> id, buffer + 8, bigEndian_); + + *(buffer + 12) = 1; + + shmemState_ -> sequence = clientSequence_; + + break; + } + else + { + #ifdef WARNING + *logofs << "handleShmemRequest: WARNING! Can't attach the shared " + << "memory segment. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Can't attach the shared memory " + << "segment. Error is " << EGET() << " '" + << ESTR() << "'.\n"; + } + } + else + { + #ifndef __CYGWIN32__ + + #ifdef WARNING + *logofs << "handleShmemRequest: WARNING! Can't create the shared " + << "memory segment. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Can't create the shared memory " + << "segment. Error is " << EGET() << " '" + << ESTR() << "'.\n"; + + #else + + #ifdef TEST + *logofs << "handleShmemRequest: WARNING! Can't create the shared " + << "memory segment. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + #endif + } + } + + if (shmemState_ -> present != 0) + { + #ifdef TEST + *logofs << "handleShmemRequest: Resetting shared memory " + << "presence flag for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + shmemState_ -> present = 0; + } + + handleNullRequest(opcode, buffer, size); + + break; + } + default: + { + #ifdef TEST + *logofs << "handleShmemRequest: Sending X_GetInputFocus request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getShmemParameters << " in stage " + << stage << ".\n" << logofs_flush; + #endif + + opcode = X_GetInputFocus; + + size = 4; + buffer = writeBuffer_.addMessage(size); + + sequenceQueue_.push(clientSequence_, opcode, + opcodeStore_ -> getShmemParameters, stage); + break; + } + } + + shmemState_ -> stage += 1; + + return 1; +} + +// +// Handling of MIT-SHM extension has been plugged late in +// the design, so we have to make some assumptions. Image +// is a X_PutImage request contained either in the scratch +// buffer or in the normal write buffer. We need to move +// the image data to the shared memory segment and replace +// the X_PutImage request with a X_ShmPutImage. +// + +int ServerChannel::handleShmem(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + if (shmemState_ == NULL || shmemState_ -> enabled != 1) + { + #ifdef TEST + + if (shmemState_ != NULL) + { + *logofs << "handleShmem: PANIC! Shared memory " + << "state found but support is not enabled " + << "for FD#" << fd_ << " in stage " + << shmemState_ -> stage << ".\n" + << logofs_flush; + } + + #endif + + return 0; + } +#ifdef ANDROID + cerr << "Info: handleShmem: In android no shared memory. enabled should never be 1. This should not be displayed\n"; + return 0; +#endif + + // + // Ignore null requests and requests that will not result + // in a single X_PutImage. To conform with the other func- + // tions, we get the opcode passed as a parameter. It can + // be zero if we don't want the write loop to put opcode + // and length in the resulting buffer. Anyway we are only + // interested in the original opcode of the request, that + // is stored in the image state. + // + + unsigned char *dstData = buffer + 24; + unsigned int dstDataSize = size - 24; + + if (dstDataSize == 0 || dstDataSize > + (unsigned int) control -> MaximumRequestSize) + { + #ifdef TEST + *logofs << "handleShmem: Ignoring image with opcode " + << (unsigned int) imageState_ -> opcode + << " and size " << size << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + return 0; + } + + #ifdef TEST + *logofs << "handleShmem: Handling image with opcode " + << (unsigned int) imageState_ -> opcode + << " and size " << size << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Get image data from buffer. + // + + if (imageState_ -> opcode == X_PutImage) + { + // + // We still need to get the image's data. + // + + imageState_ -> format = *(buffer + 1); + + imageState_ -> drawable = GetULONG(buffer + 4, bigEndian_); + imageState_ -> gcontext = GetULONG(buffer + 8, bigEndian_); + + imageState_ -> dstWidth = GetUINT(buffer + 12, bigEndian_); + imageState_ -> dstHeight = GetUINT(buffer + 14, bigEndian_); + + imageState_ -> srcX = 0; + imageState_ -> srcY = 0; + + imageState_ -> srcWidth = imageState_ -> dstWidth; + imageState_ -> srcHeight = imageState_ -> dstHeight; + + imageState_ -> dstX = GetUINT(buffer + 16, bigEndian_); + imageState_ -> dstY = GetUINT(buffer + 18, bigEndian_); + + imageState_ -> leftPad = *(buffer + 20); + imageState_ -> dstDepth = *(buffer + 21); + + imageState_ -> dstLines = imageState_ -> dstHeight; + + imageState_ -> dstLength = size - 24; + } + + // + // Skip the MIT-SHM operation if the image + // is 1 bits-per-plane. + // + + if (imageState_ -> dstDepth == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmem: Ignoring image with opcode " + << (unsigned int) imageState_ -> opcode << " depth " + << (unsigned int) imageState_ -> dstDepth << " and " + << "size " << size << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + return 0; + } + + // + // If the image can't fit in the available + // space, check if the completion event is + // arrived. + // + + #if defined(TEST) || defined(INFO) + + if (isTimestamp(shmemState_ -> last) == 0 && + shmemState_ -> offset != 0) + { + *logofs << "handleShmem: PANIC! No timestamp for sequence " + << shmemState_ -> sequence << " with offset " + << shmemState_ -> offset << ".\n" + << logofs_flush; + } + + #endif + + if (shmemState_ -> offset + imageState_ -> dstLength > + shmemState_ -> size) + { + if (imageState_ -> dstLength > shmemState_ -> size) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmem: WARNING! Can't fit the image " + << "in the available memory segment for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (handleShmemEvent() <= 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmem: WARNING! Missing completion " + << "after " << diffTimestamp(shmemState_ -> last, + getTimestamp()) << " Ms for shared memory " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 0; + } + } + + // + // Let image start at current offset + // in the shared segment. + // + + #ifdef TEST + *logofs << "handleShmem: Copying " << dstDataSize + << " bytes to shared memory at offset " + << shmemState_ -> offset << " for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + memcpy((unsigned char *) shmemState_ -> address + + shmemState_ -> offset, dstData, dstDataSize); + + // + // Get rid of the original X_PutImage + // request. + // + + if (writeBuffer_.getScratchData() != NULL) + { + writeBuffer_.removeScratchMessage(); + } + else + { + writeBuffer_.removeMessage(size); + } + + // + // Add a X_ShmPutImage request to the + // write buffer. + // + + buffer = writeBuffer_.addMessage(40); + + *buffer = shmemState_ -> opcode; + + *(buffer + 1) = X_ShmPutImage; + + PutUINT(40 >> 2, buffer + 2, bigEndian_); + + PutULONG(imageState_ -> drawable, buffer + 4, bigEndian_); + PutULONG(imageState_ -> gcontext, buffer + 8, bigEndian_); + + PutUINT(imageState_ -> dstWidth, buffer + 12, bigEndian_); + PutUINT(imageState_ -> dstLines, buffer + 14, bigEndian_); + + PutUINT(imageState_ -> srcX, buffer + 16, bigEndian_); + PutUINT(imageState_ -> srcY, buffer + 18, bigEndian_); + + PutUINT(imageState_ -> dstWidth, buffer + 20, bigEndian_); + PutUINT(imageState_ -> dstLines, buffer + 22, bigEndian_); + + PutUINT(imageState_ -> dstX, buffer + 24, bigEndian_); + PutUINT(imageState_ -> dstY, buffer + 26, bigEndian_); + + *(buffer + 28) = imageState_ -> dstDepth; + *(buffer + 29) = imageState_ -> format; + *(buffer + 30) = 1; + + PutULONG(shmemState_ -> segment, buffer + 32, bigEndian_); + PutULONG(shmemState_ -> offset, buffer + 36, bigEndian_); + + shmemState_ -> offset += dstDataSize; + + shmemState_ -> sequence = clientSequence_; + shmemState_ -> last = getTimestamp(); + + #ifdef TEST + *logofs << "handleShmem: Saved shared memory sequence " + << shmemState_ -> sequence << " for FD#" << fd_ + << " with offset " << shmemState_ -> offset + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + // + // Make the X server read immediately + // from the shared memory buffer and + // produce the completion event. + // + + handleFlush(flush_if_any); + + return 1; +} + +// +// Try to read more events from the socket in the +// attempt to get the completion event required +// to reset the MIT-SHM segment. +// + +int ServerChannel::handleShmemEvent() +{ + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemEvent: Waiting for shared memory " + << "sequence " << shmemState_ -> sequence + << " for X server FD#" << fd_ << ".\n" + << logofs_flush; + + T_timestamp startTs = getTimestamp(); + + #endif + + while (isTimestamp(shmemState_ -> last) != 0) + { + if (handleWait(control -> ShmemTimeout) <= 0) + { + break; + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "handleShmemEvent: WARNING! Encoded events " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + } + #endif + } + + if (isTimestamp(shmemState_ -> last) == 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemEvent: Spent " + << diffTimestamp(startTs, getTimestamp()) << " Ms " + << "waiting for shared memory sequence for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 1; + } + + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemEvent: WARNING! Can't reset shared " + << "memory sequence for FD#" << fd_ << " after " + << diffTimestamp(shmemState_ -> last, getTimestamp()) + << " Ms.\n" << logofs_flush; + #endif + + return 0; +} + +int ServerChannel::checkShmemEvent(unsigned char event, unsigned short sequence, + const unsigned char *buffer) +{ + if (isTimestamp(shmemState_ -> last) == 1 && + sequence == shmemState_ -> sequence) + { + #ifdef TEST + *logofs << "checkShmemEvent: Reset shared memory sequence " + << shmemState_ -> sequence << " for FD#" << fd_ + << " after " << diffTimestamp(shmemState_ -> last, + getTimestamp()) << " Ms.\n" << logofs_flush; + #endif + + shmemState_ -> sequence = 0; + shmemState_ -> offset = 0; + shmemState_ -> last = nullTimestamp(); + } + #ifdef TEST + else + { + *logofs << "checkShmemEvent: Skipping past shared memory " + << "image sequence " << sequence << " for FD#" + << fd_ << ".\n" << logofs_flush; + } + #endif + + return 1; +} + +int ServerChannel::checkShmemError(unsigned char error, unsigned short sequence, + const unsigned char *buffer) +{ + #ifdef TEST + + *logofs << "checkShmemError: WARNING! Failed operation for " + << "FD#" << fd_ << " in stage " << shmemState_ -> stage + << " with ERR_CODE#" << (unsigned int) *(buffer + 1); + + *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + *logofs << " sequence " << sequence << ".\n"; + + *logofs << logofs_flush; + + #endif + + // + // If enabled flag is <= 0 we are still + // in the inizialization phase. In this + // case force presence to false. + // + + if (shmemState_ -> enabled != 1) + { + if (shmemState_ -> present != 0) + { + #ifdef TEST + *logofs << "checkShmemError: Resetting shared memory " + << "presence flag for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + shmemState_ -> present = 0; + } + + return 0; + } + + if (shmemState_ -> sequence == sequence) + { + // + // Reset the sequence and timestamp. + // + + shmemState_ -> sequence = 0; + shmemState_ -> offset = 0; + shmemState_ -> last = nullTimestamp(); + } + + return 1; +} + +int ServerChannel::handleFontRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Send a synchronization request and use + // the reply to return the requested font + // path. + // + + #ifdef TEST + *logofs << "handleFontRequest: Sending X_GetInputFocus request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getFontParameters << ".\n" + << logofs_flush; + #endif + + opcode = X_GetInputFocus; + + size = 4; + buffer = writeBuffer_.addMessage(size); + + sequenceQueue_.push(clientSequence_, X_GetInputFocus, + opcodeStore_ -> getFontParameters); + + return 1; +} + +int ServerChannel::handleFontReply(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + #ifdef TEST + *logofs << "handleFontReply: Encoding font operation " + << "reply with size " << size << ".\n" + << logofs_flush; + #endif + + char data[256]; + + if (fontPort_ != -1) + { + sprintf(data + 1, "tcp/localhost:%d", fontPort_); + } + else + { + *(data + 1) = '\0'; + } + + *data = strlen(data + 1); + + unsigned char *next = (unsigned char *) data; + + unsigned int length = (unsigned int) (*next++); + + encodeBuffer.encodeValue(length, 8); + + encodeBuffer.encodeTextData(next, length); + + return 1; +} + +int ServerChannel::handleCacheRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + unsigned int mask; + + decodeBuffer.decodeCachedValue(mask, 32, clientCache_ -> + setCacheParametersCache); + + splitState_.save = (mask >> 8) & 0xff; + splitState_.load = mask & 0xff; + + #ifdef TEST + *logofs << "handleCacheRequest: Set cache parameters to " + << "save " << splitState_.save << " load " + << splitState_.load << ".\n" << logofs_flush; + #endif + + handleNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleStartSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Prepare for the split for the selected + // resource. Old proxy versions only used + // the split store at position 0. + // + + // Since ProtoStep7 (#issue 108) + unsigned char resource; + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + splitState_.resource = resource; + + splitState_.current = splitState_.resource; + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleStartSplitRequest: SPLIT! Registered id " + << splitState_.resource << " as resource " + << "waiting for a split.\n" << logofs_flush; + #endif + + handleNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleEndSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Verify that the agent resource matches. + // + + // Since ProtoStep7 (#issue 108) + unsigned char resource; + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + #ifdef TEST + + if (splitState_.resource == nothing) + { + #ifdef PANIC + *logofs << "handleEndSplitRequest: PANIC! SPLIT! Received an end of " + << "split for resource id " << (unsigned int) *(buffer + 1) + << " without a previous start.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + else if (resource != splitState_.resource) + { + #ifdef PANIC + *logofs << "handleEndSplitRequest: PANIC! SPLIT! Invalid resource id " + << resource << " received while waiting for resource id " + << splitState_.resource << ".\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + #endif + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleEndSplitRequest: SPLIT! Reset id " + << splitState_.resource << " as resource " + << "selected for splits.\n" << logofs_flush; + #endif + + splitState_.resource = nothing; + + handleNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleSplitChecksum(DecodeBuffer &decodeBuffer, T_checksum &checksum) +{ + unsigned int receive; + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeBoolValue(receive); + + if (receive == 1) + { + checksum = new md5_byte_t[MD5_LENGTH]; + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + decodeBuffer.decodeValue(receive, 8); + + if (checksum != NULL) + { + checksum[i] = (unsigned char) receive; + } + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitChecksum: SPLIT! Received checksum " + << "[" << DumpChecksum(checksum) << "].\n" + << logofs_flush; + #endif + + return 1; + } + + return 0; +} + +void ServerChannel::handleShmemStateAlloc() +{ + if (shmemState_ == NULL) + { + shmemState_ = new T_shmem_state(); + + shmemState_ -> stage = -1; + shmemState_ -> present = -1; + shmemState_ -> enabled = -1; + + shmemState_ -> segment = -1; + shmemState_ -> id = -1; + shmemState_ -> address = NULL; + shmemState_ -> size = 0; + + shmemState_ -> opcode = 0xff; + shmemState_ -> event = 0xff; + shmemState_ -> error = 0xff; + + shmemState_ -> sequence = 0; + shmemState_ -> offset = 0; + shmemState_ -> last = nullTimestamp(); + + shmemState_ -> checked = 0; + } +} + +void ServerChannel::handleShmemStateRemove() +{ + if (shmemState_ != NULL) + { + if (shmemState_ -> address != NULL) + { +#ifndef ANDROID + shmdt((char *) shmemState_ -> address); +#else + cerr << "Info: handleShmemStateRemove: In android no shared memory. This should not be displayed. address should always be NULL\n"; +#endif + } + + if (shmemState_ -> id > 0) + { +#ifndef ANDROID + shmctl(shmemState_ -> id, IPC_RMID, 0); +#else + cerr << "Info: handleShmemStateRemove: In android no shared memory. This should not be displayed. id should always be 0\n"; +#endif + } + + delete shmemState_; + + shmemState_ = NULL; + } +} + +void ServerChannel::handleUnpackStateInit(int resource) +{ + if (unpackState_[resource] == NULL) + { + unpackState_[resource] = new T_unpack_state(); + + if (unpackState_[resource] == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackStateInit: PANIC! Can't allocate " + << "memory for unpack state in context [A].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [A].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> geometry = NULL; + unpackState_[resource] -> colormap = NULL; + unpackState_[resource] -> alpha = NULL; + } +} + +void ServerChannel::handleUnpackAllocGeometry(int resource) +{ + if (unpackState_[resource] -> geometry == NULL) + { + unpackState_[resource] -> geometry = new T_geometry(); + + if (unpackState_[resource] -> geometry == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackAllocGeometry: PANIC! Can't allocate " + << "memory for unpack state in context [B].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [B].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> geometry -> depth1_bpp = 4; + unpackState_[resource] -> geometry -> depth4_bpp = 4; + unpackState_[resource] -> geometry -> depth8_bpp = 8; + unpackState_[resource] -> geometry -> depth16_bpp = 16; + unpackState_[resource] -> geometry -> depth24_bpp = 32; + unpackState_[resource] -> geometry -> depth32_bpp = 32; + + unpackState_[resource] -> geometry -> red_mask = 0xff0000; + unpackState_[resource] -> geometry -> green_mask = 0x00ff00; + unpackState_[resource] -> geometry -> blue_mask = 0x0000ff; + + unpackState_[resource] -> geometry -> image_byte_order = imageByteOrder_; + unpackState_[resource] -> geometry -> bitmap_bit_order = bitmapBitOrder_; + unpackState_[resource] -> geometry -> scanline_unit = scanlineUnit_; + unpackState_[resource] -> geometry -> scanline_pad = scanlinePad_; + } +} + +void ServerChannel::handleUnpackAllocColormap(int resource) +{ + if (unpackState_[resource] -> colormap == NULL) + { + unpackState_[resource] -> colormap = new T_colormap(); + + if (unpackState_[resource] -> colormap == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackAllocColormap: PANIC! Can't allocate " + << "memory for unpack state in context [C].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [C].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> colormap -> entries = 0; + unpackState_[resource] -> colormap -> data = NULL; + } +} + +void ServerChannel::handleUnpackAllocAlpha(int resource) +{ + if (unpackState_[resource] -> alpha == NULL) + { + unpackState_[resource] -> alpha = new T_alpha(); + + if (unpackState_[resource] -> alpha == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackAllocAlpha: PANIC! Can't allocate " + << "memory for unpack state in context [D].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [D].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> alpha -> entries = 0; + unpackState_[resource] -> alpha -> data = NULL; + } +} + +void ServerChannel::handleUnpackStateRemove(int resource) +{ + if (unpackState_[resource] != NULL) + { + delete unpackState_[resource] -> geometry; + + if (unpackState_[resource] -> colormap != NULL) + { + delete [] unpackState_[resource] -> colormap -> data; + } + + delete unpackState_[resource] -> colormap; + + if (unpackState_[resource] -> alpha != NULL) + { + delete [] unpackState_[resource] -> alpha -> data; + } + + delete unpackState_[resource] -> alpha; + + delete unpackState_[resource]; + + unpackState_[resource] = NULL; + } +} + +void ServerChannel::handleEncodeCharInfo(const unsigned char *nextSrc, EncodeBuffer &encodeBuffer) +{ + unsigned int value = GetUINT(nextSrc, bigEndian_) | + (GetUINT(nextSrc + 10, bigEndian_) << 16); + + encodeBuffer.encodeCachedValue(value, 32, + *serverCache_ -> queryFontCharInfoCache[0], 6); + + nextSrc += 2; + + for (unsigned int i = 1; i < 5; i++) + { + unsigned int _value = GetUINT(nextSrc, bigEndian_); + + nextSrc += 2; + + encodeBuffer.encodeCachedValue(_value, 16, + *serverCache_ -> queryFontCharInfoCache[i], 6); + } +} + +int ServerChannel::setBigEndian(int flag) +{ + bigEndian_ = flag; + + readBuffer_.setBigEndian(flag); + + return 1; +} + +int ServerChannel::setReferences() +{ + #ifdef TEST + *logofs << "ServerChannel: Initializing the static " + << "members for the server channels.\n" + << logofs_flush; + #endif + + #ifdef REFERENCES + + references_ = 0; + + #endif + + return 1; +} diff --git a/nxcomp/src/ServerChannel.h b/nxcomp/src/ServerChannel.h new file mode 100644 index 000000000..374e52896 --- /dev/null +++ b/nxcomp/src/ServerChannel.h @@ -0,0 +1,529 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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); + + // + // 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 */ diff --git a/nxcomp/src/ServerProxy.cpp b/nxcomp/src/ServerProxy.cpp new file mode 100644 index 000000000..28f94842a --- /dev/null +++ b/nxcomp/src/ServerProxy.cpp @@ -0,0 +1,621 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> + +#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_ = NULL; + smbServerPort_ = NULL; + mediaServerPort_ = NULL; + httpServerPort_ = NULL; + + 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(ChannelEndPoint &cupsServerPort, + ChannelEndPoint &smbServerPort, + ChannelEndPoint &mediaServerPort, + ChannelEndPoint &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, + cupsServerPort_, "CUPS"); + } + case channel_smb: + { + smbServerPort_.setDefaultTCPInterface(1); + return handleNewGenericConnectionFromProxy(channelId, channel_smb, + smbServerPort_, "SMB"); + } + case channel_media: + { + return handleNewGenericConnectionFromProxy(channelId, channel_media, + mediaServerPort_, "media"); + } + case channel_http: + { + return handleNewGenericConnectionFromProxy(channelId, channel_http, + 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; +} diff --git a/nxcomp/src/ServerProxy.h b/nxcomp/src/ServerProxy.h new file mode 100644 index 000000000..e169c4aec --- /dev/null +++ b/nxcomp/src/ServerProxy.h @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef ServerProxy_H +#define ServerProxy_H + +#include <sys/types.h> +#include <sys/socket.h> + +#include "Proxy.h" + +#include "Misc.h" +#include "ChannelEndPoint.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(ChannelEndPoint &cupsServerPort, + ChannelEndPoint &smbServerPort, + ChannelEndPoint &mediaServerPort, + ChannelEndPoint &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) + { + // Since ProtoStep7 (#issue 108) + return ((channelId & control -> ChannelMask) == 0); + } + + private: + + // FIXME: Use a ChannelEndPoint object also for the X server! + 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. + // + + ChannelEndPoint cupsServerPort_; + ChannelEndPoint smbServerPort_; + ChannelEndPoint mediaServerPort_; + ChannelEndPoint 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 */ diff --git a/nxcomp/src/ServerReadBuffer.cpp b/nxcomp/src/ServerReadBuffer.cpp new file mode 100644 index 000000000..277b85216 --- /dev/null +++ b/nxcomp/src/ServerReadBuffer.cpp @@ -0,0 +1,247 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 accommodate + // 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; +} diff --git a/nxcomp/src/ServerReadBuffer.h b/nxcomp/src/ServerReadBuffer.h new file mode 100644 index 000000000..d6c207ead --- /dev/null +++ b/nxcomp/src/ServerReadBuffer.h @@ -0,0 +1,73 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/ServerStore.cpp b/nxcomp/src/ServerStore.cpp new file mode 100644 index 000000000..8123b6de0 --- /dev/null +++ b/nxcomp/src/ServerStore.cpp @@ -0,0 +1,183 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/ServerStore.h b/nxcomp/src/ServerStore.h new file mode 100644 index 000000000..dbbb968e5 --- /dev/null +++ b/nxcomp/src/ServerStore.h @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/SetClipRectangles.cpp b/nxcomp/src/SetClipRectangles.cpp new file mode 100644 index 000000000..b43cea938 --- /dev/null +++ b/nxcomp/src/SetClipRectangles.cpp @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} diff --git a/nxcomp/src/SetClipRectangles.h b/nxcomp/src/SetClipRectangles.h new file mode 100644 index 000000000..a2245360c --- /dev/null +++ b/nxcomp/src/SetClipRectangles.h @@ -0,0 +1,187 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/SetUnpackAlpha.cpp b/nxcomp/src/SetUnpackAlpha.cpp new file mode 100644 index 000000000..5a352d26a --- /dev/null +++ b/nxcomp/src/SetUnpackAlpha.cpp @@ -0,0 +1,266 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + 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; + + // Since ProtoStep8 (#issue 108) + 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); +} diff --git a/nxcomp/src/SetUnpackAlpha.h b/nxcomp/src/SetUnpackAlpha.h new file mode 100644 index 000000000..54714efaa --- /dev/null +++ b/nxcomp/src/SetUnpackAlpha.h @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_DATA_LIMIT 16384 + +#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_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 */ diff --git a/nxcomp/src/SetUnpackColormap.cpp b/nxcomp/src/SetUnpackColormap.cpp new file mode 100644 index 000000000..2c9bba1bd --- /dev/null +++ b/nxcomp/src/SetUnpackColormap.cpp @@ -0,0 +1,266 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + 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; + + // Since ProtoStep8 (#issue 108) + 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); +} diff --git a/nxcomp/src/SetUnpackColormap.h b/nxcomp/src/SetUnpackColormap.h new file mode 100644 index 000000000..779366531 --- /dev/null +++ b/nxcomp/src/SetUnpackColormap.h @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_DATA_LIMIT 4096 + +#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 */ diff --git a/nxcomp/src/SetUnpackGeometry.cpp b/nxcomp/src/SetUnpackGeometry.cpp new file mode 100644 index 000000000..edff6a544 --- /dev/null +++ b/nxcomp/src/SetUnpackGeometry.cpp @@ -0,0 +1,305 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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 +} diff --git a/nxcomp/src/SetUnpackGeometry.h b/nxcomp/src/SetUnpackGeometry.h new file mode 100644 index 000000000..96104f57f --- /dev/null +++ b/nxcomp/src/SetUnpackGeometry.h @@ -0,0 +1,167 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/ShapeExtension.cpp b/nxcomp/src/ShapeExtension.cpp new file mode 100644 index 000000000..2ee2b67db --- /dev/null +++ b/nxcomp/src/ShapeExtension.cpp @@ -0,0 +1,305 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; + + // Since ProtoStep7 (#issue 108) + 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 + } +} diff --git a/nxcomp/src/ShapeExtension.h b/nxcomp/src/ShapeExtension.h new file mode 100644 index 000000000..4dd636847 --- /dev/null +++ b/nxcomp/src/ShapeExtension.h @@ -0,0 +1,164 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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_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 */ diff --git a/nxcomp/src/Socket.cpp b/nxcomp/src/Socket.cpp new file mode 100644 index 000000000..8be04d76d --- /dev/null +++ b/nxcomp/src/Socket.cpp @@ -0,0 +1,757 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/utsname.h> + +#if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun) +#include <netinet/in_systm.h> +#endif + +#ifdef __sun +#include <unistd.h> +#include <sys/termios.h> +#endif + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> + +#include <netdb.h> +#include <fcntl.h> + +// +// 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])); + } +} diff --git a/nxcomp/src/Socket.h b/nxcomp/src/Socket.h new file mode 100644 index 000000000..77837a115 --- /dev/null +++ b/nxcomp/src/Socket.h @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Socket_H +#define Socket_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#ifdef __sun +#include <stropts.h> +#include <sys/filio.h> +#endif + +#ifdef HAVE_IN_ADDR_T +#define IN_ADDR_T in_addr_t +#else +#define IN_ADDR_T unsigned +#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 */ diff --git a/nxcomp/src/Split.cpp b/nxcomp/src/Split.cpp new file mode 100644 index 000000000..e2fea97cc --- /dev/null +++ b/nxcomp/src/Split.cpp @@ -0,0 +1,1839 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <unistd.h> +#include <cstring> +#include <sys/stat.h> +#include <sys/types.h> +#include <utime.h> + +#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. + // + + // Since ProtoStep7 (#issue 108) + decodeBuffer.decodeValue(compressedSize, 32, 14); + + 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; +} diff --git a/nxcomp/src/Split.h b/nxcomp/src/Split.h new file mode 100644 index 000000000..ee5eae7fe --- /dev/null +++ b/nxcomp/src/Split.h @@ -0,0 +1,543 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/StaticCompressor.cpp b/nxcomp/src/StaticCompressor.cpp new file mode 100644 index 000000000..b47193354 --- /dev/null +++ b/nxcomp/src/StaticCompressor.cpp @@ -0,0 +1,432 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/StaticCompressor.h b/nxcomp/src/StaticCompressor.h new file mode 100644 index 000000000..e0b81a527 --- /dev/null +++ b/nxcomp/src/StaticCompressor.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 diff --git a/nxcomp/src/Statistics.cpp b/nxcomp/src/Statistics.cpp new file mode 100644 index 000000000..ab8fd74dc --- /dev/null +++ b/nxcomp/src/Statistics.cpp @@ -0,0 +1,2007 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> + +#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; +} diff --git a/nxcomp/src/Statistics.h b/nxcomp/src/Statistics.h new file mode 100644 index 000000000..1ffb6b5d6 --- /dev/null +++ b/nxcomp/src/Statistics.h @@ -0,0 +1,745 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Timestamp.cpp b/nxcomp/src/Timestamp.cpp new file mode 100644 index 000000000..e7e0c494a --- /dev/null +++ b/nxcomp/src/Timestamp.cpp @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/Timestamp.h b/nxcomp/src/Timestamp.h new file mode 100644 index 000000000..604f5a3bc --- /dev/null +++ b/nxcomp/src/Timestamp.h @@ -0,0 +1,307 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Timestamp_H +#define Timestamp_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <time.h> +#include <sys/time.h> + +#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 */ diff --git a/nxcomp/src/TranslateCoords.cpp b/nxcomp/src/TranslateCoords.cpp new file mode 100644 index 000000000..f61caea47 --- /dev/null +++ b/nxcomp/src/TranslateCoords.cpp @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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); +} diff --git a/nxcomp/src/TranslateCoords.h b/nxcomp/src/TranslateCoords.h new file mode 100644 index 000000000..997d079e1 --- /dev/null +++ b/nxcomp/src/TranslateCoords.h @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Transport.cpp b/nxcomp/src/Transport.cpp new file mode 100644 index 000000000..897bf32f1 --- /dev/null +++ b/nxcomp/src/Transport.cpp @@ -0,0 +1,3068 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/socket.h> + +#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 + + 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 diff --git a/nxcomp/src/Transport.h b/nxcomp/src/Transport.h new file mode 100644 index 000000000..4ae0e3c25 --- /dev/null +++ b/nxcomp/src/Transport.h @@ -0,0 +1,577 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Transport_H +#define Transport_H + +#include <zlib.h> +#include <errno.h> + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/socket.h> + +#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 accommodate + // 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() __attribute__((noreturn)); + + virtual int drain(int limit, int timeout) __attribute((noreturn)); + + // + // 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 */ diff --git a/nxcomp/src/Types.h b/nxcomp/src/Types.h new file mode 100644 index 000000000..e82664c81 --- /dev/null +++ b/nxcomp/src/Types.h @@ -0,0 +1,271 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Types_H +#define Types_H + +using namespace std; + +#include <vector> +#include <list> +#include <map> +#include <set> + +#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()); + } + + // Avoid overriding clear() when using libc++. Fiddling with STL internals + // doesn't really seem like a good idea to me anyway. + #ifndef _LIBCPP_VECTOR + 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) */ + } + #endif /* #ifdef _LIBCPP_VECTOR */ +}; + +class T_messages : public vector < Message * > +{ + public: + + // Avoid overriding clear() when using libc++. Fiddling with STL internals + // doesn't really seem like a good idea to me anyway. + #ifndef _LIBCPP_VECTOR + 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) */ + } + #endif /* #ifndef _LIBCPP_VECTOR */ +}; + +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 accommodate 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. +// + +// Since ProtoStep8 (#issue 108) +enum T_store_action +{ + is_hit, + is_added, + is_discarded, + is_removed +}; + +// Since ProtoStep8 (#issue 108) +#define IS_HIT is_hit +#define IS_ADDED is_added + +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 */ diff --git a/nxcomp/src/Unpack.cpp b/nxcomp/src/Unpack.cpp new file mode 100644 index 000000000..e63e836f0 --- /dev/null +++ b/nxcomp/src/Unpack.cpp @@ -0,0 +1,1514 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/Unpack.h b/nxcomp/src/Unpack.h new file mode 100644 index 000000000..faaa41d82 --- /dev/null +++ b/nxcomp/src/Unpack.h @@ -0,0 +1,149 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Vars.c b/nxcomp/src/Vars.c new file mode 100644 index 000000000..685969677 --- /dev/null +++ b/nxcomp/src/Vars.c @@ -0,0 +1,54 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> + +#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 diff --git a/nxcomp/src/Version.c b/nxcomp/src/Version.c new file mode 100644 index 000000000..509bd8344 --- /dev/null +++ b/nxcomp/src/Version.c @@ -0,0 +1,101 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2015 Qindel Formacion y Servicios SL. */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License Version 2, 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 from Qindel */ +/* or write to the Free Software Foundation, Inc., 51 Franklin Street, */ +/* Fifth Floor, Boston, MA 02110-1301 USA. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "NX.h" + + +static int _NXVersionMajor = -1; +static int _NXVersionMinor = -1; +static int _NXVersionPatch = -1; +static int _NXVersionMaintenancePatch = -1; + + +const char* NXVersion() { + const char *version = VERSION; + return version; +} + +void _parseNXVersion() { + char version[32]; + int i; + strcpy(version, VERSION); + + char *value; + /* Reset values to 0 if undefined */ + _NXVersionMajor = _NXVersionMinor = _NXVersionPatch = _NXVersionMaintenancePatch = 0; + + +#define NXVERSIONSEPARATOR "." + value = strtok(version, NXVERSIONSEPARATOR); + + for (i = 0; value != NULL && i < 4; i++) + { + switch (i) + { + case 0: + _NXVersionMajor = atoi(value); + break; + + case 1: + _NXVersionMinor = atoi(value); + break; + + case 2: + _NXVersionPatch = atoi(value); + break; + + case 3: + _NXVersionMaintenancePatch = atoi(value); + break; + } + + value = strtok(NULL, NXVERSIONSEPARATOR); + } +} + +int NXMajorVersion() { + if (_NXVersionMajor == -1) + _parseNXVersion(); + return _NXVersionMajor; +} +int NXMinorVersion() { + if (_NXVersionMinor == -1) + _parseNXVersion(); + return _NXVersionMinor; +} +int NXPatchVersion() { + if (_NXVersionPatch == -1) + _parseNXVersion(); + return _NXVersionPatch; +} +int NXMaintenancePatchVersion() { + if (_NXVersionMaintenancePatch == -1) + _parseNXVersion(); + return _NXVersionMaintenancePatch; +} diff --git a/nxcomp/src/WriteBuffer.cpp b/nxcomp/src/WriteBuffer.cpp new file mode 100644 index 000000000..70b4e3b53 --- /dev/null +++ b/nxcomp/src/WriteBuffer.cpp @@ -0,0 +1,500 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stddef.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#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; +} diff --git a/nxcomp/src/WriteBuffer.h b/nxcomp/src/WriteBuffer.h new file mode 100644 index 000000000..ce408e210 --- /dev/null +++ b/nxcomp/src/WriteBuffer.h @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/XidCache.cpp b/nxcomp/src/XidCache.cpp new file mode 100644 index 000000000..ab6574cc9 --- /dev/null +++ b/nxcomp/src/XidCache.cpp @@ -0,0 +1,51 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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]; + } +} diff --git a/nxcomp/src/XidCache.h b/nxcomp/src/XidCache.h new file mode 100644 index 000000000..8a09ef9b1 --- /dev/null +++ b/nxcomp/src/XidCache.h @@ -0,0 +1,49 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#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 */ diff --git a/nxcomp/src/Z.cpp b/nxcomp/src/Z.cpp new file mode 100644 index 000000000..af3b380f2 --- /dev/null +++ b/nxcomp/src/Z.cpp @@ -0,0 +1,146 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "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; +} diff --git a/nxcomp/src/Z.h b/nxcomp/src/Z.h new file mode 100644 index 000000000..3a6d684c2 --- /dev/null +++ b/nxcomp/src/Z.h @@ -0,0 +1,37 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ +/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */ +/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */ +/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */ +/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/ +/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of the aforementioned persons and companies. */ +/* */ +/* Redistribution and use of the present software is allowed according */ +/* to terms specified in the file LICENSE.nxcomp which comes in the */ +/* source distribution. */ +/* */ +/* All rights reserved. */ +/* */ +/* NOTE: This software has received contributions from various other */ +/* contributors, only the core maintainers and supporters are listed as */ +/* copyright holders. Please contact us, if you feel you should be listed */ +/* as copyright holder, as well. */ +/* */ +/**************************************************************************/ + +#ifndef Z_H +#define Z_H + +#include <zlib.h> + +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 */ |