diff options
author | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2011-11-19 15:59:16 +0100 |
---|---|---|
committer | Mike Gabriel <mike.gabriel@das-netzwerkteam.de> | 2011-11-19 15:59:16 +0100 |
commit | a48361b11a5abb5a345dac5ec83a8f56c4d50b74 (patch) | |
tree | a16fb870a072450bb45ee09ac4df9887bfa7f98e | |
parent | 9997e13bb583de4012914006c7507839a4e11227 (diff) | |
parent | 232dfc41d41390bfffa75ec2ed065c109fa03a0e (diff) | |
download | nx-libs-a48361b11a5abb5a345dac5ec83a8f56c4d50b74.tar.gz nx-libs-a48361b11a5abb5a345dac5ec83a8f56c4d50b74.tar.bz2 nx-libs-a48361b11a5abb5a345dac5ec83a8f56c4d50b74.zip |
Merge branch 'nxcomp'
265 files changed, 118610 insertions, 0 deletions
diff --git a/nxcomp/ActionCache.cpp b/nxcomp/ActionCache.cpp new file mode 100644 index 000000000..79b670021 --- /dev/null +++ b/nxcomp/ActionCache.cpp @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Control.h" + +#include "ActionCache.h" + +ActionCache::ActionCache() +{ + for (int i = 0; i < 256; i++) + { + base_[i] = new IntCache(8); + } + + slot_ = 0; + last_ = 0; +} + +ActionCache::~ActionCache() +{ + for (int i = 0; i < 256; i++) + { + delete base_[i]; + } +} diff --git a/nxcomp/ActionCache.h b/nxcomp/ActionCache.h new file mode 100644 index 000000000..23265fcf2 --- /dev/null +++ b/nxcomp/ActionCache.h @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ActionCache_H +#define ActionCache_H + +#include "IntCache.h" + +class ActionCache +{ + friend class EncodeBuffer; + friend class DecodeBuffer; + + public: + + ActionCache(); + ~ActionCache(); + + private: + + IntCache *base_[256]; + + unsigned int slot_; + unsigned short last_; +}; + +#endif /* ActionCache_H */ diff --git a/nxcomp/ActionCacheCompat.h b/nxcomp/ActionCacheCompat.h new file mode 100644 index 000000000..8281db826 --- /dev/null +++ b/nxcomp/ActionCacheCompat.h @@ -0,0 +1,45 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ActionCacheCompat_H +#define ActionCacheCompat_H + +#include "CharCache.h" + +class ActionCacheCompat +{ + friend class EncodeBuffer; + friend class DecodeBuffer; + + public: + + ActionCacheCompat() + { + slot_ = 0; + } + + ~ActionCacheCompat() + { + } + + private: + + CharCache base_[4]; + unsigned char slot_; +}; + +#endif /* ActionCacheCompat_H */ diff --git a/nxcomp/Agent.cpp b/nxcomp/Agent.cpp new file mode 100644 index 000000000..c0b729d06 --- /dev/null +++ b/nxcomp/Agent.cpp @@ -0,0 +1,72 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Misc.h" +#include "Agent.h" +#include "Proxy.h" + +extern Proxy *proxy; + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +Agent::Agent(int fd[2]) +{ + remoteFd_ = fd[0]; + localFd_ = fd[1]; + + transport_ = new AgentTransport(localFd_); + + if (transport_ == NULL) + { + #ifdef PANIC + *logofs << "Agent: PANIC! Can't create the memory-to-memory transport " + << "for FD#" << localFd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't create the memory-to-memory transport " + << "for FD#" << localFd_ << ".\n"; + + HandleCleanup(); + } + + FD_ZERO(&saveRead_); + FD_ZERO(&saveWrite_); + + canRead_ = 0; + + #ifdef DEBUG + *logofs << "Agent: Created agent object at " << this + << ".\n" << logofs_flush; + #endif +} + +Agent::~Agent() +{ + delete transport_; + + #ifdef DEBUG + *logofs << "Agent: Deleted agent object at " << this + << ".\n" << logofs_flush; + #endif +} diff --git a/nxcomp/Agent.h b/nxcomp/Agent.h new file mode 100644 index 000000000..fac5acd43 --- /dev/null +++ b/nxcomp/Agent.h @@ -0,0 +1,243 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Agent_H +#define Agent_H + +#include <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) + { + #if defined(TEST) || defined(INFO) + *logofs << "Agent: remoteCanRead() is " << + (FD_ISSET(remoteFd_, readSet) && transport_ -> dequeuable() != 0) + << " with FD_ISSET() " << (int) FD_ISSET(remoteFd_, readSet) + << " and dequeuable " << transport_ -> dequeuable() + << ".\n" << logofs_flush; + #endif + + return (FD_ISSET(remoteFd_, readSet) && + transport_ -> dequeuable() != 0); + } + + int remoteCanWrite(const fd_set * const writeSet) + { + #if defined(TEST) || defined(INFO) + *logofs << "Agent: remoteCanWrite() is " << + (FD_ISSET(remoteFd_, writeSet) && transport_ -> + queuable() != 0 && canRead_ == 1) << " with FD_ISSET() " + << (int) FD_ISSET(remoteFd_, writeSet) << " queueable " + << transport_ -> queuable() << " channel can read " + << canRead_ << ".\n" << logofs_flush; + #endif + + return (FD_ISSET(remoteFd_, writeSet) && + transport_ -> queuable() != 0 && + canRead_ == 1); + } + + int localCanRead() + { + #if defined(TEST) || defined(INFO) + *logofs << "Agent: localCanRead() is " << + (transport_ -> readable() != 0 && canRead_ == 1) + << " with readable " << transport_ -> readable() + << " channel can read " << canRead_ << ".\n" + << logofs_flush; + #endif + + return (transport_ -> readable() != 0 && + canRead_ == 1); + } + + int proxyCanRead() + { + #if defined(TEST) || defined(INFO) + *logofs << "Agent: proxyCanRead() is " << proxy -> canRead() + << ".\n" << logofs_flush; + #endif + + return (proxy -> canRead()); + } + + int proxyCanRead(const fd_set * const readSet) + { + #if defined(TEST) || defined(INFO) + *logofs << "Agent: proxyCanRead() is " + << ((int) FD_ISSET(proxy -> getFd(), readSet) + << ".\n" << logofs_flush; + #endif + + return (FD_ISSET(proxy -> getFd(), readSet)); + } + + int enqueueData(const char *data, const int size) const + { + return transport_ -> enqueue(data, size); + } + + int dequeueData(char *data, int size) const + { + return transport_ -> dequeue(data, size); + } + + int dequeuableData() const + { + return transport_ -> dequeuable(); + } + + private: + + int remoteFd_; + int localFd_; + + fd_set saveRead_; + fd_set saveWrite_; + + int canRead_; + + AgentTransport *transport_; +}; + +#endif /* Agent_H */ diff --git a/nxcomp/Alpha.cpp b/nxcomp/Alpha.cpp new file mode 100644 index 000000000..931101495 --- /dev/null +++ b/nxcomp/Alpha.cpp @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Misc.h" +#include "Unpack.h" +#include "Alpha.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +int UnpackAlpha(unsigned char method, unsigned char *src_data, int src_size, + unsigned char *dst_data, int dst_size) +{ + if (*src_data == 0) + { + if (dst_size != src_size - 1) + { + #ifdef TEST + *logofs << "UnpackAlpha: PANIC! Invalid destination size " + << dst_size << " with source " << src_size + << ".\n" << logofs_flush; + #endif + + return -1; + } + + #ifdef TEST + *logofs << "UnpackAlpha: Expanding " << src_size - 1 + << " bytes of plain alpha data.\n" << logofs_flush; + #endif + + memcpy(dst_data, src_data + 1, src_size - 1); + + return 1; + } + + unsigned int check_size = dst_size; + + int result = ZDecompress(&unpackStream, dst_data, &check_size, + src_data + 1, src_size - 1); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "UnpackAlpha: PANIC! Failure decompressing alpha data. " + << "Error is '" << zError(result) << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decompressing alpha data. " + << "Error is '" << zError(result) << "'.\n"; + + return -1; + } + else if (check_size != (unsigned int) dst_size) + { + #ifdef PANIC + *logofs << "UnpackAlpha: PANIC! Size mismatch in alpha data. " + << "Resulting size is " << check_size << " with " + << "expected size " << dst_size << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Size mismatch in alpha data. " + << "Resulting size is " << check_size << " with " + << "expected size " << dst_size << ".\n"; + + return -1; + } + + #ifdef TEST + *logofs << "UnpackAlpha: Decompressed " << src_size - 1 + << " bytes to " << dst_size << " bytes of alpha data.\n" + << logofs_flush; + #endif + + return 1; +} + +int UnpackAlpha(T_alpha *alpha, unsigned char *dst_data, + int dst_size, int big_endian) +{ + unsigned int count = dst_size >> 2; + + unsigned int i; + + int shift; + + if (count != alpha -> entries) + { + #ifdef WARNING + *logofs << "UnpackAlpha: WARNING! Not applying the alpha with " + << count << " elements needed and " << alpha -> entries + << " available.\n" << logofs_flush; + #endif + + return 0; + } + + shift = (big_endian == 1 ? 0 : 3); + + for (i = 0; i < count; i++) + { + *(dst_data + shift) = *(alpha -> data + i); + + dst_data += 4; + } + + return 1; +} diff --git a/nxcomp/Alpha.h b/nxcomp/Alpha.h new file mode 100644 index 000000000..80620e1aa --- /dev/null +++ b/nxcomp/Alpha.h @@ -0,0 +1,27 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Alpha_H +#define Alpha_H + +int UnpackAlpha(unsigned char method, unsigned char *src_data, int src_size, + unsigned char *dst_data, int dst_size); + +int UnpackAlpha(T_alpha *alpha, unsigned char *dst_data, + int dst_size, int big_endian); + +#endif /* Aplha_H */ diff --git a/nxcomp/Auth.cpp b/nxcomp/Auth.cpp new file mode 100644 index 000000000..d8e999132 --- /dev/null +++ b/nxcomp/Auth.cpp @@ -0,0 +1,650 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Auth.h" + +#include "Misc.h" +#include "Control.h" +#include "Timestamp.h" +#include "Pipe.h" + +#define DEFAULT_STRING_LIMIT 512 + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Store the provided cookie as our 'fake' cookie, then +// read the 'real' cookie from the current X authority +// file. +// + +Auth::Auth(char *display, char *cookie) +{ + display_ = NULL; + + file_ = NULL; + + last_ = nullTimestamp(); + + fakeCookie_ = NULL; + realCookie_ = NULL; + + fakeData_ = NULL; + realData_ = NULL; + + dataSize_ = 0; + + generatedCookie_ = 0; + + if (display == NULL || *display == '\0' || cookie == NULL || + *cookie == '\0' || strlen(cookie) != 32) + { + #ifdef PANIC + *logofs << "Auth: PANIC! Can't create the X authorization data " + << "with cookie '" << cookie << "' and display '" + << display << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't create the X authorization data " + << "with cookie '" << cookie << "' and display '" + << display << "'.\n"; + + return; + } + + #ifdef TEST + *logofs << "Auth: Creating X authorization data with cookie '" + << cookie << "' and display '" << display << "'.\n" + << logofs_flush; + #endif + + // + // Get a local copy of all parameters. + // + + display_ = new char[strlen(display) + 1]; + file_ = new char[DEFAULT_STRING_LIMIT]; + + fakeCookie_ = new char[strlen(cookie) + 1]; + realCookie_ = new char[DEFAULT_STRING_LIMIT]; + + if (display_ == NULL || file_ == NULL || + fakeCookie_ == NULL || realCookie_ == NULL) + { + #ifdef PANIC + *logofs << "Auth: PANIC! Cannot allocate memory for the X " + << "authorization data.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot allocate memory for the X " + << "authorization data.\n"; + + return; + } + + strcpy(display_, display); + + *file_ = '\0'; + + strcpy(fakeCookie_, cookie); + + *realCookie_ = '\0'; + + // + // Get the real cookie from the authorization file. + // + + updateCookie(); +} + +Auth::~Auth() +{ + delete [] display_; + delete [] file_; + + delete [] fakeCookie_; + delete [] realCookie_; + + delete [] fakeData_; + delete [] realData_; +} + +// +// At the present moment the cookie is read only once, +// at the time the instance is initialized. If the auth +// file changes along the life of the session, the old +// cookie will be used. This works with X servers beca- +// use of an undocumented "feature". See nx-X11. +// + +int Auth::updateCookie() +{ + if (isTimestamp(last_) == 0) + { + #ifdef TEST + *logofs << "Auth: Reading the X authorization file " + << "with last update at " << strMsTimestamp(last_) + << ".\n" << logofs_flush; + #endif + + if (getCookie() == 1 && validateCookie() == 1) + { + // + // It should rather be the modification time + // the auth file, so we can read it again if + // the file is changed. + // + + #ifdef TEST + *logofs << "Auth: Setting last X authorization file " + << "update at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + last_ = getTimestamp(); + + return 1; + } + + #ifdef PANIC + *logofs << "Auth: PANIC! Cannot read the cookie from the X " + << "authorization file.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot read the cookie from the X " + << "authorization file.\n"; + + return -1; + } + + #ifdef TEST + *logofs << "Auth: WARNING! Skipping check on the X " + << "authorization file.\n" << logofs_flush; + #endif + + return 0; +} + +int Auth::getCookie() +{ + // + // Check the name of the auth file that we are going to use. + // It can be either the value of the XAUTHORITY environment + // or the default .Xauthority file in the user's home. + // + + char *environment; + + environment = getenv("XAUTHORITY"); + + if (environment != NULL && *environment != '\0') + { + strncpy(file_, environment, DEFAULT_STRING_LIMIT - 1); + } + else + { + snprintf(file_, DEFAULT_STRING_LIMIT - 1, "%s/.Xauthority", + control -> HomePath); + } + + *(file_ + DEFAULT_STRING_LIMIT - 1) = '\0'; + + #ifdef TEST + *logofs << "Auth: Using X authorization file '" << file_ + << "'.\n" << logofs_flush; + #endif + + // + // Use the nxauth command on Windows and the Mac, xauth + // on all the other platforms. On Windows and on the Mac + // we assume that the nxauth command is located under + // bin in the client installation directory. On all the + // other platforms we use the default xauth command that + // is in our path. + // + + char command[DEFAULT_STRING_LIMIT]; + + #if defined(__CYGWIN32__) || defined(__APPLE__) + + snprintf(command, DEFAULT_STRING_LIMIT - 1, + "%s/bin/nxauth", control -> SystemPath); + + *(command + DEFAULT_STRING_LIMIT - 1) = '\0'; + + #else + + strcpy(command, "xauth"); + + #endif + + #ifdef TEST + *logofs << "Auth: Using X auth command '" << command + << "'.\n" << logofs_flush; + #endif + + // + // The SSH code forces using the unix:n port when passing localhost:n. + // This is probably because localhost:n can fail to return a valid + // entry on machines where the hostname for localhost doesn't match + // exactly the 'localhost' string. For example, on a freshly installed + // Fedora Core 3 I get a 'localhost.localdomain/unix:0' entry. Query- + // ing 'xauth list localhost:0' results in an empty result, while the + // query 'xauth list unix:0' works as expected. Note anyway that if + // the cookie for the TCP connection on 'localhost' is set to a dif- + // ferent cookie than the one for the Unix connections, both SSH and + // NX will match the wrong cookie and session will fail. + // + + char line[DEFAULT_STRING_LIMIT]; + + if (strncmp(display_, "localhost:", 10) == 0) + { + snprintf(line, DEFAULT_STRING_LIMIT, "unix:%s", display_ + 10); + } + else + { + snprintf(line, DEFAULT_STRING_LIMIT, "%.200s", display_); + } + + const char *parameters[256]; + + parameters[0] = command; + parameters[1] = command; + parameters[2] = "-f"; + parameters[3] = file_; + parameters[4] = "list"; + parameters[5] = line; + parameters[6] = NULL; + + #ifdef TEST + *logofs << "Auth: Executing command "; + + for (int i = 0; i < 256 && parameters[i] != NULL; i++) + { + *logofs << "[" << parameters[i] << "]"; + } + + *logofs << ".\n" << logofs_flush; + #endif + + // + // Use the popen() function to read the result + // of the command. We would better use our own + // implementation. + // + + FILE *data = Popen((char *const *) parameters, "r"); + + int result = -1; + + if (data == NULL) + { + #ifdef PANIC + *logofs << "Auth: PANIC! Failed to execute the X auth command.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failed to execute the X auth command.\n"; + + goto AuthGetCookieResult; + } + + if (fgets(line, DEFAULT_STRING_LIMIT, data) == NULL) + { + #ifdef WARNING + *logofs << "Auth: WARNING! Failed to read data from the X " + << "auth command.\n" << logofs_flush; + #endif + + #ifdef TEST + cerr << "Warning" << ": Failed to read data from the X " + << "auth command.\n"; + #endif + + #ifdef PANIC + *logofs << "Auth: WARNING! Generating a fake cookie for " + << "X authentication.\n" << logofs_flush; + #endif + + #ifdef TEST + cerr << "Warning" << ": Generating a fake cookie for " + << "X authentication.\n"; + #endif + + generateCookie(realCookie_); + } + else + { + #ifdef TEST + *logofs << "Auth: Checking cookie in string '" << line + << "'.\n" << logofs_flush; + #endif + + // + // Skip the hostname in the authority entry + // just in case it includes some white spaces. + // + + char *cookie = NULL; + + cookie = index(line, ':'); + + if (cookie == NULL) + { + cookie = line; + } + + if (sscanf(cookie, "%*s %*s %511s", realCookie_) != 1) + { + #ifdef PANIC + *logofs << "Auth: PANIC! Failed to identify the cookie " + << "in string '" << line << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failed to identify the cookie " + << "in string '" << line << "'.\n"; + + goto AuthGetCookieResult; + } + + #ifdef TEST + *logofs << "Auth: Got cookie '" << realCookie_ + << "' from file '" << file_ << "'.\n" + << logofs_flush; + #endif + } + + result = 1; + +AuthGetCookieResult: + + if (data != NULL) + { + Pclose(data); + } + + return result; +} + +int Auth::validateCookie() +{ + unsigned int length = strlen(realCookie_); + + if (length > DEFAULT_STRING_LIMIT / 2 - 1 || + strlen(fakeCookie_) != length) + { + #ifdef PANIC + *logofs << "Auth: PANIC! Size mismatch between cookies '" + << realCookie_ << "' and '" << fakeCookie_ << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Size mismatch between cookies '" + << realCookie_ << "' and '" << fakeCookie_ << "'.\n"; + + goto AuthValidateCookieError; + } + + // + // The length of the resulting data will be + // half the size of the Hex cookie. + // + + length = length / 2; + + fakeData_ = new char[length]; + realData_ = new char[length]; + + if (fakeData_ == NULL || realData_ == NULL) + { + #ifdef PANIC + *logofs << "Auth: PANIC! Cannot allocate memory for the binary X " + << "authorization data.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot allocate memory for the binary X " + << "authorization data.\n"; + + goto AuthValidateCookieError; + } + + // + // Translate the real cookie from Hex data + // to its binary representation. + // + + unsigned int value; + + for (unsigned int i = 0; i < length; i++) + { + if (sscanf(realCookie_ + 2 * i, "%2x", &value) != 1) + { + #ifdef PANIC + *logofs << "Auth: PANIC! Bad X authorization data in real " + << "cookie '" << realCookie_ << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Bad X authorization data in real cookie '" + << realCookie_ << "'.\n"; + + goto AuthValidateCookieError; + } + + realData_[i] = value; + + if (sscanf(fakeCookie_ + 2 * i, "%2x", &value) != 1) + { + #ifdef PANIC + *logofs << "Auth: PANIC! Bad X authorization data in fake " + << "cookie '" << fakeCookie_ << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Bad X authorization data in fake cookie '" + << fakeCookie_ << "'.\n"; + + goto AuthValidateCookieError; + } + + fakeData_[i] = value; + } + + dataSize_ = length; + + #ifdef TEST + *logofs << "Auth: Validated real cookie '" + << realCookie_ << "' and fake cookie '" << fakeCookie_ + << "' with data with size " << dataSize_ << ".\n" + << logofs_flush; + + *logofs << "Auth: Ready to accept incoming connections.\n" + << logofs_flush; + #endif + + return 1; + +AuthValidateCookieError: + + delete [] fakeData_; + delete [] realData_; + + fakeData_ = NULL; + realData_ = NULL; + + dataSize_ = 0; + + return -1; +} + +int Auth::checkCookie(unsigned char *buffer) +{ + if (isValid() != 1) + { + #ifdef PANIC + *logofs << "Auth: PANIC! Attempt to check the X cookie with " + << "invalid authorization data.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Attempt to check the X cookie with " + << "invalid authorization data.\n"; + + return -1; + } + + const char *protoName = "MIT-MAGIC-COOKIE-1"; + int protoSize = strlen(protoName); + + int matchedProtoSize; + int matchedDataSize; + + if (buffer[0] == 0x42) + { + // + // Byte order is MSB first. + // + + matchedProtoSize = 256 * buffer[6] + buffer[7]; + matchedDataSize = 256 * buffer[8] + buffer[9]; + } + else if (buffer[0] == 0x6c) + { + // + // Byte order is LSB first. + // + + matchedProtoSize = buffer[6] + 256 * buffer[7]; + matchedDataSize = buffer[8] + 256 * buffer[9]; + } + else + { + #ifdef WARNING + *logofs << "Auth: WARNING! Bad X connection data in the buffer.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Bad X connection data in the buffer.\n"; + + return -1; + } + + // + // Check if both the authentication protocol + // and the fake cookie match our data. + // + + int protoOffset = 12; + + #ifdef TEST + *logofs << "Auth: Received a protocol size of " + << matchedProtoSize << " bytes.\n" + << logofs_flush; + #endif + + if (matchedProtoSize != protoSize || + memcmp(buffer + protoOffset, protoName, protoSize) != 0) + { + #ifdef WARNING + *logofs << "Auth: WARNING! Protocol mismatch or no X " + << "authentication data.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Protocol mismatch or no X " + << "authentication data.\n"; + + return -1; + } + + int dataOffset = protoOffset + ((matchedProtoSize + 3) & ~3); + + #ifdef TEST + *logofs << "Auth: Received a data size of " + << matchedDataSize << " bytes.\n" + << logofs_flush; + #endif + + if (matchedDataSize != dataSize_ || + memcmp(buffer + dataOffset, fakeData_, dataSize_) != 0) + { + #ifdef WARNING + *logofs << "Auth: WARNING! Cookie mismatch in the X " + << "authentication data.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Cookie mismatch in the X " + << "authentication data.\n"; + + return -1; + } + + // + // Everything is OK. Replace the fake data. + // + + #ifdef TEST + *logofs << "Auth: Replacing fake X authentication data " + << "with the real data.\n" << logofs_flush; + #endif + + memcpy(buffer + dataOffset, realData_, dataSize_); + + return 1; +} + +void Auth::generateCookie(char *cookie) +{ + // + // Code is from the SSH implementation, except that + // we use a much weaker random number generator. + // This is not critical, anyway, as this is just a + // fake cookie. The X server doesn't have a cookie + // for the display, so it will ignore the value we + // feed to it. + // + + T_timestamp timer = getTimestamp(); + + srand((unsigned int) timer.tv_usec); + + unsigned int data = rand(); + + for (int i = 0; i < 16; i++) + { + if (i % 4 == 0) + { + data = rand(); + } + + snprintf(cookie + 2 * i, 3, "%02x", data & 0xff); + + data >>= 8; + } + + generatedCookie_ = 1; + + #ifdef TEST + *logofs << "Auth: Generated X cookie string '" + << cookie << "'.\n" << logofs_flush; + #endif +} diff --git a/nxcomp/Auth.h b/nxcomp/Auth.h new file mode 100644 index 000000000..aac1d1e3f --- /dev/null +++ b/nxcomp/Auth.h @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Auth_H +#define Auth_H + +#include "Timestamp.h" + +// +// Handle the forwarding of authorization credentials +// to the X server by replacing the fake cookie with +// the real cookie as it is read from the auth file. +// At the moment only the MIT-MAGIC-COOKIE-1 cookies +// are recognized. The implementation is based on the +// corresponding code found in the SSH client. +// + +class Auth +{ + public: + + // + // Must be created by passing the fake cookie that + // will be forwarded by the remote end and with the + // real X display that is going to be used for the + // session. + // + + Auth(char *display, char *cookie); + + ~Auth(); + + int isValid() + { + return (isTimestamp(last_) == 1 && fakeCookie_ != NULL && + *fakeCookie_ != '\0' && realCookie_ != NULL && + *realCookie_ != '\0' && fakeData_ != NULL && + realData_ != NULL && dataSize_ != 0); + } + + int isFake() const + { + return generatedCookie_; + } + + // + // Method called in the channel class to find if the + // provided cookie matches the fake one. If the data + // matches, the fake cookie is replaced with the real + // one. + // + + int checkCookie(unsigned char *buffer); + + protected: + + // + // Update the real cookie for the display. If called + // a further time, check if the auth file is changed + // and get the new cookie. + // + + int updateCookie(); + + // + // Find out which authorization file is to be used + // and query the cookie for the current display. + // + + int getCookie(); + + // + // Extract the binary data from the cookies so that + // data can be directly compared at the time it is + // taken from the X request. + // + + int validateCookie(); + + // + // Generate a fake random cookie and copy it to the + // provided string. + // + + void generateCookie(char *cookie); + + private: + + char *display_; + char *file_; + + T_timestamp last_; + + char *fakeCookie_; + char *realCookie_; + + char *fakeData_; + char *realData_; + + int dataSize_; + + int generatedCookie_; +}; + +#endif /* Auth_H */ diff --git a/nxcomp/Bitmap.cpp b/nxcomp/Bitmap.cpp new file mode 100644 index 000000000..b5bad226a --- /dev/null +++ b/nxcomp/Bitmap.cpp @@ -0,0 +1,106 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Misc.h" +#include "Bitmap.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +int UnpackBitmap(T_geometry *geometry, unsigned char method, unsigned char *src_data, + int src_size, int dst_bpp, int dst_width, int dst_height, + unsigned char *dst_data, int dst_size) +{ + if (dst_bpp != 32) + { + #ifdef TEST + *logofs << "UnpackBitmap: Nothing to do with " + << "image of " << dst_bpp << " bits per plane " + << "and size " << src_size << ".\n" + << logofs_flush; + #endif + + if (src_size != dst_size) + { + #ifdef PANIC + *logofs << "UnpackBitmap: PANIC! Size mismatch with " + << src_size << " bytes in the source and " + << dst_size << " in the destination.\n" + << logofs_flush; + #endif + + return -1; + } + + memcpy(dst_data, src_data, src_size); + + return 1; + } + else if (src_size != dst_width * dst_height * 3 || + dst_size != dst_width * dst_height * 4) + { + #ifdef PANIC + *logofs << "UnpackBitmap: PANIC! Size mismatch with " + << src_size << " bytes in the source and " + << dst_size << " in the destination.\n" + << logofs_flush; + #endif + + return -1; + } + + /* + * Insert the 4th byte in the bitmap. + */ + + unsigned char *next_src = src_data; + unsigned char *next_dst = dst_data; + + if (geometry -> image_byte_order == LSBFirst) + { + while (next_src < src_data + src_size) + { + *next_dst++ = *next_src++; + *next_dst++ = *next_src++; + *next_dst++ = *next_src++; + + next_dst++; + } + } + else + { + while (next_src < src_data + src_size) + { + next_dst++; + + *next_dst++ = *next_src++; + *next_dst++ = *next_src++; + *next_dst++ = *next_src++; + } + } + + #ifdef TEST + *logofs << "UnpackBitmap: Unpacked " << src_size + << " bytes to a buffer of " << dst_size + << " with " << dst_bpp << " bits per plane.\n" + << logofs_flush; + #endif + + return 1; +} diff --git a/nxcomp/Bitmap.h b/nxcomp/Bitmap.h new file mode 100644 index 000000000..57a1b35bb --- /dev/null +++ b/nxcomp/Bitmap.h @@ -0,0 +1,28 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Bitmap_H +#define Bitmap_H + +#include "Unpack.h" + +int UnpackBitmap(T_geometry *geometry, unsigned char method, + unsigned char *src_data, int src_size, int dst_bpp, + int dst_width, int dst_height, unsigned char *dst_data, + int dst_size); + +#endif /* Bitmap_H */ diff --git a/nxcomp/BlockCache.cpp b/nxcomp/BlockCache.cpp new file mode 100644 index 000000000..f885290f0 --- /dev/null +++ b/nxcomp/BlockCache.cpp @@ -0,0 +1,69 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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/BlockCache.h b/nxcomp/BlockCache.h new file mode 100644 index 000000000..b9146ceea --- /dev/null +++ b/nxcomp/BlockCache.h @@ -0,0 +1,59 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef BlockCache_H +#define BlockCache_H + + +// Cache to hold an arbitrary-length block of bytes + +class BlockCache +{ + public: + BlockCache():buffer_(0), size_(0), checksum_(0) + { + } + ~BlockCache() + { + delete[]buffer_; + } + int compare(unsigned int size, const unsigned char *data, + int overwrite = 1); + void set(unsigned int size, const unsigned char *data); + + unsigned int getLength() const + { + return size_; + } + unsigned int getChecksum() const + { + return checksum_; + } + const unsigned char *getData() const + { + return buffer_; + } + + static unsigned int checksum(unsigned int size, const unsigned char *data); + +private: + unsigned char *buffer_; + unsigned int size_; + unsigned int checksum_; +}; + +#endif /* BlockCache_H */ diff --git a/nxcomp/BlockCacheSet.cpp b/nxcomp/BlockCacheSet.cpp new file mode 100644 index 000000000..8959ba2b3 --- /dev/null +++ b/nxcomp/BlockCacheSet.cpp @@ -0,0 +1,135 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Misc.h" +#include "BlockCacheSet.h" + + +BlockCacheSet::BlockCacheSet(unsigned int numCaches): + caches_(new BlockCache *[numCaches]), size_(numCaches), + length_(0) +{ + for (unsigned int i = 0; i < numCaches; i++) + caches_[i] = new BlockCache(); +} + + +BlockCacheSet::~BlockCacheSet() +{ + // + // TODO: There is still a strange segfault occurring + // at random time under Cygwin, when proxy is being + // shutdown. Problem appeared just after upgrading + // to the latest version of the Cygwin DLL. A stack + // trace, obtained at the last minute, reveals that + // failure happens in this destructor. + // + + #ifndef __CYGWIN32__ + + for (unsigned int i = 0; i < size_; i++) + delete caches_[i]; + delete[]caches_; + + #endif /* ifdef __CYGWIN32__ */ +} + + +int +BlockCacheSet::lookup(unsigned int dataLength, const unsigned char *data, + unsigned int &index) +{ + unsigned int checksum = BlockCache::checksum(dataLength, data); + for (unsigned int i = 0; i < length_; i++) + if ((caches_[i]->getChecksum() == checksum) && + (caches_[i]->compare(dataLength, data, 0))) + { + // match + index = i; + if (i) + { + BlockCache *save = caches_[i]; + unsigned int target = (i >> 1); + do + { + caches_[i] = caches_[i - 1]; + i--; + } + while (i > target); + caches_[target] = save; + } + return 1; + } + // no match + unsigned int insertionPoint = (length_ >> 1); + unsigned int start; + if (length_ >= size_) + start = size_ - 1; + else + { + start = length_; + length_++; + } + BlockCache *save = caches_[start]; + for (unsigned int k = start; k > insertionPoint; k--) + caches_[k] = caches_[k - 1]; + caches_[insertionPoint] = save; + save->set(dataLength, data); + return 0; +} + + +void +BlockCacheSet::get(unsigned index, unsigned int &size, + const unsigned char *&data) +{ + size = caches_[index]->getLength(); + data = caches_[index]->getData(); + if (index) + { + BlockCache *save = caches_[index]; + unsigned int target = (index >> 1); + do + { + caches_[index] = caches_[index - 1]; + index--; + } + while (index > target); + caches_[target] = save; + } +} + + + +void +BlockCacheSet::set(unsigned int dataLength, const unsigned char *data) +{ + unsigned int insertionPoint = (length_ >> 1); + unsigned int start; + if (length_ >= size_) + start = size_ - 1; + else + { + start = length_; + length_++; + } + BlockCache *save = caches_[start]; + for (unsigned int k = start; k > insertionPoint; k--) + caches_[k] = caches_[k - 1]; + caches_[insertionPoint] = save; + save->set(dataLength, data); +} diff --git a/nxcomp/BlockCacheSet.h b/nxcomp/BlockCacheSet.h new file mode 100644 index 000000000..e27b18088 --- /dev/null +++ b/nxcomp/BlockCacheSet.h @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef BlockCacheSet_H +#define BlockCacheSet_H + +#include "BlockCache.h" + + +class BlockCacheSet +{ + public: + BlockCacheSet(unsigned int numCaches); + ~BlockCacheSet(); + + int lookup(unsigned int size, const unsigned char *data, + unsigned int &index); + void get(unsigned int index, unsigned int &size, const unsigned char *&data); + void set(unsigned int size, const unsigned char *data); + + private: + BlockCache ** caches_; + unsigned int size_; + unsigned int length_; +}; + +#endif /* BlockCacheSet_H */ diff --git a/nxcomp/CHANGELOG b/nxcomp/CHANGELOG new file mode 100644 index 000000000..b7ef0d97a --- /dev/null +++ b/nxcomp/CHANGELOG @@ -0,0 +1,3778 @@ +ChangeLog: + +nxcomp-3.5.0-2 + +- Fixed TR11H02398. Solved a race condition when creating channels. + +nxcomp-3.5.0-1 + +- Opened the 3.5.0 branch based on nxcomp-3.4.0-7. + +- Updated copyright to year 2011. + +nxcomp-3.4.0-7 + +- Fixed TR03H02334. Modified the UNIX domain socket checks on MacOSX + to be compliant with the standard introduced in OSX 10.6.3. + +nxcomp-3.4.0-6 + +- Solved compilation problems on Solaris. + +nxcomp-3.4.0-5 + +- Solved compilation problems on GCC 4.4. + +nxcomp-3.4.0-4 + +- Added reference to fixed TR02H02325. + +nxcomp-3.4.0-3 + +- Updated copyright to year 2010. + +nxcomp-3.4.0-2 + +- Fixed TR03G02204. Changed the parsing of X authority entries in + order to handle the case where the hostname includes white spaces. + +- Fixed TR02H02325. Bug in PNG decompression on 16bpp displays. + +nxcomp-3.4.0-1 + +- Opened the 3.4.0 branch based on nxcomp-3.3.0-4. + +- Changed version number. + +- Updated copyright to year 2009. + +nxcomp-3.3.0-4 + +- Check if the variable storing the ping time exceeded the maximum + integer value. + +- Recover incorrect sequence number when the proxy is not connected + to an agent. + +nxcomp-3.3.0-3 + +- Removed a condition in ClientChannel that caused a loss in event + sequence numbers. + +nxcomp-3.3.0-2 + +- Updated VERSION. + +nxcomp-3.3.0-1 + +- Opened the 3.3.0 branch based on nxcomp-3.2.0-7. + +nxcomp-3.2.0-7 + +- Solved a compilation problem on GCC 4.3. + +nxcomp-3.2.0-6 + +- Changes considering that unsetenv() returns void on Mac OS X. + +nxcomp-3.2.0-5 + +- Fixed TR03F02024. Assume the launchd unix socket as X socket. + +- Changed the authorization cookie retrieval when using the launchd + socket on MacOSX, in order to wait for the X server start. + +nxcomp-3.2.0-4 + +- Fixed TR03F02026. Unset environment variable LD_LIBRARY_PATH before + calling the exec function running nxclient. + +nxcomp-3.2.0-3 + +- Fix addMsTimestamp() and subMsTimestamp(). Remove the check for + a valid input in diffTimestamp() and diffUsTimestamp(). + +nxcomp-3.2.0-2 + +- Fixed TR12E01973. Now the correct number of bits are used for the + number of points in a X_FillPoly request. + +nxcomp-3.2.0-1 + +- Opened the 3.2.0 branch based on nxcomp-3.1.0-6. + +nxcomp-3.1.0-6 + +- Always use a timeout of 50 ms to update the congestion counter. + +nxcomp-3.1.0-5 + +- Solve the possible deadlock caused by both proxies running out of + tokens at the same time. + +- In ServerProxy::handleCheckDrop() copy the list since the function + can delete the elements. + +nxcomp-3.1.0-4 + +- Classes ProxyReadBuffer and ServerReadBuffer returned an invalid + number of characters to read in the case of a readable() failure. + +- Connections to proxy versions newer than 3.0.0 were not accepted. + +- Reading options from file could fail if options contained spaces + or other characters interrupting the fscanf()'s matching of input. + +- The proxy link could not be flushed if a ping request was replied + after the end of the read loop. + +nxcomp-3.1.0-3 + +- Fixed a compilation warning on GCC 2.95. + +nxcomp-3.1.0-2 + +- Solved TR09E01852. Modified the encoding of the number of rects + in the XSetClipRectangles request. + +nxcomp-3.1.0-1 + +- Opened the 3.1.0 branch based on nxcomp-3.0.0-46. + +nxcomp-3.0.0-46 + +- Fixed TR09E01865 and TR08E01831. Assume the old behavior of compr- + essing the CUPS, SMB, HTTP and Font channels when connected to an + old proxy version. + +nxcomp-3.0.0-45 + +- Retry the fork() 30 times, but only on Cygwin (Sob!). + +nxcomp-3.0.0-44 + +- In the case of an error, try again to fork() after a second, for + a maximum of 5 times. This seems to constitute a valid workaround + to the intermittent fork() failures observed on Windows. + +- Fixed NXTransDestroy() to show the 'broken connection' dialog in + the case of a link failure on the client. + +- Handled the 'terminating' and 'terminated' messages as new proxy + stages. + +- Force all the directories and files created by the library to be + readable only by the user. + +- Don't complain if the shared memory segment can't be initialized + on Windows. + +nxcomp-3.0.0-43 + +- Prevent the reallocation of the read buffer in the proxy and the + agent transport if the buffer was borrowed by an upper layer. + +- Let the house-keeping process remove the cache directories that + are empty and have not be used since more than 30 days. + +- Added the missing destruction of the ZLIB stream used for image + decompression at proxy shutdown. + +nxcomp-3.0.0-42 + +- In handleFastWriteReply(), moved the initialization of the 'next' + pointer inside the '#ifndef __sun' block. This should fix the re- + maining encoding problems on SPARCs. + +nxcomp-3.0.0-41 + +- VMWare virtual machines can have the system timer deadly broken. + In the case of timeout, try to send a ping regardless we are the + client or the server proxy to force a write by the remote. + +- Removed the messages 'Info: Using image cache parameters...' and + 'Info: Using image streaming parameters...' from the session log + if the image cache is not enabled. + +nxcomp-3.0.0-40 + +- Removed the messages 'Info: Shutting down the NX transport' and + 'Info: End of NX transport requested by...' at the session tear + down. + +- Removed the remaining log output. + +nxcomp-3.0.0-39 + +- Don't reset the timer on proxy shutdown. + +nxcomp-3.0.0-38 + +- Don't reinstall the signal handler if the signal was enabled and + the NXTransSignal() action is NX_SIGNAL_FORWARD. + +nxcomp-3.0.0-37 + +- Return a congestion level 9 if the agent channel is in congestion + state. + +- Set the idle timeout (used to update the congestion state) based + on the link type. The timeout is 0 ms with link LAN and 50 ms in + the case of MODEM, with the other values in the between. + +nxcomp-3.0.0-36 + +- Changed the encoding of the RenderCompositeGlyphs requests. Use + a separate cache for X and Y. Send a single bit for offset X and + offset Y if they match the source coordinates. + +- Always use the memcpy() in the handleFastWrite*() on Sun, as the + alternative code doesn't work on SPARC if the buffer is not word + aligned. + +nxcomp-3.0.0-35 + +- Ensure a smooth transition between different congestion states. + If the current congestion counter is greater than the previous, + ramp up the value quickly by taking the current counter. If the + new congestion counter is less than the previous, ramp down the + reported value slowly by calculating the average of the last 8 + updates. Reset the congestion counter to 0 if no token reply is + pending and the proxy is idle until a timeout. + +nxcomp-3.0.0-34 + +- Calculate the current congestion as the average of the last 3 + frames sent. + +nxcomp-3.0.0-33 + +- When sending a X_RenderCompositeGlyph update, send a single bit + if the source X and Y match the values in cache. + +- Save the check on the bytes readable from the proxy descriptor + after the select. + +nxcomp-3.0.0-32 + +- Send the X_GetImage replies uncompressed to better leverage the + stream compression. + +- Before entering in congestion state, force a read to see whether + the remote proxy aborted the connection. + +nxcomp-3.0.0-31 + +- Try to avoid using memcpy() in the channels' handleFastWrite*(). + +- Changed the copyright notices at the beginning of the files that + were referring to NXPROXY to refer to NXCOMP. + +nxcomp-3.0.0-30 + +- Set the agent and proxy descriptors as ready, when appropriate, + also in the case of an interrupt. + +nxcomp-3.0.0-29 + +- On the X server side, read more events from the X server just be- + fore and just after having written some data. On the X client si- + de, apply the same to the proxy link. + +- X11 channels originate a congestion control code as soon as the- + re is some data left from the previous write. + +nxcomp-3.0.0-28 + +- Displaced automatically the "channel unresponsive" and "no data + received from remote" dialogs if the error condition is ceased. + +- Rewritten the channel drain procedure. + +- Tried a different approach to the channel congestion handling. + The X11 channels are not drained after a blocking write but only + if a further write is requested and the channel is still in con- + gestion state. Benchmarks show that this is still sub-optimal, + compared to sending the congestion code immediately also for X11 + channels. See the next version. + +nxcomp-3.0.0-27 + +- Ignored the agent option 'defer'. + +- Dropped support for the 1.4.0 versions. + +- More fixes to support the 1.5.0. + +nxcomp-3.0.0-26 + +- The channel read buffer can inherit the data to be encoded from + the caller, instead of taking it from the transport. This is le- + veraged by the agent channel to save the copy. + +- Ignored the agent option 'clients'. + +- Removed the deprecated code checking the CPU limits. + +- Removed the counters that were duplicated in the Statistics and + the Control classes. + +nxcomp-3.0.0-25 + +- Improved the effectiveness of the caching of the RenderComposite- + Glyphs by using a differential encoding for the length, delta x + and delta y of the first string. + +- Better handle the compatibility between different formats of the + cache files. + +nxcomp-3.0.0-24 + +- Fixed the compatibility problem with the 1.5.0 versions. + +- Removed the constants NXDisplayReadable, NXDisplayFlushable and + NXDisplayCongestion. Now nxcompext has three distinct functions. + +nxcomp-3.0.0-23 + +- Removed support for Rdp, Tight and Hextile packed images decod- + ing since they have been made obsolete by the new NX server. + +- Use the value 0 for the action is_hit and 1 for is_added. This + increases the frequency of zeroes in the encode buffer, giving + better compression. + +- Changed the copyright attribution from Medialogic to NoMachine. + +nxcomp-3.0.0-22 + +- Implemented an alternate encoding reducing the size of the int + caches to 16 elements and transmitting the values in blocks of + 4 bits. Although this encoding reduces the effectiveness of + the differential compression, it allows better compression of + the final stream, resulting in a 10% to 20% gain in all condi- + tions. The new encoding is experimental, as it would make the + 3.0.0 incompatible with all the previous NX versions. Use this + package as a reference for future implementations. + +nxcomp-3.0.0-21 + +- Encode the first two coordinates of a FillPoly message by using + a differential encoding, thus the origin of the polygon is not + included in the checksum. This is aimed to work in combination + with the agent converting all the FillPoly requests to the rela- + tive coordinate mode. + +- Add the coordinate mode to the checksum of the PolyPoint and Po- + lyLine messages so that it doesn't need to be encoded separately. + +- Disable the streaming for all messages. This saves the encoding + of a boolean for each message that could potentially be streamed. + +- Don't try to match the block size when encoding a value based on + an IntCache. This saves the additional bool in 90% of the cases. + +- Removed support for the former RDP and RFB session types. + +- Due to the above changes, this version is incompatible with the + previous 3.0.0 releases. + +nxcomp-3.0.0-20 + +- Added support for the 'lossy', 'lossless' and 'adaptive' pack me- + thod literals. These values activate the dynamic selection of the + pack method by the agent. + +- Made the 'adaptive' pack method the default for all link types. + +- The default stream compression level is now 6 for link type ISDN, + 4 for ADSL and 1 for WAN. The default data compression level is + always 1, except for LAN that is 0. + +nxcomp-3.0.0-19 + +- Print in the session log the X11 display that the proxy is imper- + sonating or where it is forwarding the connections. + +nxcomp-3.0.0-18 + +- Implemented decoding of the PACK_BITMAP_16M_COLORS pack method. + This is a very simple encoder removing the 4th byte in 32 bits- + per-plane images. This encoder is intended to better leverage + the stream compression on low bandwidth links. + +- Removed the code cleaning the padding bytes in RenderAddGlyphs + and RenderCompositeGlyphs as the operation is now performed in + Xrender. + +- Removed the "Info: Synchronizing local and remote caches." and + "Info: Using remote server '...'." lines from the session log. + +nxcomp-3.0.0-17 + +- Determine the CPU endianess at compile time only on Linux and + Cygwin. + +nxcomp-3.0.0-16 + +- Optimized the memory allocation of the ReadBuffer classes. The + buffers from the Transport classes are used, when possible, to + avoid the copy. + +nxcomp-3.0.0-15 + +- Reworked the code dealing with the channel congestion control + messages for increased efficiency. + +- Removed the pending_ flag from the proxy and channel class mem- + bers. If needed, the classes will query the transport buffers + to see whether there is data pending. + +- Removed the additional parameter from NXTransFlush(). + +- The proxy doesn't issue anymore a sync control message on a X + reply. Anyway it will respond to the request for compatibility + with older versions. + +- Removed the dynamic selection of the encoding method for the + X_QueryColors request and X_ListFonts, X_QueryColors, X_Query- + Font replies. Now all of them use the differential encoding to + better leverage the stream compression. + +- Fixed to build on SUNs where we don't have endian.h. + +nxcomp-3.0.0-14 + +- When handling partial proxy messages, don't try to determine how + many bytes still need to be read if the ZLIB stream compression + is enabled. + +- If a message can't be located, the ReadBuffer class checks if it + is possible to consume the data already in the transport buffer. + +- Optimize the MD5 routines by determining the CPU endianess at + compile time. + +- Force Drawable and GContext to be CARD32 in NXproto.h. + +nxcomp-3.0.0-13 + +- Changed the semantic of the NXDisplayFlushHandler callback. The + function is called when new data is sent to the remote proxy or, + with a 0 length, upon receiving a new token reply, so that the + agent can update its congestion state. + +- Added the NXTransCongestion() function. It returns a value bet- + ween 0 and 9 indicating the congestion level based on the tokens + remaining. A value of 9 means that the link is congested and no + further data can be sent. + +- Removed the congestion and synchronization callbacks. They are + superseded by the new NXTransCongestion() implementation. + +nxcomp-3.0.0-12 + +- Send the token replies at the end of the encode loop instead of + flushing the buffer immediately. + +- Fixed TR05E01687. Correctly detect CTRL-ALT-SHIFT-ESC keystrokes. + +- Renamed the option 'block' as 'tile'. + +- Ignored the option 'menu', targeting the X11 agent. + +nxcomp-3.0.0-11 + +- Removed the 'interactive' attribute from generic channels. We now + use the 'prioritized' attribute which yields the same result. + +- Simplified the channels' read loop by always reading only once. + +- The channels flush the link as soon as the token is exceeded even + if the flush policy is set to deferred. + +- Reserved more cache memory for the images when running a shadow + session. + +nxcomp-3.0.0-10 + +- Added the NXDisplayWriteHandler callback definition. + +- Removed the code that forced the proxy to retain in the cache the + images referenced at session startup. + +- Disabled the persistent image cache since this is not supported + by the current agent. This will save the overhead caused by the + image house-keeping process. + +nxcomp-3.0.0-9 + +- Fixed a cast problem in GetBytesReadable(). + +nxcomp-3.0.0-8 + +- Added the NXTransTime() utility. It return the time in milliseconds + elapsed since the last call to the same function. Useful for bench- + marking the Xlib layer. + +- Ignored the option 'block' targeting the X11 agent. + +nxcomp-3.0.0-7 + +- Ignored the options 'shadow', 'shadowmode' targeting the X11 agent. + +- Added the session type 'shadow'. + +nxcomp-3.0.0-6 + +- Fixed TR08D01484. Fixed a bug in the cleanup of the RenderAddGlyphs + messages. We clean up only the glyphs with depth 8, width greater + than 1 and pixmap byte pad 4. + +- Added file COPYING. + +nxcomp-3.0.0-5 + +- Updated the copyright notices to the current year. + +nxcomp-3.0.0-4 + +- Imported changes up to nxcomp-2.1.0-6. + +- Fixed TR12D01563. Changed the configure script to always build the + library with the -fPIC option. This is related to the way SELinux + works. + +- Fixed the dependency from local endianess for 16-bit color RDP un- + packing functions. This caused wrong colors to be displayed when the + proxy and the X server were running on two separate machines having + different endianess. + +- Fixed TR10D01523. The Channel::handleDrain() method now accounts + for the fact that agent connections can't be drained. + +- Changed the Tight and Hextile decoding functions to use the X ser- + ver's byte order. + +nxcomp-3.0.0-3 + +- Changed the main loop to handle the 3.0.0 version. + +nxcomp-3.0.0-2 + +- Updated the file VERSION. + +nxcomp-3.0.0-1 + +- Opened the 3.0.0 branch based on nxcomp-2.0.0-81. + +nxcomp-2.0.0-81 + +- Fixed the dependency from local endianess for JPEG and PNG decomp- + ression functions. This caused wrong colors to be displayed when + the proxy and the X server were running on two separate machines + having different endianess. + +- Fixed the number of entries in the handleColormap() function. + +nxcomp-2.0.0-80 + +- Fixed TR06D01404. Removed the warning issued when calling the + AgentTransport::drain() method. + +nxcomp-2.0.0-79 + +- Updated copyright to year 2006. + +nxcomp-2.0.0-78 + +- Fixed a bug in the cleanup of the RenderAddGlyphs messages. + +- Minor improvements to the encoding of the RenderCompositeGlyphs + and RenderComposite requests. + +- Added 'client' to the list of the ignored options. + +nxcomp-2.0.0-77 + +- Added cleanup of the padding bytes in the RenderCompositeGlyphs + and RenderAddGlyphs requests. + +nxcomp-2.0.0-76 + +- Before adding the frame's bytes to the tokens, the frame length + is normalized based on the stream compression ratio of the last + frame sent. This avoids entering the drain loop too early when + the data is highly compressible. + +- Moved the render messages' private data in a union to reduce the + footprint of the render store. + +nxcomp-2.0.0-75 + +- The action performed on the remote message store and the affected + position are encoded as a single value. This saves approximately + 1 bit per each message. + +- Created more Compat classes to handle the backward compatibility + with the 1.4.0 and 1.5.0 versions. + +nxcomp-2.0.0-74 + +- Added support for the RLE pack method and made it the default if + the selected link type is LAN. + +- Removed the legacy support for the 'render' option provided at X + server side. + +- When defining LAME in ClientChannel.h, the channel has the chance + of suppressing more opcodes by the handleTaintLameRequest() call. + This is for test purposes. + +nxcomp-2.0.0-73 + +- The X_NXSetUnpackColormap and X_NXSetUnpackAlpha now carry their + data in compressed form. The alpha data is compressed using the + ZLIB RLE encoding, while the colormap data is compressed using + the default ZLIB deflate. + +- Thanks to the above changes, ZLIB data compression of all messa- + ges which do not have a specific encoding is turned off, so that + we can make better use of the final stream compression. + +- Created new message structures to handle the compatibility with + the old proxy versions. When connected to an old proxy version + the agent should use the NXSetUnpackColormapCompat() and NXSet- + UnpackAlpha() interfaces. + +nxcomp-2.0.0-72 + +- Solved a compatibility problem with the 1.X.X versions due to the + new render encodings. + +nxcomp-2.0.0-71 + +- Improvements to X_RenderSetPictureClipRectangles. + +- Fixed handleUnpack() to pass the correct byte order parameter to + to the unpack alpha function. + +- Added the --parent parameter to the client even in pulldown mode. + +- Fixed a deallocation bug in StaticCompressor. + +nxcomp-2.0.0-70 + +- Added compression of X_RenderSetPictureFilter, X_RenderSetPicture- + Transform, X_RenderTrapezoids and X_RenderTriangles. + +nxcomp-2.0.0-69 + +- Changed the format of the persistent cache to accomodate the new + encoding of the render opcodes. Caches from the 1.4.0 and 1.5.0 + should be still loaded and saved correctly when connected to and + old version of the library. + +- Improved the encoding of the X_CreatePixmap, X_ChangeGC, X_Render- + CompositeGlyphs, X_RenderComposite, X_RenderCreateGlyphSet, X_Rend- + erFreePicture and X_RenderCreatePicture messages. Added new classes + to handle the backward compatibility. + +- Made the static compression and decompression code reside in the + same class. + +nxcomp-2.0.0-68 + +- Improved the encoding of the X_RenderCreateGlyphSet and X_Render- + FreeGlyphSet requests. + +nxcomp-2.0.0-67 + +- Modified the following session messages: + + From: "Session: Session starting at..." + To: "Session: Starting session at..." + + From: "Session: Session terminating at..." + To: "Session: Terminating session at..." + +- Improved the encoding of the requests creating or freeing an XID. + This affects X_CreateGC, X_CreateWindow, X_CreatePixmap, X_Render- + CreatePicture, X_FreeGC, X_DestroyWindow, X_FreePixmap, X_Render- + FreePicture. + +- Added 'streaming' and 'backingstore' to the list of options used + by agents. + +- Modified the info messages to print the ZLIB compression paramet- + ers to be more compact and only use two lines. + +nxcomp-2.0.0-66 + +- When part of an agent, the proxy will wait for the NXTransDestroy() + function to initiate the shutdown. + +nxcomp-2.0.0-65 + +- Starting from this version the NX client can control the behaviour + of the client proxy by monitoring the messages marked as "Session". + + At the early initialization the proxy will print: + + "Session: Session starting at '...'. + + The ellipsis here represent the current timestamp, as reported by + the POSIX function ctime(): + + Once the session is running, the proxy will print: + + "Session: Session started at '...'. + + At the time the session is being shut down, the proxy will print: + + "Session: Session terminating at '...'. + + Followed by: + + Session: Session terminated at '...'. + + The message "Info: Starting X protocol compression." is removed, + replaced by the message 'Session started at...' with the same + meaning. + + Once the message 'Session: Session terminated at..." is read from + the output, the client can optionally wait for the proxy process + to complete and terminate with an exit code 0. + +- The "Session" messages are not printed when the proxy is running + on the NX server side, as part of an agent. + +nxcomp-2.0.0-64 + +- Reduced the default token limit from 32 to 24. The synchronizat- + ion callback is triggered by the tokens going below half the li- + mit. + +nxcomp-2.0.0-63 + +- Added -I/usr/include/c++ to makedepend. + +nxcomp-2.0.0-62 + +- The house-keeping process is now started upon the initialization, + if the memory cache is enabled, and, on the X server side, when + the remote actually requested access to the image cache. + +nxcomp-2.0.0-61 + +- Added a NX_HANDLER_SYNCHRONIZATION callback to NXTransHandler(). + The handler is called when the agent can use the available band- + width to synchronize X objects that are corrupted or incomplete. + + The reason can be: + + NX_SYNCHRONIZATION_BEGIN: The synchronizationis is allowed. + + NX_SYNCHRONIZATION_END: The synchronizationis must stop. + + The reason can be determined by agents by using the NXBeginSynch- + ronization and NXEndSynchronization values defined in NXvars.h. + +- Bytes from 14 to 24 in the X_NXGetControlParameters reply report, + respectively, the frame timeout, the ping timeout, the preferred + image split mode and the split size threshold. + +nxcomp-2.0.0-60 + +- The following messages used by the NX client and server have been + changed. The NX client and server should be modified accordingly. + + From: "Info: Shutting down the link and exiting." + To: "Info: Shutting down the NX transport." + + From: "Info: End of session requested by remote proxy." + To: "Info: End of NX transport requested by remote" + + From: "Info: End of session requested by agent termination." + To: "Info: End of NX transport requested by agent." + + From: "Info: End of session requested by signal '...'." + To: "Info: End of NX transport requested by signal '...'." + + From: "Info: Shutting down the link and exiting." + To: "Info: Shutting down the NX transport." + + From: "Info: Waiting for cleanup timeout to complete." + To: "Info: Waiting the cleanup timeout to complete." + + From: "Info: Waiting for watchdog process to complete." + To: "Info: Waiting the watchdog process to complete." + +nxcomp-2.0.0-59 + +- Added more debug information regarding the timing of packets sent + and received. + +- Added the possibility to redirect the log output to a file that can + be later shared with nxssh running in "binder" mode. This is useful + when testing the forwarding of the SSHD connection by nxssh to the + agent. Activated by defining BINDER in Loop.cpp. + +nxcomp-2.0.0-58 + +- Fixed the unpack alpha procedure to only rely on the image's byte + order. + +- Solved the X_PutImage encoding problem introduced in the 2.0.0-56 + version and restored the compatibility with the 1.5.0 version. + +nxcomp-2.0.0-57 + +- Added a NXTransFile() function. It returns the name of the files + used by the proxy for the current session. + + The type parameter can be: + + NX_FILE_SESSION: Usually the file 'session' in the user's session + directory. + + NX_FILE_ERRORS: The file used for the diagnostic output. Usually + the file 'errors' in the session directory. + + NX_FILE_OPTIONS: The file containing the NX options, if any. + + NX_FILE_STATS: The file used for the statistics output. + + The returned string is allocated in static memory. The caller must + copy the string upon returning from the function, without freeing + the pointer. + +- Fixed the PANIC in the handling of the abort split messages. + +- The encode and decode routines may write one byte past the nominal + end of the encode buffer. This additional byte is now included in + the payload. This is actually a bug, but harmless. + +nxcomp-2.0.0-56 + +- Added the X_NXAbortSplit request. It can be used to interrupt the + streaming of all the messages currently in the split store for + the specified resource. Depending if the split store did or did + not contain split messages, the client will receive an immediate + end-split or no-split notification. + +- Removed the split modes NXSplitModeEager and NXSplitModeLazy. The + available modes are now NXSplitModeSync and NXSplitModeAsync. The + mode NXSplitModeAsync replaces the two old modes and is now the + default. Similarly to NXSplitModeEager, it makes the proxy split + only messages that exceed a size threshold, but the threshold is + generally smaller than in the previous versions. + + #define NXSplitModeDefault 0 + #define NXSplitModeAsync 1 + #define NXSplitModeSync 2 + +- Renamed X_NXSplit to X_NXSplitData, X_NXAbortSplit to X_NXSplitEv- + ent. + +- The proxy now enters a drain loop, when in congestion state, and + waits until the decongestion. Also the X channels are drained if + blocked. + +- The select is restarted immediately if only the proxy is selected + and the data read doesn't make a complete message. + +- The size of the shared memory segment used by the X server proxy + is now negotiated at startup and the selected value is printed in + the session log. + +- Added the option 'encrypted'. It tells the local proxy if it is + running as part of a program providing encryption of the point to + point communication. This is useful to determine if the proxy can + block waiting for data or needs to yield to the client to let it + handle the remote connection. + +nxcomp-2.0.0-55 + +- Fixed handleRestart() to correctly send the end-split event only + after the split sequence has been closed by the client. + +- Fixed a possible memory error in the Keeper class occurring if the + image caches were erased while in the middle of a loop. + +nxcomp-2.0.0-54 + +- Added a '--parent' option when running the client in dialog mode. + This is the process id that needs to be signaled in the case the + user chooses to do so. + +nxcomp-2.0.0-53 + +- Solved a bug in the abort split procedure. The procedure assumed + that the split had to be at the head of the list. This may not be + the case if the split was loaded from disk asynchronously, after + a different split with the same checksum was recomposed. + +- By defining STRICT in Loop.cpp, the number of available tokens is + set to 1. This can be used to trigger the congestion more often + than it would possible when testing the software with the default + settings. + +- Added more debug output to the procedures handling the streaming + of the images. + +- Reset the channel's split pending and congestion flags in the fin- + ish procedure. Not doing so caused some additional loops at chan- + nel shutdown. + +- Fixed a bug on Cygwin that prevented the log files to be succes- + sfully reopened when the size limit was exceeded. + +- Failures to write to the agent transport are reported sooner when + the channel is finished. This saves a few more loops at session + shutdown. + +nxcomp-2.0.0-52 + +- Started implementing a new handler to let the agent include arbit- + rary data in the transport statistics. The parameter, in this case, + is a pointer to a pointer to a null terminated string. The pointer + is set at the time the handler is registered. The pointed string + will have to be filled by the agent with its statistics data. For + now, only the interfaces and the stubs exist. + +- Disabled the timestamp cache as it still causes rounding problems + when calculating the current bitrate. + +nxcomp-2.0.0-51 + +- Removed SelectPackMethod(), not used anymore, from Pack.c. + +nxcomp-2.0.0-50 + +- Added the X_NXFinishSplit request. It forces the proxy to comple- + tely transfer all the split messages for the given resource, and + then notify the agent. + +- Fixed the statistics to not account the split bits to the control + token. + +nxcomp-2.0.0-49 + +- Fixed a bug that caused the split timeout to be reset even if a + different channel had splits to send. + +- Removed an error condition that arose when a split operation was + requested by a different channel than the first to connect. The + reason is that an auxiliary channel (like the one created by the + server to store the keyboard configuration) can be the first to + open a connection to the X server, then can come the agent, that + is actually using the NX opcodes. The proxy will now simply print + a debug message. + +- Removed a further error condition that was assumed by the proxy + if a pending flush was detected while no channel was selected for + output. This can actually happen if the channel was dropped and, + in the meanwhile, the user requested the proxy statistics. + +nxcomp-2.0.0-48 + +- Added a 'type' parameter to NXTransFlush(). The new NX_FLUSH_IDLE + type should be used by agents when they have finished handling all + their clients and are going to wait for more input. This is the + case, for example, of the X agent returning from WaitForSomething() + after having set a small timeout to find out if there are clients + ready, or the case of a RDP agent finding that there are no opcodes + to read from the RDP server. The 'idle' flush should be requested + also when the X11 agent is waiting for a split to complete. When + the flag is used, the proxy uses the available bandwidth to encode + more low-priority data, like the payload of images being streamed. + +nxcomp-2.0.0-47 + +- Added a new RGB image encoder. For now the encoder uses a static + Z stream to compress the image data in the destination buffer and + allows the agent to use the simplest encoding by still separating + the alpha channel from the image data. The new encoder can be the + the base for implementing color reduction by dithering or a color- + mapped translation of the image similar to PNG, but without the + PNG overhead and with the colormap being sent to the client using + the NXSetUnpackColormap() opcode. + +- Put the routines implementing static deflating and inflating of + a ZLIB buffer from the Compressor and Decompressor classes to Z.h + and Z.cpp, so that they can be reused by the new RGB encoder. + +nxcomp-2.0.0-46 + +- Fixed a segfault arising when trying to start a child without the + NX transport. + +nxcomp-2.0.0-45 + +- Undefined the MATCH directive. + +nxcomp-2.0.0-44 + +- Changed the encoding of the X_CreatePixmap, X_CreateGC, X_Render- + CreatePicture, X_CreateWindow, RenderCreateGlyphSet.cpp. The Xid + of the drawable is included in the checksum of the X_CreateGC and + X_CreatePixmap message. This leverages the way GC and Pixmaps are + allocated in the X agent to obtain better compression. + +- Removed the code handling the reset of the proxy connection. This + functionality was unused since when the persistence is handled by + the agent. + +- Added a specific section in the statistics output for the render + extension. + +nxcomp-2.0.0-43 + +- Fixed a bug that could have caused the sending of multiple 'end + split' messages for the same split sequence. + +- Added handling of the option 'strict'. + +nxcomp-2.0.0-42 + +- Added a missing channel switch directive when encoding abort split + messages at the server side. This could cause a decoding error on + the client. + +nxcomp-2.0.0-41 + +- Ensured that the link is flushed immediately on the X server side + when important keyboard or mouse activity is detected in input. + +- Implemented a timestamp cache to avoid calling gettimeofday() when + it is enough to use the last timestamp gotten from the system. The + cache can be disabled by undefining CACHE_TIMESTAMP in Timestamp.h. + +nxcomp-2.0.0-40 + +- Added the karma delay field to the X_NXGetControlParameters reply. + +- Solved a bug with the decoding of split messages likely to happen + only when using link type LAN. + +nxcomp-2.0.0-39 + +- Implemented separate flow control for the generic channels and the + image streaming data. + +- Improved the split procedure to load a message from the disk cache + if found after it was originally discarded because locked. + +nxcomp-2.0.0-38 + +- Renamed the 'shmem' option as 'shseg'. The 'shmem' option becomes + specific to the agent and will be used together with 'shpix' option + to indicate if, respectively, the shared memory extension and the + use of the shared pixmaps must be enabled in the agent server. The + new option will be instead used by the proxy to determine the size + of the memory segment shared between the proxy and the X server. + +- Added the 'shmem', 'shpix', 'keyboard' and 'clipboard' to the list + of ignored options. The 'keyboard' option should become a synonym + of 'kbtype'. The 'clipboard' option will tell the NX agent if the + clipboard support should be disabled for security reasons. + +nxcomp-2.0.0-37 + +- Ensured that, when running a raw session, the persistent cache is + loaded and saved only once in the session lifetime. + +- Added the 'product' keyword to the list of recognized options. The + option is ignored by the proxy, but can be used by the client or + server to facilitate the support. + +nxcomp-2.0.0-36 + +- Added the remote version number in the X_NXGetControlParameters + reply. + +- Solved a bug related to the removal of the split stores. + +- Removed the unused code dealing with the flush timeouts. + +nxcomp-2.0.0-35 + +- Added an alert to notify the user about the failure of the XDMCP + session. + +nxcomp-2.0.0-34 + +- The active channels and the agent resources are now stored in a + list container. + +- Added the X_NXFreeSplit request. The request is currently unused + because the split store is freed automatically when it becomes + empty, but can be useful in future if we want to implement other + otpimizations. + +nxcomp-2.0.0-33 + +- There is now a split store for each agent resource. This allows + the proxy to divide the bandwidth among the agent resources. + +- A simple round-robin load-balancing is implemented, dividing the + bandwidth equally among all the split stores. + +- If there is a previous request being streamed for the same res- + ource, cached messages are appended to the split store and then + committed in the original order. This makes possible to stream + the colormap and the alpha channel data even if the split invol- + ves multiple PutImage or PutPackedImage operations in a single + start-split/end-split sequence. + +- Implemented the commit store class. Made the commit store expand + the message in the final buffer, instead of leveraging the mes- + sage store. + +- Channels having a drop command pending are checked before trying + to load or save the cache. This prevents a potential problem with + the protocol violation error that could be reported by the client + side proxy. + +- The current bitrate is now calculated at the time the transport + actually writes the data to the network, not at the time the data + is appended to the transport by the upper layers. The reason is + that the data can be compressed by the stream compressor and so + we can calculate the real bitrate only after having flushed the + ZLIB stream. This fixes the wrong numbers that could be reported + by the statistics in the previous versions. + +nxcomp-2.0.0-32 + +- More progress in the implementation of the new image streaming. + This version has been tested to work when connecting to an 1.5.0 + server. Due to the significant changes in the way the streaming + works in the newer versions, split is never activated by a 2.0.0 + server connecting to a 1.5.0 client. + +- Fixed the NXTransClose() function to check if the fd matches the + NX agent connection. + +- Fixed the includes in Socket.h to compile on Solaris and MacOSX. + +nxcomp-2.0.0-31 + +- Changed the NX transport functions that didn't require a NX fd + parameter to include one. The value can be NX_FD_ANY to signify + any running NX transport. + +nxcomp-2.0.0-30 + +- More progress in the new implementation of image streaming. + +- This version is apparently compatible with the 1.5.0 client but + needs further testing. + +nxcomp-2.0.0-29 + +- Implemented the NXTransHandler() interface and made the proxy + use the callback, when it is installed, instead of the notifi- + cation events. + +- Modified the agent transport to report an EOF condition as soon + as the channel is dropped, instead of reporting the error only + after the proxy shutdown. This allows the agent to handle its + clients while the proxy is handling the grace timeout. + +- Added a test facility to verify if the client and server caches + match at shutdown. + +- More advances in the new implementation of streaming. + +nxcomp-2.0.0-28 + +- Made the session shutdown faster, normally below 1 second. The + grace time is now 500 ms once all channels have been dropped. + +nxcomp-2.0.0-27 + +- The house-keeping process will now run a number of iterations + (currently 100) and then will exit. The proxy will check the + exit code and, if it was requested by the child, will restart + it. This is intended to keep the memory consumption low, as I + noted that after a big number of iterations the performance of + the allocator start to degrade quickly. + +nxcomp-2.0.0-26 + +- Started implementing the NXTransHandler() interface to let the X + application hook a callback and be notified by the proxy when an + event occurs. At the moment only congestion events are supported. + The interface makes possible for the caller to provide a generic + parameter. This parameter is passed back to the handler function. + The parameter is expected to be the Xlib display pointer, given + that the proxy doesn't have access to the Xlib structures. + +- Moved all the internal variables shared between Xlib, nxcompext + and the X server in nxcomp. Declarations and function prototypes + moved to NXvars.h. + +- Removed the NXGetCleanupParameters() and NXGetImageParameters() + interfaces and the remaining references to the unused display + buffer and image cleanup functions. + +- In this version the images streaming functionality is disabled. + Because of that, expect compatibility problems when connecting + to a different nxcomp version. + +nxcomp-2.0.0-25 + +- Forced the end of the session earlier if a shutdown has been re- + quested by the local side but the remote proxy is not responding. + +nxcomp-2.0.0-24 + +- Changed the loop deleting the file items in Keeper to pick the + first element instead of looping through the iterator. + +- More problems with the libstdc++ allocators. During the tests, I + found that the Keeper class can leak industrial amounts of memory. + Valgrind says that the objects that are not freed are the File + items we allocate in collect(). As this looked suspicious, I log- + ged the constructors and destructors and can say that everything + is OK on our side. Unfortunately, by removing the spleeps used + to let the program work silently in background, one can find that + the memory occupation still grows by a MB every few seconds. This + is less a problem on Windows, where the memory occupation remains + almost constant. See the comment on __USE_MALLOC below. + +- Increased the maximum size of a message that the proxy is allo- + wed to cache to 4MB. This makes possible to cache many replies + (like the X_QueryFont replies) that often exceeded the previous + limit. + +nxcomp-2.0.0-23 + +- Fixed a compatibility problem with the 1.5.0 that arose when + encoding an image with caching and streaming temporarily disa- + bled. + +- Removed the __USE_MALLOC define in Types.h because this is what + the g++ developers want. I'll try to avoid flames and will not + comment on this. + +- Added a --with-use-malloc configure option. This option should + be used when building the release packages on GCC versions that + support it. + +- Started fixing the commit procedure to be able to deal with the + alpha channel. + +nxcomp-2.0.0-22 + +- Prevented channels from being dropped while in the middle of a + read loop. + +- Added a small utility ensuring that the SIGCHLD we receive in + the main loop can be always identified as pertaining to an our + child. + +- Increased the space available for the control messages at the + beginning of the encode buffer and ensured that there is always + enough space for adding a token message, in the case this is + required by the flush procedure. + +- Made all cache files readable only by the user. + +nxcomp-2.0.0-21 + +- Solved a bug in the streaming of images that caused the agent's + clients to never resume when connected to an old proxy version. + +nxcomp-2.0.0-20 + +- Disabled again streaming of the alpha channel data. By enabling + it, the agent randomly fails to repaint the exposed regions. The + reason is to be investigated. + +nxcomp-2.0.0-19 + +- Implemented the split mode NXSplitModeSync. When this mode is + selected, the proxy will not send data for the split at the head + of the list until the remote peer has confirmed that the message + is not available in the image cache. This is going to be useful + in combination with the lazy encoding. + +- Improved the error detection in the case of failure when saving + a recomposed split on disk. + +- Added X_NXSetUnpackAlpha to the set of requests that is possible + to stream. + +nxcomp-2.0.0-18 + +- Enabled the 2.0.0 features that were disabled for test purposes. + +- Changed the LICENSE file to state that the software is only made + available under the version 2 of the GPL. + +nxcomp-2.0.0-17 + +- Introduced a new channel of type 'slave'. Opening a slave channel + should let the proxy fork a new NX client instance (or whatever + is in the NX_SLAVE environment) and pass to it the channel's desc- + riptors. This client instance should authenticate the peer, for + example using the proxy cookie (see how this is implemented in + nxsensor) and then negotiate the service to be used on the channel. + This can be used to implement forwarding of arbitrary ports or ad- + ditional services, like a simple file transfer protocol. For now + the implementation is incomplete. Opening a slave channel will + result in the remote proxy just dropping the channel connection. + +- The proxy is forcibly flushed at the end of each loop. This is a + temporary fix until the agents are updated to flush the proxy link + on demand. + +- Added a new alert to be used in the case the RDP session is closed + and replaced with a new session using the same Windows account. + +- Added a forward declaration of class RenderMinorExtensionStore in + file RenderExtension.h to compile using gcc 4.0.X. + +- Added removal of all .orig file when running a 'make clean'. + +- The code uses again the 2.0.0 style shutdown (the one waiting for + the watchdog process to terminate). + +nxcomp-2.0.0-16 + +- Added the NXTransChannel() interface to allow agents to create + new channels connected to the port that will be forwarded by the + remote proxy. The function returns 1 on success, 0 if the NX + transport is not running, or -1 in the case of error. On success, + the descriptor provided by the caller can be used for all the + subsequent I/O, including selecting the descriptor to check if + I/O is possible. The type parameter not only tells to the proxy + the remote port where the channel has to be connected, but gives + a hint about the type of data that will be carried by the channel, + so that the proxy can optimize the compression and schedule the + traffic on the slow link. + + The type can be: + + NX_CHANNEL_X: The channel will carry X traffic and it + will be connected to the remote X display. + + NX_CHANNEL_CUPS: The channel will carry CUPS/IPP protocol. + + NX_CHANNEL_SMB: The channel will carry SMB/CIFS protocol. + + NX_CHANNEL_MEDIA: The channel will transport audio or other + multimedia data. + + NX_CHANNEL_HTTP: The channel will carry HTTP protocol. + + NX_CHANNEL_FONT: The channel will forward a X font server + connection. + + Only the proxy running at the NX server/X client side will be + able to create a X, CUPS, SMB, MEDIA and HTTP channel. A proxy + running at the NX client/X server side can create font server + connections. The channel creation will also fail if the remote + end was not set up to forward the connection. + + To create a new channel the agent will have to set up a socket- + pair and pass to the proxy one of the socket descriptors. + + Example: + + #include <sys/types.h> + #include <sys/socket.h> + + int fds[2]; + + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) + { + ... + } + else + { + // + // Use fds[0] locally and let the + // proxy use fds[1]. + // + + if (NXTransChannel(fds[1], NX_CHANNEL_X) <= 0) + { + ... + } + + // + // The agent can now use fds[0] in + // read(), write() and select() + // system calls. + // + + ... + } + + Note that all the I/O on the descriptor should be non-blocking, + to give a chance to the NX transport to run in the background + and handle the data that will be fed to the agent's side of the + socketpair. This will happen automatically, as long as the agent + uses the XSelect() version of the select() function (as it is + normal whenever performing Xlib I/O). In all the other cases, + like presumably in the agent's main loop, the agent will have + to loop through NXTransPrepare(), NXTransSelect() and NXTrans- + Execute() functions explicitly, adding to the sets the descript- + ors that are awaited by the agent. Please check the implementa- + tion of _XSelect() in nx-X11/lib/X11/XlibInt.c for an example. + +nxcomp-2.0.0-15 + +- Some changes aimed at finding a workaround to the unreliability + of the font server connections. The X server seems to be really + picky about the network latency, so we try to read immediately + from the channel just being created, instead of waiting for a + new loop. Additionally the proxy will keep reading from the font + server connections even after the maximum length of the frame + is exceeded, in the hope that it will be able to send a complete + message in a single frame. + +- updated the various internal consistency tests run when the TEST + or INFO flags are enabled. + +nxcomp-2.0.0-14 + +- It seems that the closure of the socket sometimes takes several + seconds, on Windows, after the connection is broken, with the + result that the alert can be shown long after the user has gone + after the failed session. We now skip the closure of the proxy + link on Windows if we are about to exit. + +- The code uses the 1.5.0 style shutdown (the one waiting for the + term signal in the main process) to be able to test the library + with the old server. To activate the new shutdown procedure be + sure the define SHUTDOWN_COMPATIBLE_1_5_0 is unset. + +nxcomp-2.0.0-13 + +- Ensured that static members in channels are reset at the time a + new proxy is created. + +- Adjusted the token parameters for link modes other than modem. + +nxcomp-2.0.0-12 + +- Handled compatibility with 1.X.X versions not sending the count + field in the token. + +- The proxy link is now flushed automatically if the agent has not + set the deferred flush mode. + +nxcomp-2.0.0-11 + +- Significant modifications to allow the proxy to keep data in the + encode buffer across multiple loops, until the data is flushed + because an explicit request by the agent or. Besides maximizing + the amount of data sent in a single frame, this makes it possi- + ble to move any packet coalescence strategy in the agent, while + still using an 'immediate' flush policy. + +- Modified the procedure handling the proxy congestion. Each token + now carries a counter which represents the multiple of TokenBytes + data bytes that need to be confirmed by the remote end. The ser- + ver side proxy will need to reply by including the value of the + counter. When sending a new token, the tokens are decremented by + the counter and the client side proxy enters in congestion when + the available tokens becomes zero or negative. + +nxcomp-2.0.0-10 + +- Implemented specific encoding of the X_NXSetCacheParameters + request. + +- Handled the compatibility with older versions not supporting the + new request. In this case the request is handled at the local si- + de, preventing the X requests to be cached or split, even without + the cooperation of the remote side. + +nxcomp-2.0.0-9 + +- Modified the shutdown procedure to not rely on the TERM signal + when the proxy needs to wait for the server to complete. We will + now print the following message in the session log: + + Info: Watchdog running with pid 'n'. + Info: Waiting for watchdog process to complete. + + The NX server will have to kill the process with the given pid + to mandate the proxy shutdown. This solves the TR11C01192. + +- Added the X_NXSetCacheParameters request. It tells to the proxy + how to handle caching of X requests, namely if requests should + be stored in the memory cache, split in smaller data chunks and, + in the case of images, saved on disk in the persistent image + cache. The request affects all the X messages until the proxy + is further configured. + +- Enabling and disabling loading and saving of persistent images + due to a X_NXSetCacheParameters is still to be implemented. + +- Implemented font channel compatibility with older versions. If + the font port is queried and the remote proxy doesn't support + the tunneling of font server connections, an empty string is + returned. + +- The option 'resize', used by agents, is now silently ignored. + This solves the TR10C01061. + +- Added the missing reference to TR08C000981. + +nxcomp-2.0.0-8 + +- Discarded the first idea of setting the font path implicitly in + the X server side proxy, by means of a multi-pass operation and + added the NXGetFontParameters() request and reply. If the proxy + has been configured accordingly, the request returns the X font + path that can be set by the agent to tunnel the font server con- + nections through the NX link. + +- Changed the parsing of the font server port to be able to handle + the defaults. + +nxcomp-2.0.0-7 + +- More work on tunneling of font server connections. + +- The synchronization requests are now replied at the same time + as a new request is received, by checking if the channel has + already gone past the awaited sequence number. This avoids the + delay caused by the previous versions in the case the expected + reply had been handled asynchronously, inside the write loop. + +nxcomp-2.0.0-6 + +- The display is again passed to the client using the -display + parameter. + +- The option 'type' is now ignored at X server side. Support at + the X server side had been left for compatibility with older + versions of the proxy. + +- Added direct support for more session types, namely 'unix-cde', + 'unix-xdm', 'unix-console', 'unix-default'. The 'unix-console' + and 'unix-default', similarly to 'unix-application', will now + trigger the 'raw' mode. + +nxcomp-2.0.0-5 + +- Renamed Png.h and Png.cpp to Pgn.h and Pgn.cpp to avoid name + clashes on Windows. + +- The distclean target now removes the autom4te.cache directory. + +nxcomp-2.0.0-4 + +- Added the 'kill=n' option to let the proxy add the process to + the list of daemons that must be terminated at session shutdown. + Multiple 'kill=n' options can be specified. The proxy will send + to each process a SIGTERM signal before exiting. + +- Removed the code that forcibly disabled the RENDER extension on + Solaris. Eventual compatibility problems will be now handled in + the NX agent. + +- The path to the client executable is now assumed to be in the + NX_CLIENT environment. If the variable is not set, the proxy + will guess the location based on the target platform. This is + part of the FR11C01215. + +nxcomp-2.0.0-3 + +- More code cleanup. Protocol step 5 and cache step 2 are assumed + by default. This allows us to remove the various channel caches + that are not used anymore. + +- Removed the outdated encodeBegin() and encodeEnd() methods. + +nxcomp-2.0.0-2 + +- Added compatibility with the 1.5.0 and 1.4.0 versions. Protocol + version older than the 1.4.0 will cause the proxy to drop the + connection. + +- Rewritten the version negotiatiation logic to better handle the + compatibility between the local and the remote version. + +- Removed the conditional code handling the compatibility with + the protocol encodings from 1 to 4. + +- Small adjustments to the session negotiation procedure to give + more hints in the case of errors. A dialog is now issued if the + remote proxy drops the connection due to a probable authentica- + tion failure or when the remote proxy version is not compatible + with our version. + +nxcomp-2.0.0-1 + +- Opened the 2.0.0 branch based on nxcomp-1.5.0-80. + +nxcomp-1.5.0-80 + +- Fixed a bug that prevented the Unix socket to be created in the + right directory. This affected the ability of the server to run + NX sessions without the agent encoding. + +nxcomp-1.5.0-79 + +- Solved a bug in the 15 to 16 bpp conversion for RDP sessions. + +- Corrected a typo in the PANIC message of Unpack16To24 function. + +- Optimized the 16 bpp RDP decompression routine. + +nxcomp-1.5.0-78 + +- Implemented the FR11C01215. The following environment variables + are checked to determine the location of the relevant NX direct- + ories: + + $NX_ROOT The root NX directory is the place where the session + directory and the cache files are created. This is + usually overridden by passing the 'root=' option. By + default, the root NX directory is assumed to be the + directory '.nx' in the user's home. + + $NX_SYSTEM The directory where NX programs and libraries reside. + If not set, the value is assumed to be '/usr/NX'. + Programs, libraries and data files are respectedly + searched in the 'bin', 'lib' and 'share' subdirecto- + ries. + + $NX_HOME The NX user's home directory. If $NX_ROOT is not set + or invalid, the user's NX directory is created here. + + $NX_TEMP The directory where the X11 Unix Domain Sockets and + all temporary files are to be created. + + $NX_CLIENT The full path to the nxclient executable. If the va- + riable is not set, the nxclient executable will be + run assuming that the program is in the system path. + This can be useful on platforms like Windows and the + Mac where nxclient is located in a different direct- + ory compared to the other programs, to make easier + for the user to execute the program from the shell. + + Other environment variables: + + $HOME The variable is checked in the case $NX_HOME is not + set, null or invalid. + + $TEMP The variable is checked whenever the $NX_TEMP direct- + ory is not set, null or invalid. + + $PATH The path where all executables are searched, except + nxclient. If $NX_CLIENT is not set, also the client + executable is searched in the system path. + + $XAUTHORITY + This is the file containing the X11 authorization + cookies. If not set, the file is assumed to be in + the user's home (either $NX_HOME or $HOME). + + $LD_LIBRARY_PATH + System-wide library search order. This should be set + by the program invoking the proxy. + +- Extended the usage message to document the relevant environment. + +- Made the WaitChild() function interruptible. This allows the user + to terminate the connection procedure immediately, by sending a + signal to the proxy process while an abort dialog is shown in the + foreground. This is part of the solution to the TR11C01216. + +- Implementation of the FR11C01215 is not yet complete. The client + program and the nxauth/xauth are still searched using the previ- + ous logic. In particular, the NX_CLIENT variable is ignored. + +nxcomp-1.5.0-77 + +- Corrected the typo in the alert dealing with VNC authentication + failures. This solves the TR11C01199. + +nxcomp-1.5.0-76 + +- Added the missing initialization in the client and server proxy + constructors. + +nxcomp-1.5.0-75 + +- Starting from this version the build procedure will automatically + create a DLL on Windows. + +- Disabled exceptions in the compiled code. + +- Disabled run-time type information to further reduce the size of + the executable. + +- Compiling with GCC 4.0.0 and -fno-exceptions produces a warning + in some files, due to control apparently reaching the end of a + non void function. This is bogus and has been confirmed to be a + GCC 4.0.0 bug solved in more recent versions. + +nxcomp-1.5.0-74 + +- Imported changes from nxcomp-1.6.0-4. + +- Solved a compilation error on Solaris and MacOSX platforms. + +- The 'font' option now accepts either a numeric value (for a TCP + port) or a 'unix/:port' or 'tcp/:port' specification. In the case + of Unix ports, we assume that a port specification "unix/:7100" + corresponds to the "/tmp/.font-unix/fs7100" socket and a port like + "unix/:-1" corresponds to "/tmp/.font-unix/fs-1". + +- An absolute file path is also accepted as a valid socket. This is + useful to test the forwarding of the font server connection, for + example by running a normal X client and pointing the socket to + the X server. + +- Protocol step 7 is now only assumed when connecting to a remote + proxy version 1.6.0 or later. + +nxcomp-1.5.0-73 + +- Use the include files from nx-X11 if the nx-X11/include directory + is found. The previous configure checked the presence of nx-X11/ + exports/include, that might not be built at the time this library + is compiled. + +nxcomp-1.5.0-72 + +- Fixed a problem on AMD64 due to the size of the area pointed by + the argument of NXTransReadable(). The ioctl() requires a pointer + to an int, at least on Linux. The original _X11TransBytesReadable() + function simply calls the ioctl() by passing the pointer that is + provided. Returning the value assuming a pointer to a long crashes + some applications, among them xterm. Now NXTransReadable() follows + the same schema of the ioctl() call and stores the result assuming + a pointer to an int. + +nxcomp-1.5.0-71 + +- Ensured that the NX trasport is destroyed on NXTransExit() even if + the parent has overridden our signal handlers. + +- Added attribute 'noreturn' to NXTransExit(). + +- Small change to the shutdown procedure to send the "Waiting for a + further signal" just after the watchdog is started. + +nxcomp-1.5.0-70 + +- Added the definition of protocol step 7. The new protocol step is + implicitly assumed when activating the font server channel, so that + NX clients and servers may decide what to do by verifying the patch + version of the peer software. This makes possible to activate the + feature without stepping up the 1.5.0 version of the software. + +nxcomp-1.5.0-69 + +- Added the -fPIC GCC flag when compiling on AMD64 architectures. + +- Small changes to configure.in to have specific CFLAGS. + +- Created a new configure using autoconf 2.59. + +- Added the 'mask=n' option to determine how channel ids are distri- + buted between client ans server. By default, channels whose ids are + multiple of 8 (starting from 0) are reserved for the client side. + All other channels can be allocated at the NX server side. + +- A check is missing on the protocol supported by the remote side, + so it is likely that mixing this version with older 1.5.0 will not + work. + +- The release has the debug logs enabled. + +nxcomp-1.5.0-68 + +- Added provision for opening new channels on both client and server + side. This required small changes to the way channel ids are mapped + and retrieved, so that both sides now support bidirectional mapping + of file descriptors to channels. + +- Made the code handling allocation of new generic channels to reside + in the base Proxy class. + +nxcomp-1.5.0-67 + +- Renamed the 'embedded keyboard' channels as 'aux' channel. + +- Renamed 'samba' channels as 'smb'. + +- The 'samba' and 'smb' options can now be used interchangeably. The + same applies to the 'keybd' and 'aux' options, used to set up the + auxiliary X channel. + +- Added a new font channel. The channel is used to forward X font + server connections from the X server on the NX client to the remote + NX server. The channel is not yet functional and requires change to + the channel allocation mechanism. + +- Added a common interface to create new listening sockets. This is + currently used for additional services, but not for the X display + sockets. + +- Simplified the interface used to accept new connections to channels + being forwarded. + +- Removed the -V option as nxproxy is not using dlopen() anymore. + +- Removed the inclusion of ClientChannel.h in Proxy.cpp that caused + test symbols to be reverted to undefined. + +nxcomp-1.5.0-66 + +- Made failures setting the IPTOS_LOWDELAY on the proxy socket to + cause a warning, instead of an error. + +- Made the clean target delete the Cygwin specific libraries. + +- Updated the configure and Message.h to deal with GCC 4. Solves the + TR08C000981. + +nxcomp-1.5.0-65 + +- Removed the warning issued on parsing the agent option 'rootless'. + Solves the TR08C00959. + +- MacOSX 10.4 defines socklen_t. Made the definition conditional so + that we can still support older versions. Solves the TR07C00926. + +- Updated the ChangeLog to include references to the solved TRs. + +nxcomp-1.5.0-64 + +- Imported the 1.6.0 changes in the maintenance 1.5.0. + +- Removed a wrong assertion that might cause the session to fail + when the software was compiled with TEST enabled in Proxy.cpp. + +- The nxclient dialog process is signaled with SIGKILL on Windows, + as the SIGTERM is ignored. This solves the TR07C00929. + +- Ensured the JPEG error flag is always set before jumping out of + the Jpeg decompression. + +- Skipped errors encontered setting the TCP_NODELAY flag on Mac. + Solves TR08C00940. + +- Few cosmetic changes. + +nxcomp-1.5.0-63 + +- Ensured that the parent is checked often in the keeper process, + so that, in the case of a premature death, the child is exited + earlier. + +nxcomp-1.5.0-62 + +- Some performance tuning of the LAN and WAN link modes. + +nxcomp-1.5.0-61 + +- Fixed the problems arisen with loading or saving the image files + on Windows by forcing the streams to be opened in binary mode. + These problems have been triggered by the recent Cygwin upgrade. + +- Removed the logs that had be left enabled for test purposes. + +nxcomp-1.5.0-60 + +- Made all errors encountered while unpacking an image just print + a warning in the session log. Such errors are not fatal but the + client monitors the state of the session at startup, so that by + marking image decompression errors as such may cause the session + to be aborted. + +- Marked as warnings also errors encountered by trying to load an + image from disk. + +nxcomp-1.5.0-59 + +- When detected, the CTRL+ALT+SHIFT+ESC sequence is removed from + the event stream. + +- Also modified the message in the session log to issue a warning, + instead of an error. + +nxcomp-1.5.0-58 + +- Added a setjmp() before yielding the control to the JPEG library. + This makes possible to recover from the JPEG decompression errors + that were previously fatal. + +- In the commit split request, the client id is now encoded by the + client channel beside the propagate flag. The client id is igno- + red by the decoding party. As in the old protocol versions, the + committing client is taken from the client id that was originally + sent together with the packed image. + +- Fixed the compilation problem when defining OPCODES in Misc.cpp. + +- Skipped the synchronous flush of the proxy link when connecting + to a previous NX version. + +- Ensured that the new alerts are only requested when connected to + a compatible proxy. + +- Ignored the option 'fullscreen', targeting the agents. + +nxcomp-1.5.0-57 + +- Added more alerts and changed the name of those dealing with the + resume of a session. + +- Added the MIXED define. When set, the proxy will let all logs go + to the standard error. This is useful to interleave the Xlib log + output with the proxy output in a single file. + +- Added a new alert to report an I/O error at agent reconnection. + This is one of those alert that can't be actually shown, but are + included to offer a consistent view to the agent implementation. + +nxcomp-1.5.0-56 + +- Added the split mode NXSplitModeSync. By selecting this mode, + the proxy will try to empty the split store immediately, until + all messages marked in such mode will be synchronized. The im- + plementation is left to future versions. + + #define NXSplitModeSync 3 + +- Added the messages specific to RDP and VNC sessions in NXalert.h. + Included are also a few alerts related to changes to the color + depth and desktop geometry that may be eventually mandated by the + remote server. + +- Corrected a typo in NXalert.h + +nxcomp-1.5.0-55 + +- Added the 'mode' field in the X_NXStartSplit request. It determi- + nes the strategy that the proxy will adopt to handle the image. + If set to 'eager', the proxy will only split the messages whose + size exceeds the split threshold (the threshold can be found in + the X_NXGetControlParameters reply). If mode is set to lazy, the + proxy will split any image that it is not able to find in its + cache. + + The opcode and the two available modes are defined in NXproto.h, + currently: + + #define NXSplitModeDefault 0 + #define NXSplitModeEager 1 + #define NXSplitModeLazy 2 + +- All requests related to image streaming now carry a 'resource' id. + The id is currently ignored by the proxy in the case of X_NXCom- + mitSplit requests. + +- Added a new NXSetSplitMode() request. It determines the strategy + that the proxy will adopt to stream the images. If set to 'eager' + the proxy will only split the messages whose size exceeds the + split threshold, otherwise it will split all the images, regard- + less their size. This is in preparation of the lazy encoding in + agent. + +- Slightly decreased the startup timeout compared to the value it + had in the 1.4.0. This timeout is used to retain the images used + during session startup in the persistent cache. + +nxcomp-1.5.0-54 + +- Modified the text of the begin-reconnection alert to mention the + speed of the connection detected by the agent. + +nxcomp-1.5.0-53 + +- Reworked the handling of the abort-split events issued by the re- + remote proxy so that we now finalize the procedure and restart the + client asynchronously, in the middle of the write loop. + +nxcomp-1.5.0-52 + +- Rewritten the internal interface to the notification events. There + are now five different split notification events: + + NXNoSplitNotify The operation didn't cause any actual split. + The client can be immediately restarted. + + NXStartSplitNotify A split is ongoing. The client should be + suspended until the end of the split. + + NXCommitSplitNotify A complete request has been recomposed. The + client can commit the given request to the + X server. + + NXEndSplitNotify The split operation was duly completed. The + client can be restarted. + + NXEmptySplitNotify No more split operation are pending. The + agent can use this information to implement + specific strategies, requiring for example + that all messages have been recomposed at + the remote end. This can include updating + the drawables that were not synchronized + because of the lazy encoding. + +- Removed the X_NXSync and X_NXKarma operations, not used anymore by + the NX agents. + +- Increasing or decreasing a timeout makes it respectively equal to + the base timeout or to the base timeout / 4. + +nxcomp-1.5.0-51 + +- Improved the mechanism by which synchronization replies are sent + to the X client proxy. + +- Using the leftPad field as passed by the nxagent 1.5.0-58 doesn't + seem to work with the MIT-SHM. The workaround is to disable the + MIT-SHM operation in the case of 1 bpp images. + +- Removed the limitation on the size of the shared memory segment + that had been introduced for test purposes. + +nxcomp-1.5.0-50 + +- Solved compilation problem when --with-info was not given. + +nxcomp-1.5.0-49 + +- Improved support for color matching from 15 and 16 bpp to 24 and + 32 bpp in RDP sessions. + +- Solved TR05C00910. This was about "shadows" that might appear in + RDP sessions when small elements like tooltips were drawn. + +nxcomp-1.5.0-48 + +- Added a 'commit' field in X_NXCommitSplit request. When zero, the + X server side proxy will unlock the message in the message store, + without actually committing the image to the X server. The new + field required changes to the encoding. This means that this ver- + sion is not compatible with older 1.5.0 nxcomp releases. + +- Added a new control message. This is used by NXTransFlush() to for- + ce a roundtrip between the proxies and speed-up the handling of + the synchronous replies. + +nxcomp-1.5.0-47 + +- Made the timeouts dynamic, based on the user input and the current + bitrate. + +- Set the available tokens to 8. This allows for 16 KB to be sent on + a modem before having to wait for a reply. + +- Added provision for removing a dialog on the remote end. This can + be used, for example, to get rid of a message that was displayed + by the agent at the beginning of a lengthy operation. + +- Fixed a fault that arose when trying to run the proxy on a display + whose name began with the 'nx' string. + +- Running a client or server session on a display host beginning with + 'nx' should be now possible. + +- Flush is now always immediate when there is already data queued in + the TCP socket. + +nxcomp-1.5.0-46 + +- Created the NXalert.h header from Alert.h to share the alert codes + with the agents. The new NXTransAlert() interface can be used to + start an alert on the remote side. + +- Created the NXTransParse*() and NXTransCleanup() wrappers. + +- Temporarily using the single-dash style when passing parameters to + nxclient until the double-dash style is fixed. + +nxcomp-1.5.0-45 + +- Motion events are now flushed at the time the X server channel is + sending the token reply. + +- Renamed NXDialog() to NXTransDialog() and added a window ID parame- + ter, to be used by the agent when starting the "pulldown" dialogs. + +- Also renamed the other NX utility functions. + +nxcomp-1.5.0-44 + +- Solved a bug that prevented auxiliary X connections from working + with the agent. + +- Added the missing check on the proxy pointer in HandleTimer() as + it may be actually called before the proxy is created. + +nxcomp-1.5.0-43 + +- Started tuning the performance. For now, the available tokens are + set to an improbable value of 10000. + +nxcomp-1.5.0-42 + +- Disabled the X server side remote expose events for test purposes. + This is intended uncover the display problems introduced by the + new handling of exposures in the X11 agent. + +- Fixed some timestamp related functions to avoid rounding problems. + +- Moved the timestamps of split, frame and flush from the Control + to the Proxy class. + +- Made the X server side proxy to skip updating the counters related + to token management. + +- Made also the X server side use deferred flushes. + +- Modified handleFrame() to manage both pings and data frames and + send the token before the actual write operation. + +- Cleaned the pseudo-files used to load the control parameters. + +- Cleaned up the FIXME's in the lower layers. More FIXME's remain in + the frontend classes. + +- Added a further counter to the statistics tracking the number of + network writes performed on the proxy link. + +nxcomp-1.5.0-41 + +- Now the watchdog process doesn't kill the parent. The termination + event is determined by monitoring the child. This is advisable to + avoid interfering with the signal handling in agent. + +- The house-keeping process is not started if differential compres- + sion (and thus caching of images) is disabled. + +- Wait for the watchdog to terminate before starting the house-keep- + ing process. + +- Fixed the problem of gray stripes in white areas when using 15bpp + RDP compression. This solves the TR06C00916. + +- Added a log in Message.cpp to tell when an image is removed from + the memory cache. + +- Removed most of the logs. Only left the logs directly related to + the deferred flushes. + +- Removed a warning due to a missing definition in NX.h. + +nxcomp-1.5.0-40 + +- Added the signal '12' to the set of blocked signal on Cygwin. This + is what is delivered to the application when it tries to create a + shared memory segment when the cygserver is not running. The call + later returns "Operation not implemented", only useful if you have + survived to the signal. + +- Modified the configure script to not link against cygipc. + +- Improved error handling in the negotiation phase. + +- Reset the last signal received when aborting a connection attempt. + Not doing that might cause a warning. + +nxcomp-1.5.0-39 + +- Both channels and proxy can now deal with incomplete messages by + waiting for the full data to become available. This is not going + to work when the transport is connected to an agent that is run- + ning in-process, so the timeout must be really small. + +- Made the main loop more compact by letting a single function read + from proxy and channels. The same change applies to writing to + descriptors that are reported to be ready. + +- Optimized the loop to report as ready the agent descriptors that + have become available after the proxy execution phase. + +- Now the proxy will try to asynchronously read from its descriptor + after having read from the channel. + +- Unified the routine setting the routines setting the read and write + descriptors and the timeouts. + +- Unified the routine handling the channel events. + +- Reintroduced the last ping timestamp to avoid sending more than a + single ping every PingTimeout milliseconds. + +- The cleanup procedure now waits for the watchdog and house-keeping + processes to terminate. + +- Reverted the change occurred in 1.5.0-35 about the byte-ordering + problem in decompression of PNG and JPEG images. + +- More coding about deferred flushes. + +- Relevant debug logs are enabled in the main loop, in the proxy and + in the channel classes, so use only for testing. + +nxcomp-1.5.0-38 + +- Started the implementation of deferred writes based on the new NX + transport. + +- Solved a problem with the statistics file affecting the Windows + platform. Basically it seems that Cygwin or the stdc++ library + don't deal correctly with files that are reopened just after + having unlinked them. + +- Implemented support for 15 bpp RDP bitmaps. + +- Added "rdpcolors" and "rdpcache" to the list of parameters that + are ignored in ParseEnviromentOptions(). + +- This version has lot of logs enabled and some forced cleanups in + the case of unexpected conditions. Avoid to use it for long-last- + ing test sessions. + +nxcomp-1.5.0-37 + +- Now splitting of images is fully disabled when running with link + LAN. Previously the split was not executed by the proxy, but the + agent was configured to send the start/end sequence. + +- Added the check protecting the proxy from token underflow. In the + previous code the check was left out to verify the conditions un- + der which this event might be encountered. + +- NXTransWrite() and NXTransWriteVector() allocate a context before + calling handleRead(), so they can correctly return in the case of + a cleanup. + +nxcomp-1.5.0-36 + +- Modified the channel write loop to interleave abort split events + between the writes to the X server socket. + +nxcomp-1.5.0-35 + +- Solved a byte-ordering problem in decompression of PNG and JPEG + images. + +- Changed the handleToken() interface to make explicit if a token + must be issued because of a ping. + +- Small fix to prevent a warning in Jpeg.cpp when TEST is enabled. + +nxcomp-1.5.0-34 + +- Solved a few problems introduced by the rewrite of the read loops + in channels. + +- Added definition of NXCollectInputFocusNotify. + +nxcomp-1.5.0-33 + +- Implemented a new control flow system based on tokens exchanged bet- + ween the proxies. When the clint side runs out of tokens it stops + sending data until a new token is returned. Tokens are sent whenever + the data written to the socket exceeds the amount of data set for a + scheduled write, so the proxy is free to send a number of smaller + frames before running out of tokens. + +- The reason the new system is introduced is because the old method + had two outstanding problems: + + - It worked very well when the proxies were directly connected, + but couldn't reliably detect a link congestion when tunneled + over SSH. + + - In the attempt of reducing the data queued to the TCP layer, it + didn't leverage all the available bandwidth. + +- Moved the final check on the state of the session at the end of + execution stage, instead of the beginning. The signals sent by the + user to request the statistics could be reset before the proxy had + entered the right procedure. + +- Added the session type 'unix-rootless', to be used for single apps + run with the agent in rootless mode. + +nxcomp-1.5.0-32 + +- Modified the main loop to skip the select when I/O is possible on + any of the agent descriptors or the proxy link. + +- Now the house-keeping process is allocated in the heap instead of + the stack. + +nxcomp-1.5.0-31 + +- The server channel now performs asynchronous reads from the display + by interleaving them with data decoded from the remote proxy. + +- Improved the handling of the memory-to-memory transport. + +nxcomp-1.5.0-30 + +- Redesigned the handling of the congestion events to work more re- + liably and to report the events to the remote peer earlier. + +- Increased the size of the shared memory segment when the MIT-SHM + extension is activated. + +- Solved a bug that prevented the ping timeout to work as expected. + +- Ensured that, when entering the main select(), neither the chan- + nels or the proxy have pending messages in the read buffers. + +- There is now only a single case where we can have pending messa- + ges, namely in the handling of the MIT-SHM events. + +- Solved a bug introduced by the 1.5.0-29 that made the proxy en- + ter the select with a null timeout. + +- Renamed LINK_TYPE_AUTO to LINK_TYPE_NONE in NXproto.h. + +nxcomp-1.5.0-29 + +- Now encoding data from the agent descriptors happens in the same + context as data is written to the buffer. + +- Optimized the handling of the congestion events to avoid running + further unneeded loops. + +- Added timer handling utilities. + +nxcomp-1.5.0-28 + +- Added code providing information about the reason of the failure + encountered connecting to the local X server. This greatly helps + when troubleshooting X authorization problems. + +- On connection failure a warning message is printed in the session + log. + +- Removed the warning that was previously printed when the MIT-SHM + extension failed to be initialized on Windows. Shared memory has + problems on Cygwin and it currently doesn't work in NXWin. + +- Reworked the message printed in the case of link failure. + +nxcomp-1.5.0-27 + +- Solved a bug in the connection procedure introduced by 1.5.0-25. + +nxcomp-1.5.0-26 + +- Transformed the errors printed on failure of the fork() creating + the children into warnings. This can happen quite often on Win- + dows, due to well known Cygwin problems. See also the ChangeLog + entry for nxcomp 1.4.0-28. This patch closes the TRSL052278 but + it's obviously not a long-term solution. + +- Set the sticky bit when creating the '/tmp/.X11-unix' directory. + +- Modified the Makefile.in to remove the *.out.* files generated by + Valgrind. + +- Updated the README files. + +nxcomp-1.5.0-25 + +- Changed the directory where the client for the Mac is searched if + it is not found in the system path. + +- Modified Auth.cpp to use nxauth also on the Mac. + +- Reworked the procedure showing the alert dialog when a timeout is + encountered in the initial connection. + +- Removed the experimental code from the official 1.5.0 branch. + +nxcomp-1.5.0-24 + +- Implemented more experimental classes. + +nxcomp-1.5.0-23 + +- Added the NX_SIGNAL_FORWARD action to NXTransSignal(). This can + be used to let the proxy call the original signal handler of the + agent after having blocked the signal. + +nxcomp-1.5.0-22 + +- Ensured that we always have a context, even before creating the + transport. + +- Suppressed the error message printed when passing the -h option. + +- Added the experimental code that is currently under development. + +nxcomp-1.5.0-21 + +- Enabled the fake X cookie authentication. This requires checking + the remote proxy version to verify that the server supports the + new authorization mechanism. + +- It's worth noting that the X client side proxy doesn't care which + cookie is sent over the connection. The problem is that clients + connecting to 1.4.0 servers don't have a method to force the ser- + ver to use the fake cookie. This means that we have to solve the + problem by letting the proxy check the remote version so that it + can omit to replace the cookie when connecting to older servers. + +nxcomp-1.5.0-20 + +- Added the NXTransCongestion() function. It returns true if proxy + is in congestion state. + +- Removed an incorrect warning that was printed when calling force() + for the memory-to-memory transport. The agent could actually re- + quire multiple loops to read all data queued for it. + +nxcomp-1.5.0-19 + +- Small optimization in NXTransReadable() to run a new NXTransConti- + nue() loop only after at least RetryTimeout milliseconds are pas- + sed since the last call and still no data is available. This cuts + the number of unneeded loops to 1/4th of the total, probably more, + on faster machines. + +- Added NXCollectGrabPointerNotify to NXproto.h. + +nxcomp-1.5.0-18 + +- Minor changes to NXTransContinue(). + +nxcomp-1.5.0-17 + +- Moved respawning of a new nxclient instance in the cleanup procedu- + re. This ensures that the respawn is executed whatever is the rea- + son of the session shutdown. + +- Added a method to force closure of a given channel in proxy. + +- Removed code handling the special case triggered on Windows by the + presence of a NX_SESSION variable in the environment. + +nxcomp-1.5.0-16 + +- Added the NXTransSignal() function to let agents tell to the proxy + how to handle the standard POSIX signals. Given the SIGINT signal, + for example, the caller can specify any of the following actions. + + NX_SIGNAL_ENABLE: A signal handler will have to be installed by + the library, so that it can be intercepted by + the proxy. + + NX_SIGNAL_DISABLE: The signal will be handled by the caller and, + eventually, forwarded to the proxy by calling + NXTransSignal() explicitly. + + NX_SIGNAL_RAISE: The signal must be handled now, as if it had + been delivered by the operating system. This + function can be called by the agent with the + purpose of propagating a signal to the proxy. + + As a rule of thumb, agents should let the proxy handle SIGUSR1 and + SIGUSR2, used for producing the NX protocol statistics, and SIGHUP, + used for disconnecting the NX transport. + +- The following signals are blocked by default upon creation of the + NX transport: + + SIGCHLD These signals should be always put under the control + SIGUSR1 of the proxy. If agents are intercepting them, agents + SIGUSR2 should later call NXTransSignal(..., NX_SIGNAL_RAISE) + SIGHUP to forward the signal to the proxy. + + SIGINT These signals should be intercepted by agents. Agents + SIGTERM should ensure that NXTransDestroy() is called before + exiting, to give the proxy a chance to shut down the + NX transport. + + SIGPIPE This signal is blocked by the proxy, but not used to + implement any functionality. It can be handled by the + NX agent without affecting the proxy. + + SIGALRM This is not blocked by the proxy, but could be used + in future. + + SIGVTALRM These signals are not used and should not be used in + SIGWINCH future versions of the library. + SIGIO + SIGTSTP + SIGTTIN + SIGTTOU + +- By calling NXTransSignal(..., NX_SIGNAL_DISABLE) nxcomp will res- + tore the signal handler that was saved at the time the proxy hand- + ler was installed. This means that you should call the function + just after the XOpenDisplay() or any other function used to init- + ialize the NX transport. + +nxcomp-1.5.0-15 + +- In NXTransContinue(), if the transport is gone, return immediately, + that is without having to wait until the NXTransSelect() timeout. + +- Ensure that NXTransCreate() has a jump context, just in the case + a subsequent operation would cause a cleanup. + +nxcomp-1.5.0-14 + +- Solved a problem with requests left in the agent's buffer when run- + ning the NX transport. The agent could have enqueued data to our + side and checked the available events but requests could not be + written to the proxy because proxy might not have had a chance to + enter a new select. We found that this behaviour was triggered by + _XEventsQueued, so now a new loop is forced when agent is calling + _X11TransDataReadable. The procedure can be optimized, by avoiding + an expensive loop when no critical I/O is pending. + +- Added a few additional logs to ClientChannel and ServerChannel. + +nxcomp-1.5.0-13 + +- Added the code handling the special cases of an user not specifying + a proxy cookie or the case of the X authorization file not contain- + ing a value matching the display. In the first case we'll forward + the same cookie that was feeded to the proxy, in the second case we + will forward to the X server a random generated cookie, similarly to + what SSH does in this same condition. + +- Rewritten the command line parser. Removed all the command line + options parsed on behalf of nxproxy except: + + -C Specify that nxproxy has to run on the "X client" + side, listening for connections and impersonating + an X server.\n\ + + -S Specify that nxproxy has to run in "X server" mode, + thus forwarding the connections to daemons running + on the client.\n\ + + -V n.n.n Request nxproxy to load the given nxcomp version. + This option is only present on Solaris and Linux. + + -v Print version information. + + host:port Put at the end, specifies the host and port of the + listening proxy. + + value=name Set the NX option to the provided value. + + Multiple NX options can be specified in the DISPLAY environment or + on the command line, by using the nx/nx,name=value notation. + +- The above information is printed on the console when incurring in + a parse error, together with a list of the available option=value + parameters. + +- Renamed the 'log' option to 'errors'. This makes sense as the de- + fault name for the log file is actually 'errors'. + +- Now the "Established X server connection" message is printed to + the session log only after the X connection has passed the X auth- + entication phase. This means that the NX client should become able + to show the details of the session log whenever the session fails + due to a cookie problem. + +- When selecting the additional services without specifying a port, + the client proxy will now automatically forward the connections to + the corresponding well-known ports of the CUPS, SMB and HTTP servi- + ces. Embedded X keyboard connections will be automatically forward- + ed to the same display port used to connect to the X server. The + user will still have to specify the port to be used for the media + connections as we don't have a suitable well-known port. + +- Starting from this version, connections to the keybd port will cre- + ate real X connection channels. This is required to let connections + leverage the fake authorization cookie. + +- By testing the forwarding of keybd connections I found that, when + letting X clients connect to the port, it is required to provide + the X cookie for the unix display. Adding only the TCP cookie will + not work. For example, by creating a cookie as in: + + xauth add localhost:2009 MIT-MAGIC-COOKIE-1 6f...f4 + + And running: + + xterm xterm -display localhost:2009 + + You will get the following error: + + Xlib: connection to "localhost:2009.0" refused by server + Xlib: No protocol specified + + Adding also the unix cookie will fix it: + + xauth add localhost/unix:2009 MIT-MAGIC-COOKIE-1 6f...f4 + + This seems to be a Xlib problem, with Xlib trying to get the cookie + for the UDS port even if the TCP port was requested by the user. + +- Fixed a bug that prevented the nxclient dialog to be displayed when + the session was abruptedly shut down. + +- Fixed the compilation error on Apple MacOSX due to the sa_restorer + field in sigaction. As long as this field is present on Linux we + will keep following the safer route and will set it explicitly to + NULL. + +- Included what needs to be included in Process.cpp to compile with + older gcc. + +nxcomp-1.5.0-12 + +- Implemented a replacement for the popen() and pclose() that do not + rely on a shell to run the command. They were required on Windows, + where we don't ship a suitable shell in the install. + +- Removed code forcing the PATH to include the bin directory on + Windows. + +nxcomp-1.5.0-11 + +- Forced Auth.cpp on Windows to have the directory containing the + nxauth executable in the PATH. This is just for testing, until + nxclient is fixed. + +- Fixed compilation errors on Cygwin and Sun. + +- Removed the NX_FORCE_* stubs. + +nxcomp-1.5.0-10 + +- Modified the memory management policies in ReadBuffer to fit all + the available bytes in a single buffer allocation. + +- The locateMessage() methods now give hints on the amount of data + that has to be read. + +- The read loop in channel now doesn't yield in the case of prio- + ritized messages. This is experimental. + +- Removed the check on isTimeToYield() between encodings of multi- + ple messages. This is aimed at reducing the risk of leaving pen- + ding messages in channels. + +- Modified the channels' read loop to always read all the available + data. + +- Disabled the log output that was selected when compiling with the + configure option --with-info. This leaves space for other log out- + put to be selected for more up-to-date scopes. + +- Implemented the NXTransReadVector() and the NXTransWriteVector() + functions to replace READV() and WRITEV(). + +- Implemented memory-to-memory communication between the agent and + the NX proxy by making use of the NXTransAgent() interface in the + nx-X11/lib/X11/Xtranssock.c file. + +- Added a check in NXTransSelect() for the EBADF and, on Solaris, + the EINVAL errors. It can happen in the X11 code that a descript- + or has become invalid before it is removed from the managed set + +- Rewritten the signal handling functions to restore the old actions + and masks when the NX transport is destroyed. + +- Added a NXTransAgent() function to let agents tell the proxy which + descriptor must be used for the controlling connection. Setting a + controlling connection has the effect of disabling further X client + connections and makes the proxy terminate when the channel is shut + down. + +- Solved a problem with setting the initial timeout in the select(). + +- Modified the Makefile.in to not include -Wno-deprecated when compi- + ling C programs. + +nxcomp-1.5.0-9 + +- Fixed a problem that prevented the 1.5.0-8 to work on the NX server + side. + +- This version has NX_FORCE_NULL_LISTEN_OPTION and NX_FORCE_NEW_SES- + SION_OPTION undefined, so it should work in a way that is compati- + ble with the old nxcomp. + +nxcomp-1.5.0-8 + +- The new code comes with a preliminary integration of nxcomp with + SSH. It is now possible to create the NX transport by just calling + the "switch" command as in the following example: + + NX> 299 Switching connection to: NX options: ... + +- Other possible forms for the NX switch command are: + + NX> 299 Switching connection to: NX mode: ... + + NX> 299 Switching connection to: NX mode: ... options: ... + + Or just: + + NX> 299 Switching connection to: NX + + The "mode" parameter is there to provide a way to run both enc- + rypted and unencrypted connections. Possible values are "encrypt- + ed", "unencrypted" or "default", the latter being an alias for + "encrypted". Unfortunately I was not able to test unencrypted + connections, so this may or may not work. + +- The top-level process can create the NX transport layer by calling + NXTransCreate(). The user has to set up a socket pair and pass the + higher descriptor to nxcomp. nxcomp will later monitor its end, by + reading and writing NX-encoded traffic. The user has to call the + NXTransExecute() function as often as it is possible, by letting + first NXTransPrepare() combine the sets of NX descriptors with the + descriptors that are used inside its process. A custom NXTransSel- + ect() is provided to optionally replace the original select(). This + function saves the original error code and the number of selected + descriptors upon exit, so the user can call it, restore the original + values as they were returned by the select() and run the rest of + the loop unmodified. + +- Future versions of the library should provide appropriate methods + for passing data to and from the proxy by means of a memcpy(), so + that it will be possible to remove the even minimal TCP overhead. + +- Note that integration is far from complete. More work is required + especially to manage the shutdown cleanly, in a way that gives to + SSH a chance to free its resources, and on adding facilities for + handling SSH and NX signals in a single function. + +- Rewritten the initialization procedure to make possible to run the + proxy in-process in an arbitrary connection manager, like SSH or a + HTTP utility. The same functionality can be used to embed nxcomp in + the NX agents, so that nxcomp has not to run in a separate process. + +- A new state-machine handles the advances through the connection + stages until the remote proxy (or the forwarder process ) is auth- + enticated, options have been negotiated and the session is started. + +- The option "session" is now used to pass the name of the session + file to the proxy. The parameter was previously used to pass the + literal name of the session, as set by the user, and was ignored + by the proxy. By default the session log is the standard error of + the process. It is anyway required to set the option when running + inside SSH, otherwise output will go to the same file as the SSH + output. In the NX client this would be the 'sshlog' file. + +- This version can be easily crafted to test the new integration by + setting the following define: + + NX_FORCE_NULL_LISTEN_OPTION + + This makes possible to test the nxcomp/nxssh integration by using + any production 1.4.0 client. To run this version you are required + to use nxproxy 1.5.0-4 and nxssh-1.5.0-6. + +- New functions handling enabling and disabling signals, based on + sigprocmask(). + +- Improved error reporting when failures are encountered while ne- + gotiating the session. + +nxcomp-1.5.0-7 + +- Caches are saved with a version identifier 1.4.0 so that they are + not discarded after upgrading the software to the 1.5.0. + +- Made values of T_* enumerations to be all lower case. + +- Updated copyright to year 2005. + +- Started working on an interface for running nxcomp in-process, as + a additional transport layer of nxssh or nxagent. + +nxcomp-1.5.0-6 + +- Modified the configure script and the makefiles to not include the + -Wstrict-prototypes and -Wmissing-prototypes compilation flags. The + -Wnested-externs and -Wmissing-declarations flags are not included + when using GCC 3. + +- Removed the initial newline from string "NXPROXY - Version" printed + at program startup. + +- Made X authentication compatible with 1.4.0 clients. This is a tem- + porary solution while code is updated to handle the new X authori- + zation scheme. + +nxcomp-1.5.0-5 + +- Added an Auth class to handle the X authentication. The NX server + should now use the same proxie cookie that is passed by the client + at session startup. The X server side proxy will forward the autho- + rization credentials by replacing the fake cookie with the real co- + okie, as it is read from the auth file using the xauth utility. The + implementation is based on the corresponding code found in the SSH + client and comes with the same limitations: only MIT-MAGIC-COOKIE-1 + cookies are checked and the authorization file is read only once, + at the time the instance is initialized. If the auth file changes + along the life of the session, the old cookie will still be used. + This works with X servers because of an undocumented "feature". + See also nx-X11. + +nxcomp-1.5.0-4 + +- Adjusted for alarm(0) returning an inconsistent value in ConnectTo- + Remote(). + +- Small changes to Types.h, Jpeg.cpp and Png.cpp to compile with gcc + 3.4.2. + +- Cosmetic changes to the ChangeLog file. + +nxcomp-1.5.0-3 + +- Removed test code from ServerChannel.cpp. + +- Small cosmetic change in Loop.cpp. + +- Tested for compatibility against 1.4.1-8. + +nxcomp-1.5.0-2 + +- Changed VERSION file according to TRCL052336. + +nxcomp-1.5.0-1 + +- Opened the 1.5.0 branch based on 1.4.1-9. + +nxcomp-1.4.1-9 + +- Implemented methods PACK_RDP_PLAIN_64K_COLORS and PACK_RDP_PLAIN_- + 16M_COLORS. It seems that 16 bpp plain bitmaps can be seldom recei- + ved. This doesn't seem to be the case of 24 bpp bitmaps. + +nxcomp-1.4.1-8 + +- The new code enables use of 16 bpp and 32 bpp RDP bitmaps. This re- + quired modifications to the following files: + + Misc.h + Misc.cpp + Loop.cpp + Control.cpp + ServerChannel.cpp + Unpack.h + Unpack.cpp + +- Lots of cosmetic changes compared to the original code in 1.4.1-7. + +- Merged the 1.4.0 and the 1.4.1 branches by importing changes up to + the 1.4.1-7. + +nxcomp-1.4.0-30 + +- Modified the function checking the Unix socket where X connections + will be forwarded. The function will not fail if the socket itself + doesn't exist yet. This solves the TRCL042203. + +- Moved the code checking for the CTRL+ALT+SHIFT+ESC sequence to a + separate function. The implementation will now look for a different + sequence on the MacOS/X platform. This should solve the TRCL042182. + +nxcomp-1.4.0-29 + +- Solved a bug that could cause proxy to stop reading data from the + X channels due to an incorrect calculation of the bytes queued on + the proxy link. The bug only affected Linux kernels of the 2.0/2.2 + series. + +- Fixed a problem in the proxy class that could let the select time- + out to become zero. + +nxcomp-1.4.0-28 + +- Prevented the main proxy process from quitting when the fork() of + any of the children fails. This can actually happen on Windows due + to conflicts in reallocating any of the Cygwin DLLs. + +- Reworked handling of the priority flag in channels when dealing + with the X_InternAtoms and X_AllocColor requests to reduce the time + of session startup. Priority is never set in case of X_InternAtom + requests and replies as we assume that most clients use the appro- + priate Xlib function to pipeline multiple requests in a single net- + work operation. + +- The timeout after which proxy will abort the peer connection is + still 120 seconds. An alert dialog will be shown earlier, after 30 + seconds instead of 60. + +nxcomp-1.4.0-27 + +- Removed the code setting priority on channels on ButtonPress and + Button release events. It seems preferrable to delay the flush and + pack more events in a single frame. This makes possible to get the + ButtonRelease together with the ButtonPress. + +- Fixed error detection in SetNoDelay() where a positive result + could produce a 'not supported' message in debug mode. + +nxcomp-1.4.0-26 + +- Reworked the check aimed at detecting the clock drifts. The previ- + ous code, introduced in 1.3.2-2, had the undesired side-effect of + resetting the proxy timeout. This could cause the proxy to never + detect that the other end had been killed. + +- The default behaviour of proxy is now to terminate the session at + the time an error is encountered. This includes network failures. + +- Added a new dialog to be shown to the user whenever the session + is terminated abnormally. + +nxcomp-1.4.0-25 + +- Fixed a bug in handling of suppression of errors generated by + committing the image splits. Other errors, unrelated to the commit + of split, could be suppressed. This might cause the hangup of the + session when the suppressed error was matching a reply. + +nxcomp-1.4.0-24 + +- When killing the proxy process -9 the watchdog could remain alive. + This caused the SSH link to keep the stderr open, with the effect + that former proxy message were not flushed to the session log. + Now the watchdog checks every second if the parent is dead, so + that file descriptors can be closed as soon as possible. + +- SetupDisplaySocket() tried to force the connections to the X + server on the UNIX port whenever the display was set to localhost. + This patch was intended to fix a bug in nxclient, using the TCP + port even when the display was set to a UNIX socket. This beha- + viour caused problems when running nxclient on a remote host by + means of a ssh -X. Proxy will now adhere to the display setting. + +nxcomp-1.4.0-23 + +- Added the possibility to respawn nxclient at the end of session. + This is an useful feature when powering thin-clients where NX + is the only application made available. + +- This configuration applies system-wide to the local client ins- + tallation. By default it is disabled. It can be enabled by the + by creating a 'noexit' file in the directory '/usr/NX/share', + or at compile time, by setting ENABLE_RESTART_ON_SHUTDOWN to 1. + +- Note that the solution is not perfect yet, as there are cases + where session could die without going through HandleShutdown(), + for example if a decoding error is raised in the communication + with the remote peer and the subsequent restore of the proxy + link fails to succeed. + +nxcomp-1.4.0-22 + +- Corrected typos in Timestamp.h and Loop.cpp. + +nxcomp-1.4.0-21 + +- Tuned the handling of RenderCompositeGlyphs. Now compression of + RENDER shows an average ratio of 8:1. This is a steady increase + compared to the 5:1 of the 1.3.2 version. The overall advantage + when running clients displaying a large amount of text is 30%. + +nxcomp-1.4.0-20 + +- Added a new message store for the CreatePixmap request. + +- Fixed handleCopy() to only send the data part past the offset. + This fix can save a significant amount of traffic, especially + when sending many small images whose size doesn't reach the + threshold set for compressing them. + +nxcomp-1.4.0-19 + +- Added tuning of all the RENDER requests. RenderCompositeGlyphs + will require further work. + +- Completed porting of the RENDER requests to the new templates. + +nxcomp-1.4.0-18 + +- Tuned the handling of RenderCreatePicture and RenderFreePicture. + +- Created a new template system for writing new message encoders. + +- Renamed getBits() in EncodeBuffer with diffBits(). The getBits() + method now returns the bits actually allocated in the buffer and + can be called multiple times. + +nxcomp-1.4.0-17 + +- Provided specific encoding of the remaining RENDER requests and + added compression of RenderCreateGlyphSet. + +nxcomp-1.4.0-16 + +- Code underwent through 7 different patch versions but seems to + be stable now. It still needs to provide specific encoding for + RenderCreatePicture RenderChangePicture and RenderFreePicture. + Code will likely include incompatible changes at any new version + until the new encoding will stabilize, so be sure that you use + the same nxcomp version at both sides. + +- Created the infrastructure for compressing extension messages + based on the minor opcode. A new class, MinorMessageStore, has + to be inherited by stores requiring specific compression, so + that all the encoding methods of the parent can be redirected + to it. The RENDER extension is now compressed using this new + class. + +- Managed to maintain the compatibility with caches created by the + 1.3.X version. Caches will be saved using the old format if the + remote version is not a 1.4.x. When restoring a cache created + by the 1.3.x with both proxies being the newest version, RENDER + mesages will be discarded without having to discard the whole + cache. + +- Added a member to the message class to store the identity size. + This field is now used instead of the default data offset to + manage the message data, that is the part of message after the + identity. + +- Removed the warnings printed in the session log when passing the + 'kbtype' and the 'geometry' options to the proxy. These options + are actually used by agents. + +nxcomp-1.4.0-15 + +- Started implementation of the new framework for handling enco- + ding of X extensions. + +- Renamed the overloaded methods handleEncode() and handleDecode() + to handleEncodeCached(), handleEncodeIdentity() and handleDecode- + Cached(), handleDecodeIdentity(). + +- Updated the copyright notice to year 2004. + +nxcomp-1.4.0-14 + +- Added the new channel for HTTP connections. This channel can be + used to let applications running on the NX server get access to + data and applications made available by a HTTP daemon running at + the client side. + + This channel is not used at the moment by nxclient. It can be + activated by passing http=1 to the NX server side proxy and the + value http=80 to the NX client side. + +nxcomp-1.4.0-13 + +- Quick patch to run on MacOS/X where inet_addr("127.0.0.1") + alone seems to fail to return a valid interface. + +nxcomp-1.4.0-12 + +- Few cosmetic changes to logging. + +- Removed the debug output. + +nxcomp-1.4.0-11 + +- Removed code used for simulating the new forwarding function- + ality without client and server support. + +nxcomp-1.4.0-10 + +- Implemented authentication of the forwarder to the listening + proxy. If a session cookie is required, the forwarder must + provide the cookie upon connection. + + An authentication phase at the time the forwarder connects + to the NX client side is strongly suggested because it is usu- + ally this side that sends the authorization cookie. Without + such a forwarder authentication, the local peer would reveal + the cookie to the first process connecting to the port. + +- If no cookie is specified in the switch command, the forward- + er is required to skip the authentication phase. This can be + useful when running the programs on the command line. + +- The implementation required appropriate changes to session + negotiation in nxssh. This version requires nxssh-1.4.0-8. + +nxcomp-1.4.0-9 + +- Fixed the startup procedure to correctly negotiate the cookie + when the X server side proxy is listening for a forwarder. + +- When listening for a local forwarder (that is when the listen + option is enabled at X server side), proxy will listen for the + forwarder on the localhost interface. + +- This version has ports and and other parameters hardcoded for + testing purposes and is not intended for normal use. + +nxcomp-1.4.0-8 + +- Slightly modified the text of FAILED_PROXY_CONNECTION alert. + +nxcomp-1.4.0-7 + +- Added provision for leaving a running dialog showing an OK box + if proxy is exiting abnormally. This seems to fail to yield the + expected results as, at the present moment, client checks if + parent has exited. + +- Added a Binder class invoked when calling proxy with -B option. + It would serve as a replacement of the modifications I'm doing + in nxssh. The class is just a framework and the implementation + is unfinished. + +nxcomp-1.4.0-6 + +- Added a line in the session log if the connection procedure is + aborted due to a signal. + +- Fixed ParseHostOption() to let it take in account the proxy + port offset when passing 'host:port' as the last parameter at + X server side. + +nxcomp-1.4.0-5 + +- Lowered the default port offsets used for CUPS and SMB services + to 2000 and 3000. Arbitrary ports can be used by passing the ser- + vice's port at proxy startup. By default, anyway, the port is at + the same offset of the proxied display. Considering that the NX + server uses to start the first session at display 1000, we must + lower some of the defaults to avoid interference with the normal + X sessions run on the server. + + Session at display ":1000" will have: + + . Forwarding of CUPS connections at 3000. + + . Forwarding of SMB connections at 4000. + + . The listening proxy (at NX server side) at 5000. This + port is not used when enabling SSH tunneling + + . The proxied X display (NX agent or proxy) at 7000. + + . Forwarding of multimedia channels at 8000. + + . Forwarding of embedded keyboard connections at 9000. + +- Turned off the log output. + +nxcomp-1.4.0-4 + +- Managed to get the X client side proxy connect to the remote + peer. This means that some parameters that were implicitly + assumed based on the proxied port simulating the X display + must be now specified in the NX display string. + +- The 'port' parameter now specifies the remote TCP port where + the local proxy will connect to the remote peer. In previous + versions this parameter also affected the proxied port. This + was mainly a side effect, as the remote peer was always con- + tacted at port 4000 plus the proxied display offset. + +- Added the 'listen' parameter to tell to the proxy that is go- + ing to accept the peer connection on which port it will be + contacted. By default, similarly to the previous version, the + proxy will listen at the proxied display offset. + +- A check has been added in order to disallow passing both the + 'accept' and the 'connect' parameter at the same time. A simi- + lar check affects the 'listen' parameter. + +- Renamed the previously unused 'sync' channel as 'cups'. + +nxcomp-1.4.0-3 + +- Preserved the ability of version 1.3.2 to load caches generated + by this version. + +nxcomp-1.4.0-2 + +- Small fixes to compile under Solaris 8. + +nxcomp-1.4.0-1 + +- Opened the 1.4.0 branch. + +nxcomp-1.3.2-4 + +- Fixed a problem with shmget(). Code in ServerChannel checked + the return value for being greater then 0, while 0 can actual- + ly be a valid segment. + +- When memory cache is disabled, nxcomp will explicitly set the + appropriate control variables dealing with loading and saving + of the persistent cache. This can be considered a cosmetic + change as nxcomp will disable NX delta compression if memory + cache is not available and this has an implicit effect on the + ability to load and save such a cache. + +nxcomp-1.3.2-3 + +- Removed inclusion of zlib.h in Png.cpp. Conflicting symbols + with zlib.h from nx-X11 could cause compilation to fail. + +nxcomp-1.3.2-2 + +- Added a function to take into account the clock drifts at the + time we check the ping from the remote proxy. This can be + caused by the user changing the system time or by small adjust- + ments introduced by the operating system making the clock go + backward. Such events could cause the proxy link to be shut + down and reconnected. + +- Reduced the length of lines printed in statistics when showing + the details of X protocol's opcodes. This is intended to help + nxclient to keep the whole statistics in the 'details' window, + so that users don't have to use the scrollbars. + +nxcomp-1.3.2-1 + +- Opened the 1.3.2 branch. + +nxcomp-1.3.1-5 + +- It seems that Solaris can return an EINVAL error selecting a + shutdown descriptor even before we actually close the socket. + We now ignore the condition on Solaris. This is definitely a + Solaris bug. + +nxcomp-1.3.1-4 + +- Increased the timeout after which proxy will abort the peer + connection to 120 seconds. An alert dialog will now be shown + after 60 seconds, instead of the 30 seconds being the default + in the previous version. Some users reported the timeout to + be too short in the case of temporary network failures. + +nxcomp-1.3.1-3 + +- Some optimizations in the cache house-keeping process. Now it + runs at lower system priority in respect to the parent. Any 2 + iterations through directories and images, it also sleeps for + a tiny amount of time. This further decreases the system load. + +- Removed the underline characters and added a colon in the + title of this ChangeLog to fully comply with format used in + the release notices. + +nxcomp-1.3.1-2 + +- Fixed a crash when running both client and server on the SPARC + Solaris. An optimization used to avoid byte-swapping when both + hosts have the same endianess doesn't work on SPARC if buffer + is not aligned to word boundary. A better version of the code + should check the CPU, not the OS, as this probably applies to + other processors. + +nxcomp-1.3.1-1 + +- Opened the 1.3.1 branch. + +nxcomp-1.3.0-50 + +- Disabled the processor limit in X client side proxy. In previous + versions the processor load limit was set to an idle time ratio + of 2. This was likely to cause an unwanted slowdown on very old + hardware or when running the server as guest OS inside a VMWare + virtual machine. + +nxcomp-1.3.0-49 + +- Last minute update on proxy shutdown bug on Cygwin. A stack + trace reveals that faillure happens in the static destructor of + the BlockCacheSet class. It seems that problem appeared just + after having upgraded to the latest version of Cygwin DLL. Now + the destructor is skipped at shutdown. + +nxcomp-1.3.0-48 + +- Further fix to overcome the shutdown problem on Windows. + +nxcomp-1.3.0-47 + +- Used T_files::value_type() in Keeper.cpp. The form without the + explicit constructor fails to compile with GCC 2.91 on RH 6.2. + +- Fixed '==' -> '=' in configure.in to build under RH 6.2 and + probably other platforms. + +nxcomp-1.3.0-46 + +- The cleanup procedure now skips deletion of the IO streams + under Windows. This is intended to overcome a strange segfault + occurring at random time, at the time proxy is being shutdown. + +nxcomp-1.3.0-45 + +- Newer versions of the stdlibc++ do not seem to be able to deal + with NULL strings or non printable characters. This caused the + standard error stream to get sometimes corrupted in the case + TEST and DEBUG logs were enabled in Loop.cpp. + +nxcomp-1.3.0-44 + +- Disabled TEST and DEBUG logs in ServerChannel. + +nxcomp-1.3.0-43 + +- Fixed a crash when unpacking an image in server proxy in the + case the unpack state for the given channel had not been + previously created. + +nxcomp-1.3.0-42 + +- Small modification to setSchedule() to not account previous + data accumulated in the encode buffer. The new code seems to + allow better use of the available bandwidth. + +nxcomp-1.3.0-41 + +- Further fix in Tight decompression. Content of the main write + buffer was not removed before flushing the unpacked data to + the X server link. + +nxcomp-1.3.0-40 + +- Removed the code marked as FIXME in Loop and ServerChannel. + Code was used in previous version to override any pack method + other than NO_PACK and to disable initialization of the shared + memory. + +nxcomp-1.3.0-39 + +- Solved the problem with unpacking of RDP text on big-endian + architectures. The width field in PutPackedImage was put as + 16 bits and extracted as 32 bits at decoding side. + +- Moved the code checking if any of the children has exited in a + specific function. This function is now called any time signals + are newly enabled. This works even on Solaris. + +- Enabled use of shared memory with Tight image decompression. + +- Added event NXCollectPropertyNotify to NXproto.h. + +nxcomp-1.3.0-38 + +- Message locks are now checked before splitting a message. + +nxcomp-1.3.0-37 + +- Added a further counter to yield encoding data if the amount + of output bytes to be written to channels exceeds a threshold. + +- Modified proxy to write data immediately if the encoding loop + was interrupted. + +nxcomp-1.3.0-36 + +- Further fix required by Tight decompression. By handling mul- + tiple writes in the Tight class we may flush the main write + buffer in the wrong order. If Tight decompression is enabled, + the buffer is now flushed before decoding the packed image. + +- A SIGCHLD is raised any time signals are newly enabled. This + allows the main loop to wait() the pid of children that had + exited while signals were disabled. + +nxcomp-1.3.0-35 + +- Fixed Tight decompression by handling multiple writes to the + channel's transport inside the decompression function. + +- Made Tight decompressor in ServerChannel a pointer instead of + an instance of the class. Class is allocated the first time it + is referenced. + +- Rewritten handleUnpack() in ServerChannel to use a switch() + on the pack method instead of multiple if(). + +nxcomp-1.3.0-34 + +- Disabled RENDER extension when running X agent sessions on + the Solaris client. Problems seem to be caused by incompati- + bilities in the implementation of RENDER on the Sun X server + shipped by default. We did not test NX with the XFree86 X + server, but it seems that, when using the Sun X server, even + plain X clients don't make full use of the extension. + +nxcomp-1.3.0-33 + +- Added 4 new fields to the X_NXGetControlParameters reply. + A dialog should be shown by agent if proxy was not able to + negotiate a persistent cache or if a cache was selected but + not loaded because incompatible or corrupted. + +- Fixed a bug on Solaris where socket() must be recreated if + the first connect() fails. Not a problem. The strange thing + is that if you reuse the socket(), the subsequent connect() + is successful and the program fails as soon as you try to + write to it. + +nxcomp-1.3.0-32 + +- Added request X_NXFreeUnpack to free the resources allocated + by the remote proxy to unpack images for the given agent's + client + +- Added an alert at X server side if proxies were not able to + negotiate a persistent cache or if a cache was selected but + not loaded at X server side (that means that probably cache + was not compatible or corrupted). The alert is not enabled + at the moment. Before enabling it, we need to find a way to + deal with full-screen mode. + +- Tuning of MIT-SHM parameters. + +nxcomp-1.3.0-31 + +- Modified the following requests to carry the id of the + agent's client: + + - X_NXSetUnpackGeometry + - X_NXSetUnpackColormap + - X_NXSetUnpackAlpha + - X_NXPutPackedImage + + Now each agent's client has its own record pointing to the + geometry, colormap and alpha channel that will be used to + unpack the image. Code is not finished yet so it is advisable + that agents' writers keep using client id 0. Note also that + the final solution will require some changes to the way split + notifications are sent to agent that I plan to implement in + the next versions. + +- The X_NXSetUnpackGeometry request has now a message store. + This should reduce the overhead to the minimum even in the + case of dumb agents not checking if the current geometry + matches the image to be unpacked. + +nxcomp-1.3.0-30 + +- Improved handling of short-circuited replies at X client + side. Request opcodes are now pushed in the sequence queue + to determine if a reply is expected. In this case tainting + of reply is skipped to preserve the sequence ordering of + any event or error that could have generated by the reply. + This seems to solve all the problems reported by people + running a NX session in virtual desktop mode nested in an + existing session being run in single application mode. + +- Modified the cleanup procedure to print the 'Waiting for' + message in the session log only at the time all X channels + have been actually shut down. This message can be used by + the NX server to find out the appropriate time to close + the session. + +- Added the selected session type to the 'Using pack method' + message printed at startup. + +- Added the option to close down the proxy link and perform + a clean shutdown of the session, re-read the whole session + configuration or restart the proxy link when a SIGHUP is + delivered to the proxy process. The default behaviour is + to close down the session. The new default (the old one was + to restart the link) is a required feature to let users run + full sessions using single application mode f.e. in a LTSP + environment. I noted that when the controlling terminal of + the X session is closed, a SIGHUP is delivered to the X + server side proxy (probably it is delivered to all the con- + nected clients, and, thus for NX, only to the proxy). This + signal can be used to terminate the whole session. + +nxcomp-1.3.0-29 + +- Changed defaults to disable image masks when running proxy + in single application mode. The new configuration requires + changes in NX server and in NX X11 library. Users should + experience much better image quality when using NX on slow + links. This comes at the cost of slightly worse compression + ratios. + +nxcomp-1.3.0-28 + +- Opcode of request was not rewritten by server channel when + using link LAN. This caused problems with taint of replies. + +nxcomp-1.3.0-27 + +- Modified the split store and the split procedures in client + channel to always return the client id in the notification + events. The new field is added to event at byte offset 28 + to preserve compatibility with previous agent releases. The + modification permits to agent to match the commit of alpha + channel with the original image, if requests are split by + proxy. + +- Temporarily set the timeout used to poll MIT-SHM completion + events coming from X server to 0. More testing is required. + +- Fixed an inconsistent message generated in statistics due to + a division by 0. + +nxcomp-1.3.0-26 + +- Modified pending timeout from 1 to 0 milliseconds. This means + that both channels and proxy are now immediately restarted to + let them consume all data left in their read buffer. + +- When querying the X server for MIT-SHM support, size of name + in X_QueryExtension request was sent as 32 bits instead of 16. + This caused request to fail on big endian architectures. + +- Modified the MIT-SHM initialization procedure to always send + all the 3 protocol requests also in the case of early failures. + +nxcomp-1.3.0-25 + +- Implemented handling of the new X_NXSetUnpackAlpha message. + Performances are very satisfactory. Using most of the current + GNOME and KDE applications, caching reaches 90% of the total + messages. + +- Modified the persistent cache management routines to handle + backward compatibility with proxy versions prior of 1.3.0. + +- It has been made possible to send both X_NXSetUnpackColormap + and X_NXSetUnpackAlpha messages with 0 entries to temporarily + disable use of the colormap or the alpha channel and free the + resources allocated by the server channel class. + +- Added function UnpackAlpha() to Unpack.cpp. + +nxcomp-1.3.0-24 + +- Using WriteBuffer::registerPointer() to track growing of the + write buffer in handleFastWrite() functions. This problem + existed even in 1.2.2 but never shown up because we didn't + have to use the pointer after data had been written to the + write buffer. Now, instead, we have to post-process the write + buffer to copy data to the shared segment. + +- Optimized memory allocations running with link LAN to save a + memcpy() any time new data is allocated in the scratch buffer. + +- When running with link LAN the scratch buffer is now used only + when: + + - A further allocation would case growing of the write buffer + (and, thus, a memcpy() of the previous content). + + - When data to added is bigger than the write threshold. + + In previous versions the scratch buffer was used any time the + total amount of data to be written (write buffer + scratch + buffer) exceeded the scheduled write threshold. This caused + small writes to be appended even when a single write could + be obtained without reallocating the buffer. + +- Preliminary support for transporting the alpha channel in a + separate message in case of 32 bits displays using the RENDER + extension. + +nxcomp-1.3.0-23 + +- Modified the mask used to open the shared memory segment on + OS/X to 0777. We have to better investigate why the previous + 0600 mask doesn't work even if the user running the proxy is + the same user running the X server. + +nxcomp-1.3.0-22 + +- Implemented MIT-SHM support on LAN connections. + +- More MIT-SHM bug fixes. + +nxcomp-1.3.0-21 + +- Better use of the shared segment through an improved + algorithm leveraging the offset field of the X_ShmPutImage + request. The new algorithm greatly reduces the amount of + polls the proxy needs to perform to find if the completion + event has arrived. + +- Implemented MIT-SHM support for X_PutImage requests. + +- Implemented option shmem=value. Use of this option is anyway + discouraged. Proxy will allocate the shared memory segment + based on the size of the in-memory cache set by the user. + Use of MIT-SHM is disabled when user did set a memory cache + smaller than 2MB (for example on the embedded client). + +- Rewritten post-processing of images in server channel loop. + +- Improved error handling to ensure we intercept all MIT-SHM + X errors before they reach the NX agent. + +- Solved a problem that was causing channels to not reflect + shared memory support flags set in control. + +nxcomp-1.3.0-20 + +- Solved a compatibility problem when mixing proxy versions + 1.2.2 and 1.3.0. + +nxcomp-1.3.0-18 + +- Rewritten interfaces to shared memory initialization in + client and server channel. + +- Server channel checks for the completion event until a + timeout before reusing the shared memory segment. + +- Flush flag was not cleared after the write buffer had been + flushed in handleWrite() of both client and server channels. + This could lead to multiple fragmented writes, affecting + the performances. + +- Added -lcygipc to linking on Windows platform. + +- Added a check on GCC version to see if -Wnested-externs + -Wmissing-declarations are valid options. + +nxcomp-1.3.0-17 + +- Implemented initial support for MIT-SHM extension in the + network path between the X server proxy and the real X + server. Presently it works only for X_NXPutPackedImages. + +- Modified configure.in to compile under FreeBSD. + +- Small changes to sources due to FreeBSD support. + +nxcomp-1.3.0-16 + +- Fixed caching of RENDER extension on MacOS/X and Solaris. + +- Under Solaris an explicit call to EnableSignal() is needed + at the end of the signal handler as raising a signal seems + to reset the previous settings. + +- Can't find a way to get bytes queued for write on Solaris as + both FIONWRITE and TIOCOUTQ don't seem to be available. This + means that NX server on Solaris is only able to detect con- + gestions on proxy link at the time a write fails with error + EAGAIN. + +- Starting from this version, render extension messages are not + automatically discarded from cache when running agent based X + sessions. This is in preparation of render support introduced + in this release. + +nxcomp-1.3.0-15 + +- Changed default to force writes if X channels exceed buffer + limits. This change was suggested by benchmarks performed on + Win32. + +- Wrapped IO on cache files in functions performing better error + checking. + +- General cleanup in handling of socket options for MacOS/X and + Solaris. + +nxcomp-1.3.0-14 + +- Corrupted persistent caches were not deleted in case loading + of any of the message stores failed. To run further sessions + on the same host, user had to delete the cache file manually. + +- Improved error handling in JPEG decompression. Now connection + is reset in case of failure. + +- Before performing JPEG or PNG decompression, image is better + checked to verify if loading from disk failed. + +- Improved error handling in case of failure loading persistent + cache from disk. On MacOS/X istream -> fail() doesn't seem to + work properly. This needs further investigation. + +- The default installation path of nxclient is searched under + MacOS/X at the time nxclient is invoked in dialog mode. + +nxcomp-1.3.0-13 + +- Fixed a (further) compilation problem under Solaris. Now static + libraries are first searched under /usr/sfw/lib (in case Sun + would decide to include them in future releases). + +nxcomp-1.3.0-12 + +- Fixed parsing of command line when passing option -V. + +- Correctly detected ENOPROTOOPT when setting TCP_NODELAY socket + option on MacOS/X and Solaris. + +nxcomp-1.3.0-11 + +- Given option in configure to specify what needs to be built + statically: + + --with-static-png enable static linking of PNG library + --with-static-jpeg enable static linking of JPEG library + --with-static-z enable static linking of Z library + +nxcomp-1.3.0-10 + +- Fixed a problem in saving of persistent cache on big-endian + machines. + +nxcomp-1.3.0-9 + +- Testing with different settings to check if it's possible to + increase the performances under Windows. + +- Solved a problem in parsing of options that prevented proxy + to connect to a remote session running at port offset 0. + +- Fixed two warnings compiling on Solaris. + +- Changed configure.in to first check for nx-X11 includes + and libraries. Added "/usr/openwin/bin/makedepend" to path + searched for the executable. + +nxcomp-1.3.0-8 + +- Small cleanup in configure.in and files modified by Gregorz + Kryza to add support for Solaris. + +- A new configure script has been generated using autoconf-2.57-3. + +nxcomp-1.3.0-7 + +- Added support for detection of Solaris in configure script. + Now Makefile.in uses ranlib instead of ar. + +- Small changes in source and header files to support Solaris. + +nxcomp-1.3.0-4 + +- Corrected a bug that could cause priority on proxy and channels + to be not taken in account at the time proxy tries to determine + if it's time to flush the proxy link. + +- Better implementation of abort split notification by X server + proxy to its remote peer. The new implementation doesn't + need to set a timeout and permits notifications to be received + earlier. + +- Improved support for 'tainting' XSync() messages coming from + X clients in single application mode. Now a X_GetInputFocus + is sent to the real X server any n such messages received by + proxy. + +- Included support for 15 bpp displays. It seems that handling + them as 16 bpp it's OK. diff --git a/nxcomp/COPYING b/nxcomp/COPYING new file mode 100644 index 000000000..d511905c1 --- /dev/null +++ b/nxcomp/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/nxcomp/ChangeGC.cpp b/nxcomp/ChangeGC.cpp new file mode 100644 index 000000000..574651763 --- /dev/null +++ b/nxcomp/ChangeGC.cpp @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ChangeGC.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int ChangeGCStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ChangeGCMessage *changeGC = (ChangeGCMessage *) message; + + // + // Here is the fingerprint. + // + + changeGC -> gcontext = GetULONG(buffer + 4, bigEndian); + changeGC -> value_mask = GetULONG(buffer + 8, bigEndian); + + // + // Clear the unused bytes carried in the + // payload to increase the effectiveness + // of the caching algorithm. + // + + if ((int) size > dataOffset) + { + #ifdef DEBUG + *logofs << name() << ": Removing unused bytes from the " + << "data payload.\n" << logofs_flush; + #endif + + changeGC -> value_mask &= (1 << 23) - 1; + + unsigned int mask = 0x1; + unsigned char *source = (unsigned char *) buffer + CHANGEGC_DATA_OFFSET; + unsigned long value = 0; + + for (unsigned int i = 0; i < 23; i++) + { + if (changeGC -> value_mask & mask) + { + value = GetULONG(source, bigEndian); + + value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i])); + + PutULONG(value, source, bigEndian); + + source += 4; + } + + mask <<= 1; + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ChangeGCStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ChangeGCMessage *changeGC = (ChangeGCMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(changeGC -> gcontext, buffer + 4, bigEndian); + PutULONG(changeGC -> value_mask, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void ChangeGCStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + ChangeGCMessage *changeGC = (ChangeGCMessage *) message; + + *logofs << name() << ": Identity gcontext " << changeGC -> gcontext + << ", mask " << changeGC -> value_mask << ", size " + << changeGC -> size_ << ".\n" << logofs_flush; + #endif +} + +void ChangeGCStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ +/* + md5_append(md5_state_, buffer + 4, 8); +*/ + md5_append(md5_state_, buffer + 8, 4); +} + +void ChangeGCStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + ChangeGCMessage *changeGC = (ChangeGCMessage *) message; + ChangeGCMessage *cachedChangeGC = (ChangeGCMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << changeGC -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(changeGC -> gcontext, clientCache -> gcCache); + + cachedChangeGC -> gcontext = changeGC -> gcontext; +} + +void ChangeGCStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + ChangeGCMessage *changeGC = (ChangeGCMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + changeGC -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << changeGC -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif +} diff --git a/nxcomp/ChangeGC.h b/nxcomp/ChangeGC.h new file mode 100644 index 000000000..81b808d87 --- /dev/null +++ b/nxcomp/ChangeGC.h @@ -0,0 +1,177 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ChangeGC_H +#define ChangeGC_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define CHANGEGC_ENABLE_CACHE 1 +#define CHANGEGC_ENABLE_DATA 0 +#define CHANGEGC_ENABLE_SPLIT 0 +#define CHANGEGC_ENABLE_COMPRESS 0 + +#define CHANGEGC_DATA_LIMIT 144 +#define CHANGEGC_DATA_OFFSET 12 + +#define CHANGEGC_CACHE_SLOTS 3000 +#define CHANGEGC_CACHE_THRESHOLD 3 +#define CHANGEGC_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class ChangeGCMessage : public Message +{ + friend class ChangeGCStore; + + public: + + ChangeGCMessage() + { + } + + ~ChangeGCMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int gcontext; + unsigned int value_mask; +}; + +class ChangeGCStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + ChangeGCStore() : MessageStore() + { + enableCache = CHANGEGC_ENABLE_CACHE; + enableData = CHANGEGC_ENABLE_DATA; + enableSplit = CHANGEGC_ENABLE_SPLIT; + enableCompress = CHANGEGC_ENABLE_COMPRESS; + + dataLimit = CHANGEGC_DATA_LIMIT; + dataOffset = CHANGEGC_DATA_OFFSET; + + cacheSlots = CHANGEGC_CACHE_SLOTS; + cacheThreshold = CHANGEGC_CACHE_THRESHOLD; + cacheLowerThreshold = CHANGEGC_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~ChangeGCStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "ChangeGC"; + } + + virtual unsigned char opcode() const + { + return X_ChangeGC; + } + + virtual unsigned int storage() const + { + return sizeof(ChangeGCMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new ChangeGCMessage(); + } + + virtual Message *create(const Message &message) const + { + return new ChangeGCMessage((const ChangeGCMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (ChangeGCMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; +}; + +#endif /* ChangeGC_H */ diff --git a/nxcomp/ChangeGCCompat.cpp b/nxcomp/ChangeGCCompat.cpp new file mode 100644 index 000000000..ca2973774 --- /dev/null +++ b/nxcomp/ChangeGCCompat.cpp @@ -0,0 +1,131 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ChangeGCCompat.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int ChangeGCCompatStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ChangeGCCompatMessage *changeGC = (ChangeGCCompatMessage *) message; + + // + // Here is the fingerprint. + // + + changeGC -> gcontext = GetULONG(buffer + 4, bigEndian); + changeGC -> value_mask = GetULONG(buffer + 8, bigEndian); + + // + // Clear the unused bytes carried in the + // payload to increase the effectiveness + // of the caching algorithm. + // + + if ((int) size > dataOffset) + { + #ifdef DEBUG + *logofs << name() << ": Removing unused bytes from the " + << "data payload.\n" << logofs_flush; + #endif + + changeGC -> value_mask &= (1 << 23) - 1; + + unsigned int mask = 0x1; + unsigned char *source = (unsigned char *) buffer + CHANGEGC_DATA_OFFSET; + unsigned long value = 0; + + for (unsigned int i = 0; i < 23; i++) + { + if (changeGC -> value_mask & mask) + { + value = GetULONG(source, bigEndian); + + value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i])); + + PutULONG(value, source, bigEndian); + + source += 4; + } + + mask <<= 1; + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ChangeGCCompatStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ChangeGCCompatMessage *changeGC = (ChangeGCCompatMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(changeGC -> gcontext, buffer + 4, bigEndian); + PutULONG(changeGC -> value_mask, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void ChangeGCCompatStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + ChangeGCCompatMessage *changeGC = (ChangeGCCompatMessage *) message; + + *logofs << name() << ": Identity gcontext " << changeGC -> gcontext + << ", mask " << changeGC -> value_mask << ", size " + << changeGC -> size_ << ".\n" << logofs_flush; + #endif +} + +void ChangeGCCompatStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 4, 8); +} diff --git a/nxcomp/ChangeGCCompat.h b/nxcomp/ChangeGCCompat.h new file mode 100644 index 000000000..3a7b0c0a9 --- /dev/null +++ b/nxcomp/ChangeGCCompat.h @@ -0,0 +1,170 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ChangeGCCompat_H +#define ChangeGCCompat_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define CHANGEGC_ENABLE_CACHE 1 +#define CHANGEGC_ENABLE_DATA 0 +#define CHANGEGC_ENABLE_SPLIT 0 +#define CHANGEGC_ENABLE_COMPRESS 0 + +#define CHANGEGC_DATA_LIMIT 144 +#define CHANGEGC_DATA_OFFSET 12 + +#define CHANGEGC_CACHE_SLOTS 3000 +#define CHANGEGC_CACHE_THRESHOLD 3 +#define CHANGEGC_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class ChangeGCCompatMessage : public Message +{ + friend class ChangeGCCompatStore; + + public: + + ChangeGCCompatMessage() + { + } + + ~ChangeGCCompatMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int gcontext; + unsigned int value_mask; +}; + +class ChangeGCCompatStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + ChangeGCCompatStore() : MessageStore() + { + enableCache = CHANGEGC_ENABLE_CACHE; + enableData = CHANGEGC_ENABLE_DATA; + enableSplit = CHANGEGC_ENABLE_SPLIT; + enableCompress = CHANGEGC_ENABLE_COMPRESS; + + dataLimit = CHANGEGC_DATA_LIMIT; + dataOffset = CHANGEGC_DATA_OFFSET; + + cacheSlots = CHANGEGC_CACHE_SLOTS; + cacheThreshold = CHANGEGC_CACHE_THRESHOLD; + cacheLowerThreshold = CHANGEGC_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~ChangeGCCompatStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "ChangeGCCompat"; + } + + virtual unsigned char opcode() const + { + return X_ChangeGC; + } + + virtual unsigned int storage() const + { + return sizeof(ChangeGCCompatMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new ChangeGCCompatMessage(); + } + + virtual Message *create(const Message &message) const + { + return new ChangeGCCompatMessage((const ChangeGCCompatMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (ChangeGCCompatMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* ChangeGCCompat_H */ diff --git a/nxcomp/ChangeProperty.cpp b/nxcomp/ChangeProperty.cpp new file mode 100644 index 000000000..0270d7274 --- /dev/null +++ b/nxcomp/ChangeProperty.cpp @@ -0,0 +1,179 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ChangeProperty.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int ChangePropertyStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message; + + changeProperty -> mode = *(buffer + 1); + changeProperty -> format = *(buffer + 16); + + changeProperty -> window = GetULONG(buffer + 4, bigEndian); + changeProperty -> property = GetULONG(buffer + 8, bigEndian); + changeProperty -> type = GetULONG(buffer + 12, bigEndian); + changeProperty -> length = GetULONG(buffer + 20, bigEndian); + + // + // Cleanup the padding bytes. + // + + unsigned int uiFormat; + unsigned int uiLengthInBytes; + + if ((int) size > CHANGEPROPERTY_DATA_OFFSET) + { + uiFormat = *(buffer + 16); + + uiLengthInBytes = changeProperty -> length; + + #ifdef DEBUG + *logofs << name() << ": length " << uiLengthInBytes + << ", format " << uiFormat << ", size " + << size << ".\n" << logofs_flush; + #endif + + if (uiFormat == 16) + { + uiLengthInBytes <<= 1; + } + else if (uiFormat == 32) + { + uiLengthInBytes <<= 2; + } + + unsigned char *end = ((unsigned char *) buffer) + size; + unsigned char *pad = ((unsigned char *) buffer) + CHANGEPROPERTY_DATA_OFFSET + uiLengthInBytes; + + CleanData((unsigned char *) pad, end - pad); + } + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ChangePropertyStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message; + + *(buffer + 1) = changeProperty -> mode; + *(buffer + 16) = changeProperty -> format; + + PutULONG(changeProperty -> window, buffer + 4, bigEndian); + PutULONG(changeProperty -> property, buffer + 8, bigEndian); + PutULONG(changeProperty -> type, buffer + 12, bigEndian); + PutULONG(changeProperty -> length, buffer + 20, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void ChangePropertyStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message; + + *logofs << name() << ": Identity mode " << (unsigned int) changeProperty -> mode << ", format " + << (unsigned int) changeProperty -> format << ", window " << changeProperty -> window + << ", property " << changeProperty -> property << ", type " << changeProperty -> type + << ", length " << changeProperty -> length << ", size " << changeProperty -> size_ + << ".\n" << logofs_flush; + + #endif +} + +void ChangePropertyStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 1, 1); + md5_append(md5_state_, buffer + 16, 1); + + md5_append(md5_state_, buffer + 8, 4); + md5_append(md5_state_, buffer + 12, 4); + md5_append(md5_state_, buffer + 20, 4); +} + +void ChangePropertyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message; + ChangePropertyMessage *cachedChangeProperty = (ChangePropertyMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << changeProperty -> window + << " as window field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(changeProperty -> window, clientCache -> windowCache); + + cachedChangeProperty -> window = changeProperty -> window; +} + +void ChangePropertyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> windowCache); + + changeProperty -> window = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << changeProperty -> window + << " as window field.\n" << logofs_flush; + #endif +} + + diff --git a/nxcomp/ChangeProperty.h b/nxcomp/ChangeProperty.h new file mode 100644 index 000000000..f941fced9 --- /dev/null +++ b/nxcomp/ChangeProperty.h @@ -0,0 +1,181 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ChangeProperty_H +#define ChangeProperty_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define CHANGEPROPERTY_ENABLE_CACHE 1 +#define CHANGEPROPERTY_ENABLE_DATA 0 +#define CHANGEPROPERTY_ENABLE_SPLIT 0 +#define CHANGEPROPERTY_ENABLE_COMPRESS 0 + +#define CHANGEPROPERTY_DATA_LIMIT 28688 +#define CHANGEPROPERTY_DATA_OFFSET 24 + +#define CHANGEPROPERTY_CACHE_SLOTS 2000 +#define CHANGEPROPERTY_CACHE_THRESHOLD 2 +#define CHANGEPROPERTY_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class ChangePropertyMessage : public Message +{ + friend class ChangePropertyStore; + + public: + + ChangePropertyMessage() + { + } + + ~ChangePropertyMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char mode; + unsigned char format; + unsigned int window; + unsigned int property; + unsigned int type; + unsigned int length; +}; + +class ChangePropertyStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + ChangePropertyStore() : MessageStore() + { + enableCache = CHANGEPROPERTY_ENABLE_CACHE; + enableData = CHANGEPROPERTY_ENABLE_DATA; + enableSplit = CHANGEPROPERTY_ENABLE_SPLIT; + enableCompress = CHANGEPROPERTY_ENABLE_COMPRESS; + + dataLimit = CHANGEPROPERTY_DATA_LIMIT; + dataOffset = CHANGEPROPERTY_DATA_OFFSET; + + cacheSlots = CHANGEPROPERTY_CACHE_SLOTS; + cacheThreshold = CHANGEPROPERTY_CACHE_THRESHOLD; + cacheLowerThreshold = CHANGEPROPERTY_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~ChangePropertyStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "ChangeProperty"; + } + + virtual unsigned char opcode() const + { + return X_ChangeProperty; + } + + virtual unsigned int storage() const + { + return sizeof(ChangePropertyMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new ChangePropertyMessage(); + } + + virtual Message *create(const Message &message) const + { + return new ChangePropertyMessage((const ChangePropertyMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (ChangePropertyMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* ChangeProperty_H */ diff --git a/nxcomp/Channel.cpp b/nxcomp/Channel.cpp new file mode 100644 index 000000000..71b556b0d --- /dev/null +++ b/nxcomp/Channel.cpp @@ -0,0 +1,2125 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Channel.h" + +#include "List.h" +#include "Proxy.h" +#include "Statistics.h" + +#include "StaticCompressor.h" + +#include "NXalert.h" + +extern Proxy *proxy; + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Log the operations related to splits. +// + +#undef SPLIT + +#undef COUNT + +#define COUNT_MAJOR_OPCODE 154 + +#undef MONITOR + +#define MONITOR_MAJOR_OPCODE 154 +#define MONITOR_MINOR_OPCODE 23 + +#undef CLEAR + +#define CLEAR_MAJOR_OPCODE 154 +#define CLEAR_MINOR_OPCODE 23 + +// +// Define this to know how many messages +// are allocated and deallocated. +// + +#undef REFERENCES + +// +// Set to the descriptor of the first X +// channel successfully connected. +// + +int Channel::firstClient_ = -1; + +// +// Port used for font server connections. +// + +int Channel::fontPort_ = -1; + +// +// This is used for reference count. +// + +#ifdef REFERENCES + +int Channel::references_ = 0; + +#endif + +Channel::Channel(Transport *transport, StaticCompressor *compressor) + + : transport_(transport), compressor_(compressor) +{ + fd_ = transport_ -> fd(); + + finish_ = 0; + closing_ = 0; + drop_ = 0; + congestion_ = 0; + priority_ = 0; + + alert_ = 0; + + firstRequest_ = 1; + firstReply_ = 1; + + enableCache_ = 1; + enableSplit_ = 1; + enableSave_ = 1; + enableLoad_ = 1; + + // + // Must be set by proxy. + // + + opcodeStore_ = NULL; + + clientStore_ = NULL; + serverStore_ = NULL; + + clientCache_ = NULL; + serverCache_ = NULL; + + #ifdef REFERENCES + *logofs << "Channel: Created new Channel at " + << this << " out of " << ++references_ + << " allocated references.\n" << logofs_flush; + #endif +} + +Channel::~Channel() +{ + if (firstClient_ == fd_) + { + firstClient_ = -1; + } + + #ifdef REFERENCES + *logofs << "Channel: Deleted Channel at " + << this << " out of " << --references_ + << " allocated references.\n" << logofs_flush; + #endif +} + +int Channel::handleEncode(EncodeBuffer &encodeBuffer, ChannelCache *channelCache, + MessageStore *store, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + #ifdef MONITOR + + static float totalMessages = 0; + static float totalBits = 0; + + int bits; + int diff; + + bits = encodeBuffer.getBits(); + + #endif + + // + // Check if message can be differentially + // encoded using a similar message in the + // message store. + // + + #ifdef COUNT + + if (*(buffer) == COUNT_MAJOR_OPCODE) + { + if (*(buffer) < 128) + { + *logofs << "handleEncode: Handling OPCODE#" << (unsigned int) *(buffer) + << ".\n" << logofs_flush; + } + else + { + *logofs << "handleEncode: Handling OPCODE#" << (unsigned int) *(buffer) + << " MINOR#" << (unsigned int) *(buffer + 1) << ".\n" + << logofs_flush; + } + } + + #endif + + #ifdef CLEAR + + if (*(buffer) == CLEAR_MAJOR_OPCODE && + (CLEAR_MINOR_OPCODE == -1 || *(buffer + 1) == CLEAR_MINOR_OPCODE)) + { + *((unsigned char *) buffer) = X_NoOperation; + + *((unsigned char *) buffer + 1) = '\0'; + + CleanData((unsigned char *) buffer + 4, size - 4); + } + + #endif + + if (handleEncodeCached(encodeBuffer, channelCache, + store, buffer, size) == 1) + { + #ifdef MONITOR + + diff = encodeBuffer.getBits() - bits; + + if (*(buffer) == MONITOR_MAJOR_OPCODE && + (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE)) + { + totalMessages++; + + totalBits += diff; + + *logofs << "handleEncode: Handled cached OPCODE#" << (unsigned int) *(buffer) + << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size + << " bytes in, " << diff << " bits (" << ((float) diff) / 8 + << " bytes) out. Average " << totalBits / totalMessages + << "/1.\n" << logofs_flush; + } + + #endif + + // + // Let the channel update the split store + // and notify the agent in the case of a + // cache hit. + // + + if (store -> enableSplit) + { + handleSplit(encodeBuffer, store, store -> lastAction, + store -> lastHit, opcode, buffer, size); + } + + return 1; + } + + // + // A similar message could not be found in + // cache or message must be discarded. Must + // transmit the message using the field by + // field differential encoding. + // + + handleEncodeIdentity(encodeBuffer, channelCache, + store, buffer, size, bigEndian_); + + // + // Check if message has a distinct data part. + // + + if (store -> enableData) + { + // + // If message split was requested by agent then send data + // out-of-band, dividing it in small chunks. Until message + // is completely transferred, keep in the split store a + // dummy version of the message, with data replaced with a + // pattern. + // + // While data is being transferred, agent should have put + // the resource (for example its client) asleep. It can + // happen, though, that a different client would reference + // the same message. We cannot issue a cache hit for images + // being split (such images are put in store in 'incomplete' + // state), so we need to handle this case. + // + + if (store -> enableSplit == 1) + { + // + // Let the channel decide what to do with the + // message. If the split can't take place be- + // cause the split store is full, the channel + // will tell the remote side that the data is + // going to follow. + // + + if (handleSplit(encodeBuffer, store, store -> lastAction, + (store -> lastAction == IS_ADDED ? store -> lastAdded : 0), + opcode, buffer, size) == 1) + { + #ifdef MONITOR + + diff = encodeBuffer.getBits() - bits; + + if (*(buffer) == MONITOR_MAJOR_OPCODE && + (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE)) + { + totalMessages++; + + totalBits += diff; + + *logofs << "handleEncode: Handled split OPCODE#" << (unsigned int) *(buffer) + << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size + << " bytes in, " << diff << " bits (" << ((float) diff) / 8 + << " bytes) out. Average " << totalBits / totalMessages + << "/1.\n" << logofs_flush; + } + + #endif + + return 0; + } + } + + // + // The split did not take place and we are going + // to transfer the data part. Check if the static + // compression of the data section is enabled. + // This is the case of all messages not having a + // special differential encoding or messages that + // we want to store in cache in compressed form. + // + + unsigned int offset = store -> identitySize(buffer, size); + + if (store -> enableCompress) + { + unsigned char *data = NULL; + unsigned int dataSize = 0; + + int compressed = handleCompress(encodeBuffer, opcode, offset, + buffer, size, data, dataSize); + if (compressed < 0) + { + return -1; + } + else if (compressed > 0) + { + // + // Update the size of the message according + // to the result of the data compression. + // + + handleUpdate(store, size - offset, dataSize); + } + } + else + { + handleCopy(encodeBuffer, opcode, offset, buffer, size); + } + } + + #ifdef MONITOR + + diff = encodeBuffer.getBits() - bits; + + if (*(buffer) == MONITOR_MAJOR_OPCODE && + (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE)) + { + totalMessages++; + + totalBits += diff; + + *logofs << "handleEncode: Handled OPCODE#" << (unsigned int) *(buffer) + << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size + << " bytes in, " << diff << " bits (" << ((float) diff) / 8 + << " bytes) out. Average " << totalBits / totalMessages + << "/1.\n" << logofs_flush; + } + + #endif + + return 0; +} + +int Channel::handleDecode(DecodeBuffer &decodeBuffer, ChannelCache *channelCache, + MessageStore *store, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Check first if the message is in the + // message store. + // + + unsigned int split = 0; + + if (handleDecodeCached(decodeBuffer, channelCache, + store, buffer, size) == 1) + { + // + // Let the channel update the split store + // in the case of a message being cached. + // + + if (store -> enableSplit == 1) + { + if (control -> isProtoStep7() == 1) + { + #ifdef DEBUG + *logofs << "handleDecode: " << store -> name() + << ": Checking if the message was split.\n" + << logofs_flush; + #endif + + decodeBuffer.decodeBoolValue(split); + } + + if (split == 1) + { + handleSplit(decodeBuffer, store, store -> lastAction, + store -> lastHit, opcode, buffer, size); + + handleCleanAndNullRequest(opcode, buffer, size); + } + } + + return 1; + } + + // + // Decode the full identity. + // + + handleDecodeIdentity(decodeBuffer, channelCache, store, buffer, + size, bigEndian_, &writeBuffer_); + + // + // Check if the message has a distinct + // data part. + // + + if (store -> enableData) + { + // + // Check if message has been split. + // + + if (store -> enableSplit) + { + #ifdef DEBUG + *logofs << "handleDecode: " << store -> name() + << ": Checking if the message was split.\n" + << logofs_flush; + #endif + + decodeBuffer.decodeBoolValue(split); + + if (split == 1) + { + // + // If the message was added to the store, + // create the entry without the data part. + // + + handleSaveSplit(store, buffer, size); + + handleSplit(decodeBuffer, store, store -> lastAction, + (store -> lastAction == IS_ADDED ? store -> lastAdded : 0), + opcode, buffer, size); + + handleCleanAndNullRequest(opcode, buffer, size); + + return 0; + } + } + + // + // Decode the data part. + // + + unsigned int offset = store -> identitySize(buffer, size); + + if (store -> enableCompress) + { + const unsigned char *data = NULL; + unsigned int dataSize = 0; + + int decompressed = handleDecompress(decodeBuffer, opcode, offset, + buffer, size, data, dataSize); + if (decompressed < 0) + { + return -1; + } + else if (decompressed > 0) + { + // + // The message has been transferred + // in compressed format. + // + + handleSave(store, buffer, size, data, dataSize); + + if (store -> enableSplit) + { + if (split == 1) + { + handleSplit(decodeBuffer, store, store -> lastAction, + (store -> lastAction == IS_ADDED ? store -> lastAdded : 0), + opcode, buffer, size); + + handleCleanAndNullRequest(opcode, buffer, size); + } + } + + return 0; + } + } + else + { + // + // Static compression of the data part + // was not enabled for this message. + // + + handleCopy(decodeBuffer, opcode, offset, buffer, size); + } + } + + // + // The message doesn't have a data part + // or the data was not compressed. + // + + handleSave(store, buffer, size); + + if (store -> enableSplit) + { + if (split == 1) + { + handleSplit(decodeBuffer, store, store -> lastAction, + (store -> lastAction == IS_ADDED ? store -> lastAdded : 0), + opcode, buffer, size); + + handleCleanAndNullRequest(opcode, buffer, size); + } + } + + return 0; +} + +int Channel::handleEncodeCached(EncodeBuffer &encodeBuffer, ChannelCache *channelCache, + MessageStore *store, const unsigned char *buffer, + const unsigned int size) +{ + if (control -> LocalDeltaCompression == 0 || + enableCache_ == 0 || store -> enableCache == 0) + { + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeActionValue(is_discarded, + store -> lastActionCache); + } + else + { + encodeBuffer.encodeActionValueCompat(is_discarded, + store -> lastActionCacheCompat); + } + + store -> lastAction = is_discarded; + + return 0; + } + + #ifdef DEBUG + *logofs << "handleEncodeCached: " << store -> name() + << ": Going to handle a new message of this class.\n" + << logofs_flush; + #endif + + // + // Check if the estimated size of cache is greater + // than the requested limit. If it is the case make + // some room by deleting one or more messages. + // + + int position; + + while (mustCleanStore(store) == 1 && canCleanStore(store) == 1) + { + #ifdef DEBUG + *logofs << "handleEncodeCached: " << store -> name() + << ": Trying to reduce size of message store.\n" + << logofs_flush; + #endif + + position = store -> clean(use_checksum); + + if (position == nothing) + { + #ifdef TEST + *logofs << "handleEncodeCached: " << store -> name() + << ": WARNING! No message found to be " + << "actually removed.\n" << logofs_flush; + #endif + + break; + } + + #ifdef DEBUG + *logofs << "handleEncodeCached: " << store -> name() + << ": Message at position " << position + << " will be removed.\n" << logofs_flush; + #endif + + // + // Encode the position of message to + // be discarded. + // + + store -> lastRemoved = position; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeActionValue(is_removed, store -> lastRemoved, + store -> lastActionCache); + } + else + { + encodeBuffer.encodeActionValueCompat(is_removed, + store -> lastActionCacheCompat); + + encodeBuffer.encodePositionValueCompat(store -> lastRemoved, + store -> lastRemovedCacheCompat); + } + + #ifdef DEBUG + *logofs << "handleEncodeCached: " << store -> name() << ": Going to " + << "clean up message at position " << position << ".\n" + << logofs_flush; + #endif + + store -> remove(position, use_checksum, discard_data); + + #ifdef DEBUG + *logofs << "handleEncodeCached: " << store -> name() << ": There are " + << store -> getSize() << " messages in the store out of " + << store -> cacheSlots << " slots.\n" << logofs_flush; + + *logofs << "handleEncodeCached: " << store -> name() + << ": Size of store is " << store -> getLocalStorageSize() + << " bytes locally and " << store -> getRemoteStorageSize() + << " bytes remotely.\n" << logofs_flush; + + *logofs << "handleEncodeCached: " << store -> name() + << ": Size of total cache is " << store -> getLocalTotalStorageSize() + << " bytes locally and " << store -> getRemoteTotalStorageSize() + << " bytes remotely.\n" << logofs_flush; + #endif + } + + #ifdef DEBUG + + if (mustCleanStore(store) == 1 && canCleanStore(store) == 0) + { + *logofs << "handleEncodeCached: " << store -> name() + << ": Store would need a clean but operation will be delayed.\n" + << logofs_flush; + + *logofs << "handleEncodeCached: " << store -> name() << ": There are " + << store -> getSize() << " messages in the store out of " + << store -> cacheSlots << " slots.\n" << logofs_flush; + + *logofs << "handleEncodeCached: " << store -> name() + << ": Size of store is " << store -> getLocalStorageSize() + << " bytes locally and " << store -> getRemoteStorageSize() + << " bytes remotely.\n" << logofs_flush; + + *logofs << "handleEncodeCached: " << store -> name() + << ": Size of total cache is " << store -> getLocalTotalStorageSize() + << " bytes locally and " << store -> getRemoteTotalStorageSize() + << " bytes remotely.\n" << logofs_flush; + } + + #endif + + // + // If 'on the wire' size of message exceeds the + // allowed limit then avoid to store it in the + // cache. + // + + if (store -> validateMessage(buffer, size) == 0) + { + #ifdef TEST + *logofs << "handleEncodeCached: " << store -> name() + << ": Message with size " << size << " ignored.\n" + << logofs_flush; + #endif + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeActionValue(is_discarded, + store -> lastActionCache); + } + else + { + encodeBuffer.encodeActionValueCompat(is_discarded, + store -> lastActionCacheCompat); + } + + store -> lastAction = is_discarded; + + return 0; + } + + // + // Fill the message object with the + // received data. + // + + Message *message = store -> getTemporary(); + + if (message == NULL) + { + #ifdef PANIC + *logofs << "handleEncodeCached: " << store -> name() + << ": PANIC! Can't allocate memory for " + << "a new message.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "a new message in context [D].\n"; + + HandleCleanup(); + } + + // + // As we are at encoding side, it is enough to store the + // checksum for the object while data can be erased. Both + // the identity and the data will never be sent through + // the wire again as long as they are stored in the cache + // at the decoding side. The split parameter is always + // set to 0 as the data will not be stored in any case. + // + + store -> parse(message, 0, buffer, size, use_checksum, + discard_data, bigEndian_); + + #ifdef DUMP + + store -> dump(message); + + #endif + + // + // Search the object in the message + // store. If found get the position. + // + + #ifdef DEBUG + *logofs << "handleEncodeCached: " << store -> name() + << ": Searching object of size " << size + << " in the cache.\n" << logofs_flush; + #endif + + int added; + int locked; + + position = store -> findOrAdd(message, use_checksum, + discard_data, added, locked); + + if (position == nothing) + { + #ifdef WARNING + *logofs << "handleEncodeCached: " << store -> name() + << ": WARNING! Can't store object in the cache.\n" + << logofs_flush; + #endif + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeActionValue(is_discarded, + store -> lastActionCache); + } + else + { + encodeBuffer.encodeActionValueCompat(is_discarded, + store -> lastActionCacheCompat); + } + + store -> lastAction = is_discarded; + + return 0; + } + else if (locked == 1) + { + // + // We can't issue a cache hit. Encoding identity + // differences while message it's being split + // would later result in agent to commit a wrong + // version of message. + // + + #ifdef WARNING + *logofs << "handleEncodeCached: " << store -> name() + << ": WARNING! Message of size " << store -> plainSize(position) + << " at position " << position << " is locked.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Message of size " << store -> plainSize(position) + << " at position " << position << " is locked.\n"; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeActionValue(is_discarded, + store -> lastActionCache); + } + else + { + encodeBuffer.encodeActionValueCompat(is_discarded, + store -> lastActionCacheCompat); + } + + store -> lastAction = is_discarded; + + return 0; + } + else if (added == 1) + { + store -> resetTemporary(); + + #ifdef DEBUG + *logofs << "handleEncodeCached: " << store -> name() << ": Message of size " + << store -> plainSize(position) << " has been stored at position " + << position << ".\n" << logofs_flush; + + *logofs << "handleEncodeCached: " << store -> name() << ": There are " + << store -> getSize() << " messages in the store out of " + << store -> cacheSlots << " slots.\n" << logofs_flush; + + *logofs << "handleEncodeCached: " << store -> name() + << ": Size of store is " << store -> getLocalStorageSize() + << " bytes locally and " << store -> getRemoteStorageSize() + << " bytes remotely.\n" << logofs_flush; + + *logofs << "handleEncodeCached: " << store -> name() + << ": Size of total cache is " << store -> getLocalTotalStorageSize() + << " bytes locally and " << store -> getRemoteTotalStorageSize() + << " bytes remotely.\n" << logofs_flush; + #endif + + // + // Inform the decoding side that message + // must be inserted in cache and encode + // the position where the insertion took + // place. + // + + store -> lastAction = IS_ADDED; + + store -> lastAdded = position; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeActionValue(IS_ADDED, store -> lastAdded, + store -> lastActionCache); + + } + else + { + encodeBuffer.encodeActionValueCompat(IS_ADDED, + store -> lastActionCacheCompat); + + encodeBuffer.encodePositionValueCompat(store -> lastAdded, + store -> lastAddedCacheCompat); + } + + return 0; + } + else + { + #ifdef DEBUG + *logofs << "handleEncodeCached: " << store -> name() + << ": Cache hit. Found object at position " + << position << ".\n" << logofs_flush; + #endif + + // + // Must abort the connection if the + // the position is invalid. + // + + Message *cachedMessage = store -> get(position); + + // + // Increase the rating of the cached + // message. + // + + store -> touch(cachedMessage); + + #ifdef DEBUG + *logofs << "handleEncodeCached: " << store -> name() << ": Hits for " + << "object at position " << position << " are now " + << store -> getTouches(position) << ".\n" + << logofs_flush; + #endif + + // + // Send to the decoding side position + // where object can be found in cache. + // + + store -> lastAction = IS_HIT; + + store -> lastHit = position; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeActionValue(IS_HIT, store -> lastHit, + store -> lastActionCache); + } + else + { + encodeBuffer.encodeActionValueCompat(IS_HIT, + store -> lastActionCacheCompat); + + encodeBuffer.encodePositionValueCompat(store -> lastHit, + store -> lastHitCacheCompat); + } + + // + // Send the field by field differences in + // respect to the original message stored + // in cache. + // + + store -> updateIdentity(encodeBuffer, message, cachedMessage, channelCache); + + return 1; + } +} + +void Channel::handleUpdateAdded(MessageStore *store, unsigned int dataSize, + unsigned int compressedDataSize) +{ + #ifdef TEST + + if (store -> lastAction != IS_ADDED) + { + #ifdef PANIC + *logofs << "handleUpdateAdded: " << store -> name() + << ": PANIC! Function called for action '" + << store -> lastAction << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Update function called for " + << "store '" << store -> name() << "' with " + << "action '" << store -> lastAction + << "'.\n"; + + HandleCleanup(); + } + + #endif + + #ifdef DEBUG + *logofs << "handleUpdateAdded: " << store -> name() << ": Updating " + << "object at position " << store -> lastAdded << " of size " + << store -> plainSize(store -> lastAdded) << " (" << dataSize + << "/" << compressedDataSize << ").\n" << logofs_flush; + #endif + + store -> updateData(store -> lastAdded, dataSize, compressedDataSize); + + #ifdef DEBUG + *logofs << "handleUpdateAdded: " << store -> name() << ": There are " + << store -> getSize() << " messages in the store out of " + << store -> cacheSlots << " slots.\n" << logofs_flush; + + *logofs << "handleUpdateAdded: " << store -> name() + << ": Size of store is " << store -> getLocalStorageSize() + << " bytes locally and " << store -> getRemoteStorageSize() + << " bytes remotely.\n" << logofs_flush; + + *logofs << "handleUpdateAdded: " << store -> name() + << ": Size of total cache is " << store -> getLocalTotalStorageSize() + << " bytes locally and " << store -> getRemoteTotalStorageSize() + << " bytes remotely.\n" << logofs_flush; + #endif +} + +int Channel::handleDecodeCached(DecodeBuffer &decodeBuffer, ChannelCache *channelCache, + MessageStore *store, unsigned char *&buffer, + unsigned int &size) +{ + // + // Create a new message object and + // fill it with received data. + // + + #ifdef DEBUG + *logofs << "handleDecodeCached: " << store -> name() + << ": Going to handle a new message of this class.\n" + << logofs_flush; + #endif + + // + // Decode bits telling how to handle + // this message. + // + + unsigned char action; + unsigned short int position; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeActionValue(action, position, + store -> lastActionCache); + } + else + { + decodeBuffer.decodeActionValueCompat(action, + store -> lastActionCacheCompat); + } + + // + // Clean operations must always come + // before any operation on message. + // + + while (action == is_removed) + { + if (control -> isProtoStep7() == 1) + { + store -> lastRemoved = position; + } + else + { + decodeBuffer.decodePositionValueCompat(store -> lastRemoved, + store -> lastRemovedCacheCompat); + } + + #ifdef DEBUG + + if (store -> get(store -> lastRemoved)) + { + *logofs << "handleDecodeCached: " << store -> name() << ": Cleaning up " + << "object at position " << store -> lastRemoved + << " of size " << store -> plainSize(store -> lastRemoved) + << " (" << store -> plainSize(store -> lastRemoved) << "/" + << store -> compressedSize(store -> lastRemoved) << ").\n" + << logofs_flush; + } + + #endif + + // + // If the message can't be found we + // will abort the connection. + // + + store -> remove(store -> lastRemoved, discard_checksum, use_data); + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeActionValue(action, position, + store -> lastActionCache); + } + else + { + decodeBuffer.decodeActionValueCompat(action, + store -> lastActionCacheCompat); + } + } + + // + // If it's a cache hit, the position + // where object can be found follows. + // + + if ((T_store_action) action == IS_HIT) + { + if (control -> isProtoStep7() == 1) + { + store -> lastHit = position; + } + else + { + decodeBuffer.decodePositionValueCompat(store -> lastHit, + store -> lastHitCacheCompat); + } + + // + // Get data from the cache at given position. + // + + #ifdef DEBUG + + if (store -> get(store -> lastHit)) + { + *logofs << "handleDecodeCached: " << store -> name() << ": Retrieving " + << "object at position " << store -> lastHit + << " of size " << store -> plainSize(store -> lastHit) + << " (" << store -> plainSize(store -> lastHit) << "/" + << store -> compressedSize(store -> lastHit) << ").\n" + << logofs_flush; + } + + #endif + + // + // Must abort the connection if the + // the position is invalid. + // + + Message *message = store -> get(store -> lastHit); + + // + // Make room for the outgoing message. + // + + size = store -> plainSize(store -> lastHit); + + buffer = writeBuffer_.addMessage(size); + + #ifdef DEBUG + *logofs << "handleDecodeCached: " << store -> name() + << ": Prepared an outgoing buffer of " + << size << " bytes.\n" << logofs_flush; + #endif + + // + // Decode the variant part. Pass client + // or server cache to the message store. + // + + store -> updateIdentity(decodeBuffer, message, channelCache); + + // + // Write each field in the outgoing buffer. + // + + store -> unparse(message, buffer, size, bigEndian_); + + #ifdef DUMP + + store -> dump(message); + + #endif + + store -> lastAction = IS_HIT; + + return 1; + } + else if ((T_store_action) action == IS_ADDED) + { + if (control -> isProtoStep7() == 1) + { + store -> lastAdded = position; + } + else + { + decodeBuffer.decodePositionValueCompat(store -> lastAdded, + store -> lastAddedCacheCompat); + } + + #ifdef DEBUG + *logofs << "handleDecodeCached: " << store -> name() + << ": Message will be later stored at position " + << store -> lastAdded << ".\n" << logofs_flush; + #endif + + store -> lastAction = IS_ADDED; + + return 0; + } + else + { + #ifdef DEBUG + *logofs << "handleDecodeCached: " << store -> name() + << ": Message will be later discarded.\n" + << logofs_flush; + #endif + + store -> lastAction = is_discarded; + + return 0; + } +} + +void Channel::handleSaveAdded(MessageStore *store, int split, unsigned char *buffer, + unsigned int size, const unsigned char *compressedData, + const unsigned int compressedDataSize) +{ + #ifdef TEST + + if (store -> lastAction != IS_ADDED) + { + #ifdef PANIC + *logofs << "handleSaveAdded: " << store -> name() + << ": PANIC! Function called for action '" + << store -> lastAction << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Save function called for " + << "store '" << store -> name() << "' with " + << "action '" << store -> lastAction + << "'.\n"; + + HandleCleanup(); + } + + #endif + + Message *message = store -> getTemporary(); + + if (message == NULL) + { + #ifdef PANIC + *logofs << "handleSaveAdded: " << store -> name() + << ": PANIC! Can't access temporary storage " + << "for message at position " << store -> lastAdded + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't access temporary storage " + << "for message at position " << store -> lastAdded + << ".\n"; + + HandleCleanup(); + } + + if (compressedData == NULL) + { + // + // If the data part has been split + // avoid to copy it into the message. + // + + store -> parse(message, split, buffer, size, discard_checksum, + use_data, bigEndian_); + } + else + { + store -> parse(message, buffer, size, compressedData, + compressedDataSize, discard_checksum, + use_data, bigEndian_); + } + + if (store -> add(message, store -> lastAdded, + discard_checksum, use_data) == nothing) + { + #ifdef PANIC + *logofs << "handleSaveAdded: " << store -> name() + << ": PANIC! Can't store message in the cache " + << "at position " << store -> lastAdded << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't store message of type " + << store -> name() << "in the cache at position " + << store -> lastAdded << ".\n"; + + HandleCleanup(); + } + else + { + store -> resetTemporary(); + + #ifdef DEBUG + *logofs << "handleSaveAdded: " << store -> name() << ": Stored " + << (compressedData == NULL ? "plain" : "compressed") + << " object at position " << store -> lastAdded + << " of size " << store -> plainSize(store -> lastAdded) + << " (" << store -> plainSize(store -> lastAdded) << "/" + << store -> compressedSize(store -> lastAdded) << ").\n" + << logofs_flush; + #endif + } + + #ifdef DEBUG + *logofs << "handleSaveAdded: " << store -> name() + << ": Size of store is " << store -> getLocalStorageSize() + << " bytes locally and " << store -> getRemoteStorageSize() + << " bytes remotely.\n" << logofs_flush; + + *logofs << "handleSaveAdded: " << store -> name() + << ": Size of total cache is " << store -> getLocalTotalStorageSize() + << " bytes locally and " << store -> getRemoteTotalStorageSize() + << " bytes remotely.\n" << logofs_flush; + #endif +} + +int Channel::handleWait(int timeout) +{ + #ifdef TEST + *logofs << "handleWait: Going to wait for more data " + << "on FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + T_timestamp startTs = getNewTimestamp(); + + T_timestamp nowTs = startTs; + + int readable; + int remaining; + + for (;;) + { + remaining = timeout - diffTimestamp(startTs, nowTs); + + if (transport_ -> blocked() == 1) + { + #ifdef WARNING + *logofs << "handleWait: WARNING! Having to drain with " + << "channel " << "for FD#" << fd_ << " blocked.\n" + << logofs_flush; + #endif + + handleDrain(0, remaining); + + continue; + } + + if (remaining <= 0) + { + #ifdef TEST + *logofs << "handleWait: Timeout raised while waiting " + << "for more data for FD#" << fd_ << " at " + << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + return 0; + } + + #ifdef TEST + *logofs << "handleWait: Waiting " << remaining << " Ms " + << "for a new message on FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + readable = transport_ -> wait(remaining); + + if (readable > 0) + { + #ifdef TEST + *logofs << "handleWait: WARNING! Encoding more data " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + if (proxy -> handleAsyncRead(fd_) < 0) + { + return -1; + } + + return 1; + } + else if (readable == -1) + { + return -1; + } + + nowTs = getNewTimestamp(); + } +} + +int Channel::handleDrain(int limit, int timeout) +{ + #ifdef TEST + *logofs << "handleDrain: Going to drain FD#" << fd_ + << " with a limit of " << limit << " bytes " + << "at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + T_timestamp startTs = getNewTimestamp(); + + T_timestamp nowTs = startTs; + + int drained; + int remaining; + + int result; + + for (;;) + { + remaining = timeout - diffTimestamp(startTs, nowTs); + + if (remaining <= 0) + { + #ifdef TEST + *logofs << "handleDrain: Timeout raised while draining " + << "FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + result = 0; + + goto ChannelDrainEnd; + } + + #ifdef TEST + *logofs << "handleDrain: Trying to write to FD#" + << fd_ << " with " << remaining << " Ms " + << "remaining.\n" << logofs_flush; + #endif + + drained = transport_ -> drain(limit, remaining); + + if (drained == 1) + { + #ifdef TEST + *logofs << "handleDrain: Transport for FD#" << fd_ + << " drained to " << transport_ -> length() + << " bytes at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + result = 1; + + goto ChannelDrainEnd; + } + else if (drained == 0 && transport_ -> readable() > 0) + { + #ifdef TEST + *logofs << "handleDrain: WARNING! Encoding more data " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + if (proxy -> handleAsyncRead(fd_) < 0) + { + goto ChannelDrainError; + } + } + else if (drained == -1) + { + goto ChannelDrainError; + } + + nowTs = getNewTimestamp(); + + if (diffTimestamp(startTs, nowTs) >= control -> ChannelTimeout) + { + int seconds = (remaining + control -> LatencyTimeout * 10) / 1000; + + #ifdef WARNING + *logofs << "handleDrain: WARNING! Could not drain FD#" + << fd_ << " within " << seconds << " seconds.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Can't write to connection on FD#" + << fd_ << " since " << seconds << " seconds.\n"; + + if (alert_ == 0) + { + if (control -> ProxyMode == proxy_client) + { + alert_ = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT; + } + else + { + alert_ = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT; + } + + HandleAlert(alert_, 1); + } + } + } + +ChannelDrainEnd: + + // + // Maybe we drained the channel and are + // now out of the congestion state. + // + + handleCongestion(); + + return result; + +ChannelDrainError: + + finish_ = 1; + + return -1; +} + +int Channel::handleCongestion() +{ + // + // Send a begin congestion control code + // if the local end of the channel does + // not consume its data. + // + + if (isCongested() == 1) + { + if (congestion_ == 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleCongestion: Sending congestion for FD#" + << fd_ << " with length " << transport_ -> length() + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + congestion_ = 1; + + // + // Use the callback to send the control + // code immediately. + // + + if (proxy -> handleAsyncCongestion(fd_) < 0) + { + finish_ = 1; + + return -1; + } + } + } + else + { + // + // If the channel was in congestion state + // send an end congestion control code. + // + + if (congestion_ == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleCongestion: Sending decongestion for FD#" + << fd_ << " with length " << transport_ -> length() + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + congestion_ = 0; + + if (proxy -> handleAsyncDecongestion(fd_) < 0) + { + finish_ = 1; + + return -1; + } + } + + // + // Remove the "channel unresponsive" + // dialog. + // + + if (alert_ != 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleCongestion: Displacing the dialog " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + HandleAlert(DISPLACE_MESSAGE_ALERT, 1); + } + } + + return 1; +} + +int Channel::handleFlush(T_flush type, int bufferLength, int scratchLength) +{ + if (finish_ == 1) + { + #ifdef TEST + *logofs << "handleFlush: Not flushing data for " + << "finishing channel for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + writeBuffer_.fullReset(); + + return -1; + } + + #ifdef TEST + *logofs << "handleFlush: Flushing " << bufferLength + << " + " << scratchLength << " bytes " + << "to FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + // + // Check if the channel has data available. + // Recent Linux kernels are very picky. + // They require that we read often or they + // assume that the process is non-interact- + // ive. + // + + int result = 0; + + if (handleAsyncEvents() < 0) + { + goto ChannelFlushError; + } + + // + // Write the data in the main buffer first, + // followed by the data in the scratch buffer. + // + + if (bufferLength > 0) + { + result = transport_ -> write(write_immediate, + writeBuffer_.getData(), bufferLength); + } + + if (result >= 0 && scratchLength > 0) + { + result = transport_ -> write(write_immediate, + writeBuffer_.getScratchData(), scratchLength); + } + + if (type == flush_if_any) + { + writeBuffer_.fullReset(); + } + else + { + writeBuffer_.partialReset(); + } + + // + // If we failed to write to the X connection then + // set the finish flag. The caller should continue + // to handle all the remaining messages or it will + // corrupt the decode buffer. At the real end, an + // error will be propagated to the upper layers + // which will perform any needed cleanup. + // + + if (result < 0) + { + goto ChannelFlushError; + } + + // + // Reset transport buffers. + // + + transport_ -> partialReset(); + + // + // Check if the X server has generated + // any event in response to our data. + // + + if (handleAsyncEvents() < 0) + { + goto ChannelFlushError; + } + + // + // Check if the channel has entered in + // congestion state and, in this case, + // send an immediate congestion control + // code to the remote. + // + + handleCongestion(); + + // + // We could optionally drain the output + // buffer if this is X11 channel. + // + // if (isCongested() == 1 && isReliable() == 1) + // { + // if (handleDrain(0, control -> ChannelTimeout) < 0) + // { + // goto ChannelFlushError; + // } + // } + // + + return 1; + +ChannelFlushError: + + finish_ = 1; + + return -1; +} + +int Channel::handleFlush() +{ + #ifdef TEST + *logofs << "handleFlush: Flushing " + << transport_ -> length() << " bytes to FD#" + << fd_ << " with descriptor writable.\n" + << logofs_flush; + #endif + + // + // Check if there is anything to read + // before anf after having written to + // the socket. + // + + if (handleAsyncEvents() < 0) + { + goto ChannelFlushError; + } + + if (transport_ -> flush() < 0) + { + #ifdef TEST + *logofs << "handleFlush: Failure detected " + << "flushing data to FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + goto ChannelFlushError; + } + + if (handleAsyncEvents() < 0) + { + goto ChannelFlushError; + } + + // + // Reset channel's transport buffers. + // + + transport_ -> partialReset(); + + // + // Check if the channel went out of the + // congestion state. + // + + handleCongestion(); + + return 1; + +ChannelFlushError: + + finish_ = 1; + + return -1; +} + +void Channel::handleResetAlert() +{ + if (alert_ != 0) + { + #ifdef TEST + *logofs << "handleResetAlert: The channel alert '" + << alert_ << "' was displaced.\n" + << logofs_flush; + #endif + + alert_ = 0; + } +} + +int Channel::handleCompress(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned int offset, const unsigned char *buffer, + const unsigned int size, unsigned char *&compressedData, + unsigned int &compressedDataSize) +{ + if (size <= offset) + { + #ifdef DEBUG + *logofs << "handleCompress: Not compressing data for FD#" << fd_ + << " as offset is " << offset << " with data size " + << size << ".\n" << logofs_flush; + #endif + + return 0; + } + + #ifdef DEBUG + *logofs << "handleCompress: Compressing data for FD#" << fd_ + << " with data size " << size << " and offset " + << offset << ".\n" << logofs_flush; + #endif + + // + // It is responsibility of the compressor to + // mark the buffer as such if the compression + // couldn't take place. + // + + if (compressor_ -> compressBuffer(buffer + offset, size - offset, compressedData, + compressedDataSize, encodeBuffer) <= 0) + { + #ifdef DEBUG + *logofs << "handleCompress: Sent " << size - offset + << " bytes of plain data for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + return 0; + } + else + { + #ifdef DEBUG + *logofs << "handleCompress: Sent " << compressedDataSize + << " bytes of compressed data for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 1; + } +} + +int Channel::handleDecompress(DecodeBuffer &decodeBuffer, const unsigned char opcode, + const unsigned int offset, unsigned char *buffer, + const unsigned int size, const unsigned char *&compressedData, + unsigned int &compressedDataSize) +{ + if (size <= offset) + { + return 0; + } + + int result = compressor_ -> decompressBuffer(buffer + offset, size - offset, + compressedData, compressedDataSize, + decodeBuffer); + if (result < 0) + { + #ifdef PANIC + *logofs << "handleDecompress: PANIC! Failed to decompress " + << size - offset << " bytes of data for FD#" << fd_ + << " with OPCODE#" << (unsigned int) opcode << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Data decompression failed for OPCODE#" + << (unsigned int) opcode << ".\n"; + + return -1; + } + else if (result == 0) + { + #ifdef DEBUG + *logofs << "handleDecompress: Received " << size - offset + << " bytes of plain data for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + return 0; + } + else + { + #ifdef DEBUG + *logofs << "handleDecompress: Received " << compressedDataSize + << " bytes of compressed data for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + return 1; + } +} + +int Channel::handleCleanAndNullRequest(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + #ifdef TEST + *logofs << "handleCleanAndNullRequest: Removing the previous data " + << "and sending an X_NoOperation " << "for FD#" << fd_ + << " due to OPCODE#" << (unsigned int) opcode << " (" + << DumpOpcode(opcode) << ").\n" << logofs_flush; + #endif + + writeBuffer_.removeMessage(size - 4); + + size = 4; + opcode = X_NoOperation; + + return 1; +} + +int Channel::handleNullRequest(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + #ifdef TEST + *logofs << "handleNullRequest: Sending an X_NoOperation for FD#" + << fd_ << " due to OPCODE#" << (unsigned int) opcode + << " (" << DumpOpcode(opcode) << ").\n" + << logofs_flush; + #endif + + size = 4; + buffer = writeBuffer_.addMessage(size); + opcode = X_NoOperation; + + return 1; +} + +void Channel::handleSplitStoreError(int resource) +{ + if (resource < 0 || resource >= CONNECTIONS_LIMIT) + { + #ifdef PANIC + *logofs << "handleSplitStoreError: PANIC! Resource " + << resource << " is out of range with limit " + << "set to " << CONNECTIONS_LIMIT << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Resource " << resource + << " is out of range with limit set to " + << CONNECTIONS_LIMIT << ".\n"; + + HandleCleanup(); + } + else + { + #ifdef PANIC + *logofs << "handleSplitStoreError: PANIC! Cannot " + << "allocate the split store for resource " + << resource << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot allocate the " + << "split store for resource " << resource + << ".\n"; + + HandleCleanup(); + } +} + +void Channel::handleSplitStoreAlloc(List *list, int resource) +{ + if (resource < 0 || resource >= CONNECTIONS_LIMIT) + { + handleSplitStoreError(resource); + } + + if (clientStore_ -> getSplitStore(resource) == NULL) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitStoreAlloc: Allocating a new " + << "split store for resource " << resource + << ".\n" << logofs_flush; + #endif + + SplitStore *splitStore = clientStore_ -> createSplitStore(resource); + + if (splitStore == NULL) + { + handleSplitStoreError(resource); + } + + list -> add(resource); + } + #if defined(TEST) || defined(SPLIT) + else + { + // + // Old proxy versions only use a single + // split store. + // + + if (resource != 0) + { + *logofs << "handleSplitStoreAlloc: WARNING! A split " + << "store for resource " << resource + << " already exists.\n" << logofs_flush; + } + } + #endif +} + +void Channel::handleSplitStoreRemove(List *list, int resource) +{ + if (resource < 0 || resource >= CONNECTIONS_LIMIT) + { + handleSplitStoreError(resource); + } + + SplitStore *splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore != NULL) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitStoreRemove: Deleting the " + << "split store for resource " << resource + << ".\n" << logofs_flush; + #endif + + clientStore_ -> destroySplitStore(resource); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitStoreRemove: Deleting resource " + << resource << " from the list " << ".\n" + << logofs_flush; + #endif + + list -> remove(resource); + } + #if defined(TEST) || defined(SPLIT) + else + { + *logofs << "handleSplitStoreRemove: WARNING! A split " + << "store for resource " << resource + << " does not exist.\n" << logofs_flush; + } + #endif +} + +Split *Channel::handleSplitCommitRemove(int request, int resource, int position) +{ + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitCommitRemove: SPLIT! Checking split " + << "commit with resource " << resource << " request " + << request << " and position " << position + << ".\n" << logofs_flush; + #endif + + // + // Remove the split from the split queue. + // + + CommitStore *commitStore = clientStore_ -> getCommitStore(); + + Split *split = commitStore -> pop(); + + if (split == NULL) + { + #ifdef PANIC + *logofs << "handleSplitCommitRemove: PANIC! Can't " + << "find the split in the commit queue.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't find the " + << "split in the commit queue.\n"; + + HandleCleanup(); + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitCommitRemove: SPLIT! Element from " + << "the queue has resource " << split -> getResource() + << " request " << split -> getRequest() << " and " + << "position " << split -> getPosition() + << ".\n" << logofs_flush; + #endif + + if ((control -> isProtoStep7() == 1 && + (resource != split -> getResource() || + request != split -> getRequest() || + position != split -> getPosition())) || + (request != split -> getRequest() || + position != split -> getPosition())) + { + #ifdef PANIC + *logofs << "handleSplitCommitRemove: PANIC! The data in " + << "the split doesn't match the commit request.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": The data in the split doesn't " + << "match the commit request.\n"; + + return NULL; + } + + #if defined(TEST) || defined(SPLIT) + + commitStore -> dump(); + + #endif + + return split; +} + +int Channel::setReferences() +{ + #ifdef TEST + *logofs << "Channel: Initializing the static " + << "members for the base class.\n" + << logofs_flush; + #endif + + firstClient_ = -1; + + fontPort_ = -1; + + #ifdef REFERENCES + + references_ = 0; + + #endif + + return 1; +} + +int Channel::setOpcodes(OpcodeStore *opcodeStore) +{ + opcodeStore_ = opcodeStore; + + #ifdef TEST + *logofs << "setOpcodes: Propagated opcodes store to channel " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 1; +} + +int Channel::setStores(ClientStore *clientStore, ServerStore *serverStore) +{ + clientStore_ = clientStore; + serverStore_ = serverStore; + + #ifdef TEST + *logofs << "setStores: Propagated message stores to channel " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 1; +} + +int Channel::setCaches(ClientCache *clientCache, ServerCache *serverCache) +{ + clientCache_ = clientCache; + serverCache_ = serverCache; + + #ifdef TEST + *logofs << "setCaches: Propagated encode caches to channel " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 1; +} diff --git a/nxcomp/Channel.h b/nxcomp/Channel.h new file mode 100644 index 000000000..68fe1d7e9 --- /dev/null +++ b/nxcomp/Channel.h @@ -0,0 +1,656 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Channel_H +#define Channel_H + +#include "Transport.h" + +#include "WriteBuffer.h" + +#include "OpcodeStore.h" + +#include "ClientStore.h" +#include "ServerStore.h" + +#include "ClientCache.h" +#include "ServerCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Forward declaration of referenced classes. +// + +class List; + +class StaticCompressor; + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Define this to log a line when a channel +// is created or destroyed. +// + +#undef REFERENCES + +// +// Type of traffic carried by channel. +// + +typedef enum +{ + channel_none = -1, + channel_x11, + channel_cups, + channel_smb, + channel_media, + channel_http, + channel_font, + channel_slave, + channel_last_tag + +} T_channel_type; + +// +// Type of notification event to be sent +// by proxy to the X channel. +// + +typedef enum +{ + notify_no_split, + notify_start_split, + notify_commit_split, + notify_end_split, + notify_empty_split, + +} T_notification_type; + +class Channel +{ + public: + + // + // Maximum number of X connections supported. + // + + static const int CONNECTIONS_LIMIT = 256; + + Channel(Transport *transport, StaticCompressor *compressor); + + virtual ~Channel(); + + // + // Read any X message available on the X + // connection and encode it to the encode + // buffer. + // + + virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, + unsigned int length) = 0; + + // + // Decode any X message encoded in the + // proxy message and write it to the X + // connection. + // + + virtual int handleWrite(const unsigned char *message, unsigned int length) = 0; + + // + // Other methods to be implemented in + // client, server and generic channel + // classes. + // + + virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store, + T_store_action action, int position, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) = 0; + + virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, + T_store_action action, int position, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) = 0; + + virtual int handleSplit(EncodeBuffer &encodeBuffer) = 0; + + virtual int handleSplit(DecodeBuffer &decodeBuffer) = 0; + + virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split) = 0; + + virtual int handleSplitEvent(DecodeBuffer &decodeBuffer) = 0; + + virtual int handleMotion(EncodeBuffer &encodeBuffer) = 0; + + virtual int handleCompletion(EncodeBuffer &encodeBuffer) = 0; + + virtual int handleConfiguration() = 0; + + virtual int handleFinish() = 0; + + // + // Interleave reads of the available + // events while writing data to the + // channel socket. + // + + virtual int handleAsyncEvents() = 0; + + // + // Handle the channel tear down. + // + + int handleClosing() + { + closing_ = 1; + + return 1; + } + + int handleDrop() + { + drop_ = 1; + + return 1; + } + + // + // Try to read more data from the socket. In + // the meanwhile flush any enqueued data if + // the channel is blocked. Return as soon as + // more data has been read or the timeout has + // been exceeded. + // + + int handleWait(int timeout); + + // + // Drain the output buffer while handling the + // data that may become readable. + // + + int handleDrain(int timeout, int limit); + + // + // Flush any remaining data in the transport + // buffer. + // + + int handleFlush(); + + // + // Called when the loop has replaced or + // closed a previous alert. + // + + void handleResetAlert(); + + // + // Initialize all the static members. + // + + static int setReferences(); + + // + // Set pointer to object mapping opcodes + // of NX specific messages. + // + + int setOpcodes(OpcodeStore *opcodeStore); + + // + // Update pointers to message stores in + // channels. + // + + int setStores(ClientStore *clientStore, ServerStore *serverStore); + + // + // The same for channels caches. + // + + int setCaches(ClientCache *clientCache, ServerCache *serverCache); + + // + // Set the port used for tunneling of the + // font server connections. + // + + void setPorts(int fontPort) + { + fontPort_ = fontPort; + } + + // + // Check if there are pending split + // to send to the remote side. + // + + virtual int needSplit() const = 0; + + // + // Check if there are motion events + // to flush. + // + + virtual int needMotion() const = 0; + + // + // Return the type of traffic carried + // by this channel. + // + + virtual T_channel_type getType() const = 0; + + // + // Check if the channel has been marked + // as closing down. + // + + int getFinish() const + { + return finish_; + } + + int getClosing() + { + return closing_; + } + + int getDrop() + { + return drop_; + } + + int getCongestion() + { + return congestion_; + } + + protected: + + int handleFlush(T_flush type) + { + // + // We could write the data immediately if there + // is already something queued to the low level + // TCP buffers. + // + // if (... || transport_ -> queued() > 0) + // { + // ... + // } + // + + if (writeBuffer_.getScratchLength() > 0 || + (type == flush_if_any && writeBuffer_.getLength() > 0) || + writeBuffer_.getLength() >= (unsigned int) + control -> TransportFlushBufferSize) + { + return handleFlush(type, writeBuffer_.getLength(), + writeBuffer_.getScratchLength()); + } + + return 0; + } + + // + // Actually flush the data to the + // channel descriptor. + // + + int handleFlush(T_flush type, int bufferLength, int scratchLength); + + // + // Handle the congestion changes. + // + + int handleCongestion(); + + // + // Encode and decode X messages. + // + + int handleEncode(EncodeBuffer &encodeBuffer, ChannelCache *channelCache, + MessageStore *store, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + int handleDecode(DecodeBuffer &decodeBuffer, ChannelCache *channelCache, + MessageStore *store, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + // + // Encode the message based on its + // message store. + // + + int handleEncodeCached(EncodeBuffer &encodeBuffer, ChannelCache *channelCache, + MessageStore *store, const unsigned char *buffer, + const unsigned int size); + + int handleDecodeCached(DecodeBuffer &decodeBuffer, ChannelCache *channelCache, + MessageStore *store, unsigned char *&buffer, + unsigned int &size); + + int handleEncodeIdentity(EncodeBuffer &encodeBuffer, ChannelCache *channelCache, + MessageStore *store, const unsigned char *buffer, + const unsigned int size, int bigEndian) + { + return (store -> encodeIdentity(encodeBuffer, buffer, size, + bigEndian, channelCache)); + } + + int handleDecodeIdentity(DecodeBuffer &decodeBuffer, ChannelCache *channelCache, + MessageStore *store, unsigned char *&buffer, + unsigned int &size, int bigEndian, + WriteBuffer *writeBuffer) + { + return (store -> decodeIdentity(decodeBuffer, buffer, size, bigEndian, + writeBuffer, channelCache)); + } + + // + // Other utility functions used by + // the encoding and decoding methods. + // + + void handleCopy(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned int offset, const unsigned char *buffer, + const unsigned int size) + { + if (size > offset) + { + encodeBuffer.encodeMemory(buffer + offset, size - offset); + } + } + + void handleCopy(DecodeBuffer &decodeBuffer, const unsigned char opcode, + const unsigned int offset, unsigned char *buffer, + const unsigned int size) + { + if (size > offset) + { + memcpy(buffer + offset, decodeBuffer.decodeMemory(size - offset), size - offset); + } + } + + void handleUpdate(MessageStore *store, const unsigned int dataSize, + const unsigned int compressedDataSize) + { + if (store -> lastAction == IS_ADDED) + { + handleUpdateAdded(store, dataSize, compressedDataSize); + } + } + + void handleSave(MessageStore *store, unsigned char *buffer, unsigned int size, + const unsigned char *compressedData = NULL, + const unsigned int compressedDataSize = 0) + { + if (store -> lastAction == IS_ADDED) + { + handleSaveAdded(store, 0, buffer, size, compressedData, compressedDataSize); + } + } + + void handleSaveSplit(MessageStore *store, unsigned char *buffer, + unsigned int size) + { + if (store -> lastAction == IS_ADDED) + { + return handleSaveAdded(store, 1, buffer, size, 0, 0); + } + } + + void handleUpdateAdded(MessageStore *store, const unsigned int dataSize, + const unsigned int compressedDataSize); + + void handleSaveAdded(MessageStore *store, int split, unsigned char *buffer, + unsigned int size, const unsigned char *compressedData, + const unsigned int compressedDataSize); + + // + // Compress the data part of a message + // using ZLIB or another compressor + // and send it over the network. + // + + int handleCompress(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned int offset, const unsigned char *buffer, + const unsigned int size, unsigned char *&compressedData, + unsigned int &compressedDataSize); + + int handleDecompress(DecodeBuffer &decodeBuffer, const unsigned char opcode, + const unsigned int offset, unsigned char *buffer, + const unsigned int size, const unsigned char *&compressedData, + unsigned int &compressedDataSize); + + // + // Send an X_NoOperation to the X server. + // The second version also removes any + // previous data in the write buffer. + // + + int handleNullRequest(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size); + + int handleCleanAndNullRequest(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size); + + // + // X11 channels are considered to be in + // congestion state when there was a + // blocking write and, since then, the + // local end didn't consume all the data. + // + + virtual int isCongested() + { + return (transport_ -> getType() != + transport_agent && transport_ -> length() > + control -> TransportFlushBufferSize); + } + + virtual int isReliable() + { + return 1; + } + + // + // Determine how to handle allocation + // of new messages in the message + // stores. + // + + int mustCleanStore(MessageStore *store) + { + return (store -> getRemoteTotalStorageSize() > control -> + RemoteTotalStorageSize || store -> getLocalTotalStorageSize() > + control -> LocalTotalStorageSize || (store -> getRemoteStorageSize() > + (control -> RemoteTotalStorageSize / 100 * store -> + cacheThreshold)) || (store -> getLocalStorageSize() > + (control -> LocalTotalStorageSize / 100 * store -> + cacheThreshold))); + } + + int canCleanStore(MessageStore *store) + { + return ((store -> getSize() > 0 && (store -> getRemoteStorageSize() > + (control -> RemoteTotalStorageSize / 100 * store -> + cacheLowerThreshold))) || (store -> getLocalStorageSize() > + (control -> LocalTotalStorageSize / 100 * store -> + cacheLowerThreshold))); + } + + protected: + + // + // Set up the split stores. + // + + void handleSplitStoreError(int resource); + + void handleSplitStoreAlloc(List *list, int resource); + void handleSplitStoreRemove(List *list, int resource); + + Split *handleSplitCommitRemove(int request, int resource, int position); + + void validateSize(const char *name, int input, int output, + int offset, int size) + { + if (size < offset || size > control -> MaximumMessageSize || + size != (int) RoundUp4(input) + offset || + output > control -> MaximumMessageSize) + { + *logofs << "Channel: PANIC! Invalid size " << size + << " for " << name << " output with data " + << input << "/" << output << "/" << offset + << "/" << size << ".\n" << logofs_flush; + + cerr << "Error" << ": Invalid size " << size + << " for " << name << " output.\n"; + + HandleAbort(); + } + } + + // + // Is the X client big endian? + // + + int bigEndian() const + { + return bigEndian_; + } + + int bigEndian_; + + // + // Other X server's features + // saved at session startup. + // + + unsigned int imageByteOrder_; + unsigned int bitmapBitOrder_; + unsigned int scanlineUnit_; + unsigned int scanlinePad_; + + int firstRequest_; + int firstReply_; + + // + // Use this class for IO operations. + // + + Transport *transport_; + + // + // The static compressor is created by the + // proxy and shared among channels. + // + + StaticCompressor *compressor_; + + // + // Map NX operations to opcodes. Propagated + // by proxy to all channels on the same X + // server. + // + + OpcodeStore *opcodeStore_; + + // + // Also stores are shared between channels. + // + + ClientStore *clientStore_; + ServerStore *serverStore_; + + // + // Caches are specific for each channel. + // + + ClientCache *clientCache_; + ServerCache *serverCache_; + + // + // Data going to X connection. + // + + WriteBuffer writeBuffer_; + + // + // Other data members. + // + + int fd_; + + int finish_; + int closing_; + int drop_; + int congestion_; + int priority_; + + int alert_; + + // + // It will be set to the descriptor of the + // first X channel that is successfully con- + // nected and will print an info message on + // standard error. + // + + static int firstClient_; + + // + // Port used for font server connections. + // + + static int fontPort_; + + // + // Track which cache operations have been + // enabled by the agent. + // + + int enableCache_; + int enableSplit_; + int enableSave_; + int enableLoad_; + + // + // Keep track of object creation and + // deletion. + // + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +#endif /* Channel_H */ diff --git a/nxcomp/ChannelCache.cpp b/nxcomp/ChannelCache.cpp new file mode 100644 index 000000000..eaf8e426b --- /dev/null +++ b/nxcomp/ChannelCache.cpp @@ -0,0 +1,56 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ChannelCache.h" + +const unsigned int CONFIGUREWINDOW_FIELD_WIDTH[7] = +{ + 16, // x + 16, // y + 16, // width + 16, // height + 16, // border width + 29, // sibling window + 3 // stack mode +}; + +const unsigned int CREATEGC_FIELD_WIDTH[23] = +{ + 4, // function + 32, // plane mask + 32, // foreground + 32, // background + 16, // line width + 2, // line style + 2, // cap style + 2, // join style + 2, // fill style + 1, // fill rule + 29, // tile + 29, // stipple + 16, // tile/stipple x origin + 16, // tile/stipple y origin + 29, // font + 1, // subwindow mode + 1, // graphics exposures + 16, // clip x origin + 16, // clip y origin + 29, // clip mask + 16, // card offset + 8, // dashes + 1 // arc mode +}; diff --git a/nxcomp/ChannelCache.h b/nxcomp/ChannelCache.h new file mode 100644 index 000000000..7b94893b6 --- /dev/null +++ b/nxcomp/ChannelCache.h @@ -0,0 +1,60 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ChannelCache_H +#define ChannelCache_H + +// +// Elements in array of caches used in TextCompressor. +// + +const unsigned int CLIENT_TEXT_CACHE_SIZE = 9999; +const unsigned int SERVER_TEXT_CACHE_SIZE = 9999; + +// +// Sizes of optional fields for ConfigureWindow +// request. +// + +extern const unsigned int CONFIGUREWINDOW_FIELD_WIDTH[7]; + +// +// Sizes of optional fields for CreateGC request. +// + +extern const unsigned int CREATEGC_FIELD_WIDTH[23]; + +// +// This is just needed to provide a pointer +// to the base cache class in encoding and +// decoding procedures of message stores. +// + +class ChannelCache +{ + public: + + ChannelCache() + { + } + + ~ChannelCache() + { + } +}; + +#endif /* ChannelCache_H */ diff --git a/nxcomp/ChannelStore.h b/nxcomp/ChannelStore.h new file mode 100644 index 000000000..3103fb308 --- /dev/null +++ b/nxcomp/ChannelStore.h @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ChannelStore_H +#define ChannelStore_H + +// +// One message store for each opcode. +// + +#define CHANNEL_STORE_OPCODE_LIMIT 256 + +// +// One split store for each resource. +// + +#define CHANNEL_STORE_RESOURCE_LIMIT 256 + +class ChannelStore +{ + public: + + ChannelStore() + { + } + + virtual ~ChannelStore() + { + } +}; + +#endif /* ChannelStore_H */ diff --git a/nxcomp/CharCache.cpp b/nxcomp/CharCache.cpp new file mode 100644 index 000000000..2803a1b1d --- /dev/null +++ b/nxcomp/CharCache.cpp @@ -0,0 +1,61 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "CharCache.h" + +int CharCache::lookup(unsigned char value, unsigned int &index) +{ + for (unsigned int i = 0; i < length_; i++) + if (value == buffer_[i]) + { + index = i; + if (i) + { + unsigned int target = (i >> 1); + do + { + buffer_[i] = buffer_[i - 1]; + i--; + } + while (i > target); + buffer_[target] = value; + } + return 1; + } + insert(value); + return 0; +} + +void CharCache::insert(unsigned char value) +{ + unsigned int insertionPoint = 0; + if (2 >= length_) + insertionPoint = length_; + else + insertionPoint = 2; + unsigned int start; + if (length_ >= 7) + start = 7 - 1; + else + { + start = length_; + length_++; + } + for (unsigned int k = start; k > insertionPoint; k--) + buffer_[k] = buffer_[k - 1]; + buffer_[insertionPoint] = value; +} diff --git a/nxcomp/CharCache.h b/nxcomp/CharCache.h new file mode 100644 index 000000000..53710f181 --- /dev/null +++ b/nxcomp/CharCache.h @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef CharCache_H +#define CharCache_H + +// +// CharCache is a counterpart of IntCache that is +// optimized for use in compressing text composed +// of 8-bit characters. +// + +class CharCache +{ + public: + + CharCache() : length_(0) + { + } + + ~CharCache() + { + } + + unsigned int getSize() const + { + return (unsigned int) length_; + } + + int lookup(unsigned char value, unsigned int &index); + + // + // This can be inlined as it is only + // called by decodeCachedValue(). + // + + unsigned int get(unsigned int index) + { + unsigned char result = buffer_[index]; + + if (index != 0) + { + unsigned int i = index; + unsigned int target = (i >> 1); + + do + { + buffer_[i] = buffer_[i - 1]; + + i--; + } + while (i > target); + + buffer_[target] = result; + } + + return (unsigned int) result; + } + + void insert(unsigned char value); + + private: + + unsigned char length_; + + unsigned char buffer_[7]; +}; + +#endif /* CharCache_H */ diff --git a/nxcomp/Children.cpp b/nxcomp/Children.cpp new file mode 100644 index 000000000..a19b882e8 --- /dev/null +++ b/nxcomp/Children.cpp @@ -0,0 +1,1038 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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 (*handler)(int); + +extern int useUnixSocket; + +extern int lastDialog; +extern int lastWatchdog; +extern int lastKeeper; + +extern void CleanupListeners(); +extern void CleanupSockets(); +extern void CleanupAgent(); +extern void CleanupGlobal(); + +extern void InstallSignals(); + +extern char *GetClientPath(); + +extern int CheckParent(const char *name, const char *type, + int parent); + +#ifdef __sun +extern char **environ; +#endif + +// +// Close all the unused descriptors and +// install any signal handler that might +// have been disabled in the main process. +// + +static void SystemCleanup(const char *name); + +// +// Release all objects allocated in the +// heap. + +static void MemoryCleanup(const char *name); + +// +// Remove 'name' from the environment. +// + +static int UnsetEnv(const char *name); + +static int NXTransKeeperHandler(int signal); +static void NXTransKeeperCheck(); + + +// +// Start a nxclient process in dialog mode. +// + +int NXTransDialog(const char *caption, const char *message, + const char *window, const char *type, int local, + const char* display) +{ + // + // Be sure log file is valid. + // + + if (logofs == NULL) + { + logofs = &cerr; + } + + int pid; + + #ifdef TEST + *logofs << "NXTransDialog: Going to fork with NX pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + pid = Fork(); + + if (pid != 0) + { + if (pid < 0) + { + #ifdef TEST + *logofs << "NXTransDialog: WARNING! Function fork failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Function fork failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + } + #ifdef TEST + else + { + *logofs << "NXTransDialog: Created NX dialog process " + << "with pid '" << pid << "'.\n" + << logofs_flush; + } + #endif + + return pid; + } + + #ifdef TEST + *logofs << "NXTransDialog: Executing child with pid '" + << getpid() << "' and parent '" << getppid() + << "'.\n" << logofs_flush; + #endif + + SystemCleanup("NXTransDialog"); + + // + // Copy the client command before + // freeing up the control class. + // + + char command[DEFAULT_STRING_LIMIT]; + + if (control != NULL) + { + strcpy(command, control -> ClientPath); + } + else + { + char *path = GetClientPath(); + + strcpy(command, path); + + delete [] path; + } + + // + // Get rid of the unused resources. + // + + MemoryCleanup("NXTransDialog"); + + #ifdef TEST + *logofs << "NXTransDialog: Running external NX dialog with caption '" + << caption << "' message '" << message << "' type '" + << type << "' local '" << local << "' display '" + << display << "'.\n" + << logofs_flush; + #endif + + int pulldown = (strcmp(type, "pulldown") == 0); + + char parent[DEFAULT_STRING_LIMIT]; + + snprintf(parent, DEFAULT_STRING_LIMIT, "%d", getppid()); + + parent[DEFAULT_STRING_LIMIT - 1] = '\0'; + + UnsetEnv("LD_LIBRARY_PATH"); + + for (int i = 0; i < 2; i++) + { + if (local != 0) + { + if (pulldown) + { + execlp(command, command, "--dialog", type, "--caption", caption, + "--window", window, "--local", "--parent", parent, + "--display", display, NULL); + } + else + { + execlp(command, command, "--dialog", type, "--caption", caption, + "--message", message, "--local", "--parent", parent, + "--display", display, NULL); + } + } + else + { + if (pulldown) + { + execlp(command, command, "--dialog", type, "--caption", caption, + "--window", window, "--parent", parent, + "--display", display, NULL); + } + else + { + execlp(command, command, "--dialog", type, "--caption", caption, + "--message", message, "--parent", parent, + "--display", display, NULL); + } + } + + #ifdef WARNING + *logofs << "NXTransDialog: WARNING! Couldn't start '" + << command << "'. " << "Error is " << EGET() + << " '" << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Couldn't start '" << command + << "'. Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + // + // Retry by looking for the default name + // in the default NX path. + // + + strcpy(command, "nxclient"); + + char newPath[DEFAULT_STRING_LIMIT]; + + strcpy(newPath, "/usr/NX/bin:/opt/NX/bin:/usr/local/NX/bin:"); + + #ifdef __APPLE__ + + strcat(newPath, "/Applications/NX Client for OSX.app/Contents/MacOS:"); + + #endif + + #ifdef __CYGWIN32__ + + strcat(newPath, ".:"); + + #endif + + int newLength = strlen(newPath); + + char *oldPath = getenv("PATH"); + + strncpy(newPath + newLength, oldPath, DEFAULT_STRING_LIMIT - newLength - 1); + + newPath[DEFAULT_STRING_LIMIT - 1] = '\0'; + + #ifdef WARNING + *logofs << "NXTransDialog: WARNING! Trying with path '" + << newPath << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Trying with path '" << newPath + << "'.\n"; + + // + // Solaris doesn't seem to have + // function setenv(). + // + + #ifdef __sun + + char newEnv[DEFAULT_STRING_LIMIT + 5]; + + sprintf(newEnv,"PATH=%s", newPath); + + putenv(newEnv); + + #else + + setenv("PATH", newPath, 1); + + #endif + } + + // + // Hopefully useless. + // + + exit(0); +} + +// +// Start a nxclient process in dialog mode. +// + +int NXTransClient(const char* display) +{ + // + // Be sure log file is valid. + // + + if (logofs == NULL) + { + logofs = &cerr; + } + + int pid; + + #ifdef TEST + *logofs << "NXTransClient: Going to fork with NX pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + pid = Fork(); + + if (pid != 0) + { + if (pid < 0) + { + #ifdef TEST + *logofs << "NXTransClient: WARNING! Function fork failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Function fork failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + } + #ifdef TEST + else + { + *logofs << "NXTransClient: Created NX client process " + << "with pid '" << pid << "'.\n" + << logofs_flush; + } + #endif + + return pid; + } + + #ifdef TEST + *logofs << "NXTransClient: Executing child with pid '" + << getpid() << "' and parent '" << getppid() + << "'.\n" << logofs_flush; + #endif + + SystemCleanup("NXTransClient"); + + // + // Copy the client command before + // freeing up the control class. + // + + char command[DEFAULT_STRING_LIMIT]; + + if (control != NULL) + { + strcpy(command, control -> ClientPath); + } + else + { + char *path = GetClientPath(); + + strcpy(command, path); + + delete [] path; + } + + // + // Get rid of unused resources. + // + + MemoryCleanup("NXTransClient"); + + #ifdef TEST + *logofs << "NXTransClient: Running external NX client with display '" + << display << "'.\n" << logofs_flush; + #endif + + // + // Provide the display in the environment. + // + + char newDisplay[DISPLAY_LENGTH_LIMIT]; + + #ifdef __sun + + snprintf(newDisplay, DISPLAY_LENGTH_LIMIT - 1, "DISPLAY=%s", display); + + newDisplay[DISPLAY_LENGTH_LIMIT - 1] = '\0'; + + putenv(newDisplay); + + #else + + strncpy(newDisplay, display, DISPLAY_LENGTH_LIMIT - 1); + + newDisplay[DISPLAY_LENGTH_LIMIT - 1] = '\0'; + + setenv("DISPLAY", newDisplay, 1); + + #endif + + UnsetEnv("LD_LIBRARY_PATH"); + + for (int i = 0; i < 2; i++) + { + execlp(command, command, NULL); + + #ifdef WARNING + *logofs << "NXTransClient: WARNING! Couldn't start '" + << command << "'. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Couldn't start '" << command + << "'. Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + // + // Retry by looking for the default name + // in the default NX path. + // + + strcpy(command, "nxclient"); + + char newPath[DEFAULT_STRING_LIMIT]; + + strcpy(newPath, "/usr/NX/bin:/opt/NX/bin:/usr/local/NX/bin:"); + + #ifdef __APPLE__ + + strcat(newPath, "/Applications/NX Client for OSX.app/Contents/MacOS:"); + + #endif + + #ifdef __CYGWIN32__ + + strcat(newPath, ".:"); + + #endif + + int newLength = strlen(newPath); + + char *oldPath = getenv("PATH"); + + strncpy(newPath + newLength, oldPath, DEFAULT_STRING_LIMIT - newLength - 1); + + newPath[DEFAULT_STRING_LIMIT - 1] = '\0'; + + #ifdef WARNING + *logofs << "NXTransClient: WARNING! Trying with path '" + << newPath << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Trying with path '" << newPath + << "'.\n"; + + // + // Solaris doesn't seem to have + // function setenv(). + // + + #ifdef __sun + + char newEnv[DEFAULT_STRING_LIMIT + 5]; + + sprintf(newEnv,"PATH=%s", newPath); + + putenv(newEnv); + + #else + + setenv("PATH", newPath, 1); + + #endif + } + + // + // Hopefully useless. + // + + exit(0); +} + +// +// Wait until the timeout is expired. +// The timeout is expressed in milli- +// seconds. +// + +int NXTransWatchdog(int timeout) +{ + // + // Be sure log file is valid. + // + + if (logofs == NULL) + { + logofs = &cerr; + } + + int pid; + + #ifdef TEST + *logofs << "NXTransWatchdog: Going to fork with NX pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + pid = Fork(); + + if (pid != 0) + { + if (pid < 0) + { + #ifdef TEST + *logofs << "NXTransWatchdog: WARNING! Function fork failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Function fork failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + } + #ifdef TEST + else + { + *logofs << "NXTransWatchdog: Created NX watchdog process " + << "with pid '" << pid << "'.\n" << logofs_flush; + } + #endif + + return pid; + } + + int parent = getppid(); + + #ifdef TEST + *logofs << "NXTransWatchdog: Executing child with pid '" + << getpid() << "' and parent '" << parent + << "'.\n" << logofs_flush; + #endif + + SystemCleanup("NXTransWatchdog"); + + // + // Get rid of unused resources. + // + + MemoryCleanup("NXTransWatchdog"); + + // + // Run until the timeout is expired + // or forever, if no timeout is + // provided. + // + + T_timestamp startTs = getTimestamp(); + + int diffTs = 0; + + for (;;) + { + // + // Complain if the parent is dead. + // + + if (CheckParent("NXTransWatchdog", "watchdog", parent) == 0) + { + #ifdef TEST + *logofs << "NXTransWatchdog: Exiting with no parent " + << "running.\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + if (timeout > 0) + { + if (diffTs >= timeout) + { + #ifdef TEST + *logofs << "NXTransWatchdog: Timeout of " << timeout + << " Ms raised in watchdog.\n" << logofs_flush; + #endif + + // + // We will just exit. Our parent should be + // monitoring us and detect that the process + // is gone. + // + + HandleCleanup(); + } + } + + if (timeout > 0) + { + #ifdef TEST + *logofs << "NXTransWatchdog: Waiting for the timeout " + << "with " << timeout - diffTs << " Ms to run.\n" + << logofs_flush; + #endif + + usleep((timeout - diffTs) * 1000); + + diffTs = diffTimestamp(startTs, getNewTimestamp()); + } + else + { + #ifdef TEST + *logofs << "NXTransWatchdog: Waiting for a signal.\n" + << logofs_flush; + #endif + + sleep(10); + } + } + + // + // Hopefully useless. + // + + exit(0); +} + +int NXTransKeeperHandler(int signal) +{ + if (keeper != NULL) + { + switch (signal) + { + case SIGTERM: + case SIGINT: + case SIGHUP: + { + #ifdef TEST + *logofs << "NXTransKeeperHandler: Requesting giveup " + << "because of signal " << signal << " ,'" + << DumpSignal(signal) << "'.\n" + << logofs_flush; + #endif + + keeper -> setSignal(signal); + + return 0; + } + } + } + + return 1; +} + +void NXTransKeeperCheck() +{ + if (CheckParent("NXTransKeeper", "keeper", + keeper -> getParent()) == 0 || keeper -> getSignal() != 0) + { + #ifdef TEST + *logofs << "NXTransKeeperCheck: Exiting because of signal " + << "or no parent running.\n" << logofs_flush; + #endif + + HandleCleanup(); + } +} + +int NXTransKeeper(int caches, int images, const char *root) +{ + // + // Be sure log file is valid. + // + + if (logofs == NULL) + { + logofs = &cerr; + } + + if (caches == 0 && images == 0) + { + #ifdef TEST + *logofs << "NXTransKeeper: No NX cache house-keeping needed.\n" + << logofs_flush; + #endif + + return 0; + } + + int pid; + + #ifdef TEST + *logofs << "NXTransKeeper: Going to fork with NX pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + pid = Fork(); + + if (pid != 0) + { + if (pid < 0) + { + #ifdef TEST + *logofs << "NXTransKeeper: WARNING! Function fork failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Function fork failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + } + #ifdef TEST + else + { + *logofs << "NXTransKeeper: Created NX keeper process " + << "with pid '" << pid << "'.\n" + << logofs_flush; + } + #endif + + return pid; + } + + int parent = getppid(); + + #ifdef TEST + *logofs << "NXTransKeeper: Executing child with pid '" + << getpid() << "' and parent '" << parent + << "'.\n" << logofs_flush; + #endif + + SystemCleanup("NXTransKeeper"); + + #ifdef TEST + *logofs << "NXTransKeeper: Going to run with caches " << caches + << " images " << images << " and root " << root + << " at " << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + // + // Create the house-keeper class. + // + + int timeout = control -> KeeperTimeout; + + keeper = new Keeper(caches, images, root, 100, parent); + + handler = NXTransKeeperHandler; + + if (keeper == NULL) + { + #ifdef PANIC + *logofs << "NXTransKeeper: PANIC! Failed to create the keeper object.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failed to create the keeper object.\n"; + + HandleCleanup(); + } + + // + // Get rid of unused resources. Root path + // must be copied in keeper's constructor + // before control is deleted. + // + + MemoryCleanup("NXTransKeeper"); + + // + // Decrease the priority of this process. + // + // The following applies to Cygwin: "Cygwin processes can be + // set to IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, HIGH_- + // PRIORITY_CLASS, or REALTIME_PRIORITY_CLASS with the nice + // call. If you pass a positive number to nice(), then the + // priority level will decrease by one (within the above list + // of priorities). A negative number would make it increase + // by one. It is not possible to change it by more than one + // at a time without making repeated calls". + // + + if (nice(5) < 0 && errno != 0) + { + #ifdef WARNING + *logofs << "NXTransKeeper: WARNING! Failed to renice process to +5. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Failed to renice process to +5. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + } + + // + // Delay a bit the first run to give + // a boost to the session startup. + // + + #ifdef TEST + *logofs << "NXTransKeeper: Going to sleep for " + << timeout / 20 << " Ms.\n" << logofs_flush; + #endif + + usleep(timeout / 20 * 1000); + + NXTransKeeperCheck(); + + // + // The house keeping of the persistent + // caches is performed only once. + // + + if (caches != 0) + { + #ifdef TEST + *logofs << "NXTransKeeper: Going to cleanup the NX cache " + << "directories at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + keeper -> cleanupCaches(); + + #ifdef TEST + *logofs << "NXTransKeeper: Completed cleanup of NX cache " + << "directories at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + } + #ifdef TEST + else + { + *logofs << "NXTransKeeper: Nothing to do for the " + << "persistent caches.\n" << logofs_flush; + } + #endif + + if (images == 0) + { + #ifdef TEST + *logofs << "NXTransKeeper: Nothing to do for the " + << "persistent images.\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + // + // Take care of the persisten image cache. + // Run a number of iterations and then exit, + // so we can keep the memory consumption + // low. The parent will check our exit code + // and will eventually restart us. + // + + for (int iterations = 0; iterations < 100; iterations++) + { + #ifdef TEST + *logofs << "NXTransKeeper: Running iteration " << iterations + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + NXTransKeeperCheck(); + + #ifdef TEST + *logofs << "NXTransKeeper: Going to cleanup the NX images " + << "directories at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + if (keeper -> cleanupImages() < 0) + { + #ifdef TEST + *logofs << "NXTransKeeper: Exiting because of error " + << "handling the image cache.\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + #ifdef TEST + *logofs << "NXTransKeeper: Completed cleanup of NX images " + << "directories at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + NXTransKeeperCheck(); + + #ifdef TEST + *logofs << "NXTransKeeper: Going to sleep for " << timeout + << " Ms.\n" << logofs_flush; + #endif + + usleep(timeout * 1000); + } + + HandleCleanup(2); + + // + // Hopefully useless. + // + + exit(0); +} + +void SystemCleanup(const char *name) +{ + #ifdef TEST + *logofs << name << ": Performing system cleanup in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + // + // Reinstall signals that might + // have been restored by agents. + // + + InstallSignals(); +} + +void MemoryCleanup(const char *name) +{ + #ifdef TEST + *logofs << name << ": Performing memory cleanup in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + DisableSignals(); + + // + // Prevent deletion of unix socket + // and lock file. + // + + useUnixSocket = 0; + + // + // Don't let cleanup kill other + // children. + // + + lastDialog = 0; + lastWatchdog = 0; + lastKeeper = 0; + + CleanupListeners(); + + CleanupSockets(); + + CleanupGlobal(); + + EnableSignals(); +} + +int UnsetEnv(const char *name) +{ + int result; + + #ifdef __sun + + char **pEnv = environ; + + int nameLen = strlen(name) + 1; + + char *varName = new char[nameLen + 1]; + + strcpy(varName, name); + + strcat(varName, "="); + + pEnv = environ; + + while (*pEnv != NULL) + { + if (!strncmp(varName, *pEnv, nameLen)) + { + break; + } + + *pEnv++; + } + + while (*pEnv != NULL) + { + *pEnv = *(pEnv + 1); + + pEnv++; + } + + result = 0; + + #else + + #ifdef __APPLE__ + + unsetenv(name); + result = 0; + + #else + + result = unsetenv(name); + + #endif + + #endif + + return result; +} diff --git a/nxcomp/ClearArea.cpp b/nxcomp/ClearArea.cpp new file mode 100644 index 000000000..223a3b3e1 --- /dev/null +++ b/nxcomp/ClearArea.cpp @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ClearArea.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int ClearAreaStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ClearAreaMessage *clearArea = (ClearAreaMessage *) message; + + // + // Here is the fingerprint. + // + + clearArea -> exposures = *(buffer + 1); + + clearArea -> window = GetULONG(buffer + 4, bigEndian); + + clearArea -> x = GetUINT(buffer + 8, bigEndian); + clearArea -> y = GetUINT(buffer + 10, bigEndian); + clearArea -> width = GetUINT(buffer + 12, bigEndian); + clearArea -> height = GetUINT(buffer + 14, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ClearAreaStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ClearAreaMessage *clearArea = (ClearAreaMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = clearArea -> exposures; + + PutULONG(clearArea -> window, buffer + 4, bigEndian); + + PutUINT(clearArea -> x, buffer + 8, bigEndian); + PutUINT(clearArea -> y, buffer + 10, bigEndian); + PutUINT(clearArea -> width, buffer + 12, bigEndian); + PutUINT(clearArea -> height, buffer + 14, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void ClearAreaStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + ClearAreaMessage *clearArea = (ClearAreaMessage *) message; + + *logofs << name() << ": Identity exposures " << clearArea -> (unsigned int) exposures + << ", window " << clearArea -> window << ", x " << clearArea -> x + << ", y " << clearArea -> y << ", width " << clearArea -> width + << ", height " << clearArea -> height << ", size " << clearArea -> size_ + << ".\n"; + + #endif +} + +void ClearAreaStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 1, 1); + md5_append(md5_state_, buffer + 4, 4); + md5_append(md5_state_, buffer + 8, 2); + md5_append(md5_state_, buffer + 10, 2); + md5_append(md5_state_, buffer + 12, 2); + md5_append(md5_state_, buffer + 14, 2); +} diff --git a/nxcomp/ClearArea.h b/nxcomp/ClearArea.h new file mode 100644 index 000000000..039eea44e --- /dev/null +++ b/nxcomp/ClearArea.h @@ -0,0 +1,174 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ClearArea_H +#define ClearArea_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define CLEARAREA_ENABLE_CACHE 1 +#define CLEARAREA_ENABLE_DATA 0 +#define CLEARAREA_ENABLE_SPLIT 0 +#define CLEARAREA_ENABLE_COMPRESS 0 + +#define CLEARAREA_DATA_LIMIT 0 +#define CLEARAREA_DATA_OFFSET 16 + +#define CLEARAREA_CACHE_SLOTS 3000 +#define CLEARAREA_CACHE_THRESHOLD 5 +#define CLEARAREA_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class ClearAreaMessage : public Message +{ + friend class ClearAreaStore; + + public: + + ClearAreaMessage() + { + } + + ~ClearAreaMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char exposures; + unsigned int window; + unsigned short x; + unsigned short y; + unsigned short width; + unsigned short height; +}; + +class ClearAreaStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + ClearAreaStore() : MessageStore() + { + enableCache = CLEARAREA_ENABLE_CACHE; + enableData = CLEARAREA_ENABLE_DATA; + enableSplit = CLEARAREA_ENABLE_SPLIT; + enableCompress = CLEARAREA_ENABLE_COMPRESS; + + dataLimit = CLEARAREA_DATA_LIMIT; + dataOffset = CLEARAREA_DATA_OFFSET; + + cacheSlots = CLEARAREA_CACHE_SLOTS; + cacheThreshold = CLEARAREA_CACHE_THRESHOLD; + cacheLowerThreshold = CLEARAREA_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~ClearAreaStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "ClearArea"; + } + + virtual unsigned char opcode() const + { + return X_ClearArea; + } + + virtual unsigned int storage() const + { + return sizeof(ClearAreaMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new ClearAreaMessage(); + } + + virtual Message *create(const Message &message) const + { + return new ClearAreaMessage((const ClearAreaMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (ClearAreaMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* ClearArea_H */ diff --git a/nxcomp/ClientCache.cpp b/nxcomp/ClientCache.cpp new file mode 100644 index 000000000..2a82009a3 --- /dev/null +++ b/nxcomp/ClientCache.cpp @@ -0,0 +1,389 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ClientCache.h" + +ClientCache::ClientCache() : + + freeGCCache(16), freeDrawableCache(16), freeWindowCache(16), + + cursorCache(16), colormapCache(16), visualCache(16), lastFont(0), + + changePropertyPropertyCache(16), changePropertyTypeCache(16), + changePropertyData32Cache(16), + changePropertyTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), + + configureWindowBitmaskCache(4), + + convertSelectionRequestorCache(16), + convertSelectionLastTimestamp(0), + + copyPlaneBitPlaneCache(8), + + createGCBitmaskCache(8), + + createPixmapIdCache(16), createPixmapLastId(0), + createPixmapXCache(8), createPixmapYCache(8), + + createWindowBitmaskCache(8), + + fillPolyNumPointsCache(8), fillPolyIndex(0), + + getSelectionOwnerSelectionCache(8), + + grabButtonEventMaskCache(8), grabButtonConfineCache(8), + grabButtonModifierCache(8), + + grabKeyboardLastTimestamp(0), + + imageTextLengthCache(8), + imageTextLastX(0), imageTextLastY(0), + imageTextCacheX(8), imageTextCacheY(8), + imageTextTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), + + internAtomTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), + + openFontTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), + + polySegmentCacheX(8), polySegmentCacheY(8), polySegmentCacheIndex(0), + + polyTextLastX(0), polyTextLastY(0), polyTextCacheX(8), + polyTextCacheY(8), polyTextFontCache(8), + polyTextTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), + + putImageWidthCache(8), putImageHeightCache(8), putImageLastX(0), + putImageLastY(0), putImageXCache(8), putImageYCache(8), + + getImagePlaneMaskCache(8), + + queryColorsLastPixel(0), + + setClipRectanglesXCache(8), setClipRectanglesYCache(8), + + setDashesLengthCache(8), setDashesOffsetCache(8), + + setSelectionOwnerCache(8), setSelectionOwnerTimestampCache(8), + + translateCoordsSrcCache(8), translateCoordsDstCache(8), + translateCoordsXCache(8), translateCoordsYCache(8), + + sendEventMaskCache(16), sendEventLastSequence(0), + sendEventIntDataCache(16), + + putPackedImageSrcLengthCache(16), putPackedImageDstLengthCache(16), + + // + // RenderExtension requests. + // + + renderFreePictureCache(16), + + renderGlyphSetCache(16), + renderFreeGlyphSetCache(16), + + renderIdCache(8), + + renderLengthCache(16), renderFormatCache(16), + renderValueMaskCache(8), renderNumGlyphsCache(8), + + renderXCache(16), renderYCache(16), + renderLastX(0), renderLastY(0), + + renderWidthCache(16), renderHeightCache(16), + + renderLastId(0), + + renderTextCompressor(textCache, CLIENT_TEXT_CACHE_SIZE), + + renderGlyphXCache(16), renderGlyphYCache(16), + renderGlyphX(0), renderGlyphY(0), + + renderLastCompositeGlyphsData(0), + + setCacheParametersCache(8), + + lastIdCache(16), lastId(0) + +{ + unsigned int i; + + for (i = 0; i < 3; i++) + { + allocColorRGBCache[i] = new IntCache(8); + + convertSelectionAtomCache[i] = new IntCache(8); + } + + for (i = 0; i < 4; i++) + { + clearAreaGeomCache[i] = new IntCache(8); + } + + for (i = 0; i < 7; i++) + { + configureWindowAttrCache[i] = new IntCache(8); + } + + for (i = 0; i < 6; i++) + { + copyAreaGeomCache[i] = new IntCache(8); + copyPlaneGeomCache[i] = new IntCache(8); + } + + for (i = 0; i < 23; i++) + { + if (CREATEGC_FIELD_WIDTH[i] > 16) + { + createGCAttrCache[i] = new IntCache(16); + } + else + { + createGCAttrCache[i] = new IntCache(CREATEGC_FIELD_WIDTH[i]); + } + } + + for (i = 0; i < 6; i++) + { + createWindowGeomCache[i] = new IntCache(8); + } + + for (i = 0; i < 15; i++) + { + createWindowAttrCache[i] = new IntCache(8); + } + + for (i = 0; i < 10; i++) + { + fillPolyXRelCache[i] = new IntCache(8); + fillPolyXAbsCache[i] = new IntCache(8); + fillPolyYRelCache[i] = new IntCache(8); + fillPolyYAbsCache[i] = new IntCache(8); + } + + for (i = 0; i < 8; i++) + { + fillPolyRecentX[i] = 0; + fillPolyRecentY[i] = 0; + } + + for (i = 0; i < 4; i++) + { + polyFillRectangleCacheX[i] = new IntCache(8); + polyFillRectangleCacheY[i] = new IntCache(8); + polyFillRectangleCacheWidth[i] = new IntCache(8); + polyFillRectangleCacheHeight[i] = new IntCache(8); + } + + for (i = 0; i < 2; i++) + { + polyLineCacheX[i] = new IntCache(8); + polyLineCacheY[i] = new IntCache(8); + } + + for (i = 0; i < 2; i++) + { + polyPointCacheX[i] = new IntCache(8); + polyPointCacheY[i] = new IntCache(8); + } + + for (i = 0; i < 4; i++) + { + polyRectangleGeomCache[i] = new IntCache(8); + } + + for (i = 0; i < 2; i++) + { + polySegmentLastX[i] = 0; + polySegmentLastY[i] = 0; + } + + for (i = 0; i < 4; i++) + { + setClipRectanglesGeomCache[i] = new IntCache(8); + } + + for (i = 0; i < 2; i++) + { + polyFillArcCacheX[i] = new IntCache(8); + polyFillArcCacheY[i] = new IntCache(8); + polyFillArcCacheWidth[i] = new IntCache(8); + polyFillArcCacheHeight[i] = new IntCache(8); + polyFillArcCacheAngle1[i] = new IntCache(8); + polyFillArcCacheAngle2[i] = new IntCache(8); + } + + for (i = 0; i < 2; i++) + { + polyArcCacheX[i] = new IntCache(8); + polyArcCacheY[i] = new IntCache(8); + polyArcCacheWidth[i] = new IntCache(8); + polyArcCacheHeight[i] = new IntCache(8); + polyArcCacheAngle1[i] = new IntCache(8); + polyArcCacheAngle2[i] = new IntCache(8); + } + + for (i = 0; i < 8; i++) + { + shapeDataCache[i] = new IntCache(8); + } + + for (i = 0; i < 8; i++) + { + genericRequestDataCache[i] = new IntCache(8); + } + + for (i = 0; i < 16; i++) + { + renderDataCache[i] = new IntCache(16); + } + + for (i = 0; i < 16; i++) + { + renderCompositeGlyphsDataCache[i] = new IntCache(16); + } + + for (i = 0; i < 3; i++) + { + renderCompositeDataCache[i] = new IntCache(16); + } +} + + +ClientCache::~ClientCache() +{ + unsigned int i; + + for (i = 0; i < 3; i++) + { + delete allocColorRGBCache[i]; + delete convertSelectionAtomCache[i]; + } + + for (i = 0; i < 4; i++) + { + delete clearAreaGeomCache[i]; + } + + for (i = 0; i < 7; i++) + { + delete configureWindowAttrCache[i]; + } + + for (i = 0; i < 6; i++) + { + delete copyAreaGeomCache[i]; + delete copyPlaneGeomCache[i]; + } + + for (i = 0; i < 23; i++) + { + delete createGCAttrCache[i]; + } + + for (i = 0; i < 6; i++) + { + delete createWindowGeomCache[i]; + } + + for (i = 0; i < 15; i++) + { + delete createWindowAttrCache[i]; + } + + for (i = 0; i < 10; i++) + { + delete fillPolyXRelCache[i]; + delete fillPolyXAbsCache[i]; + delete fillPolyYRelCache[i]; + delete fillPolyYAbsCache[i]; + } + + for (i = 0; i < 4; i++) + { + delete polyFillRectangleCacheX[i]; + delete polyFillRectangleCacheY[i]; + delete polyFillRectangleCacheWidth[i]; + delete polyFillRectangleCacheHeight[i]; + } + + for (i = 0; i < 2; i++) + { + delete polyLineCacheX[i]; + delete polyLineCacheY[i]; + } + + for (i = 0; i < 2; i++) + { + delete polyPointCacheX[i]; + delete polyPointCacheY[i]; + } + + for (i = 0; i < 4; i++) + { + delete polyRectangleGeomCache[i]; + } + + for (i = 0; i < 4; i++) + { + delete setClipRectanglesGeomCache[i]; + } + + for (i = 0; i < 2; i++) + { + delete polyFillArcCacheX[i]; + delete polyFillArcCacheY[i]; + delete polyFillArcCacheWidth[i]; + delete polyFillArcCacheHeight[i]; + delete polyFillArcCacheAngle1[i]; + delete polyFillArcCacheAngle2[i]; + } + + for (i = 0; i < 2; i++) + { + delete polyArcCacheX[i]; + delete polyArcCacheY[i]; + delete polyArcCacheWidth[i]; + delete polyArcCacheHeight[i]; + delete polyArcCacheAngle1[i]; + delete polyArcCacheAngle2[i]; + } + + for (i = 0; i < 8; i++) + { + delete shapeDataCache[i]; + } + + for (i = 0; i < 8; i++) + { + delete genericRequestDataCache[i]; + } + + for (i = 0; i < 16; i++) + { + delete renderDataCache[i]; + } + + for (i = 0; i < 16; i++) + { + delete renderCompositeGlyphsDataCache[i]; + } + + for (i = 0; i < 3; i++) + { + delete renderCompositeDataCache[i]; + } +} diff --git a/nxcomp/ClientCache.h b/nxcomp/ClientCache.h new file mode 100644 index 000000000..6702e5b66 --- /dev/null +++ b/nxcomp/ClientCache.h @@ -0,0 +1,429 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ClientCache_H +#define ClientCache_H + +#include "Misc.h" + +#include "IntCache.h" +#include "CharCache.h" +#include "OpcodeCache.h" +#include "XidCache.h" +#include "FreeCache.h" + +#include "TextCompressor.h" + +#include "ChannelCache.h" + +class ClientCache : public ChannelCache +{ + public: + + ClientCache(); + + ~ClientCache(); + + // + // Opcode prediction caches. + // + + OpcodeCache opcodeCache; + + // + // GC and drawables caches. + // + + XidCache gcCache; + FreeCache freeGCCache; + + XidCache drawableCache; + FreeCache freeDrawableCache; + + XidCache windowCache; + FreeCache freeWindowCache; + + // + // General-purpose caches. + // + + CharCache textCache[CLIENT_TEXT_CACHE_SIZE]; + IntCache cursorCache; + IntCache colormapCache; + IntCache visualCache; + CharCache depthCache; + CharCache resourceCache; + CharCache methodCache; + + unsigned int lastFont; + + // + // AllocColor request. + // + + IntCache *allocColorRGBCache[3]; + + // + // ChangeProperty request. + // + + CharCache changePropertyFormatCache; + IntCache changePropertyPropertyCache; + IntCache changePropertyTypeCache; + IntCache changePropertyData32Cache; + TextCompressor changePropertyTextCompressor; + + // + // ClearArea request. + // + + IntCache *clearAreaGeomCache[4]; + + // + // ConfigureWindow request. + // + + IntCache configureWindowBitmaskCache; + IntCache *configureWindowAttrCache[7]; + + // + // ConvertSelection request. + // + + IntCache convertSelectionRequestorCache; + IntCache* convertSelectionAtomCache[3]; + unsigned int convertSelectionLastTimestamp; + + // + // CopyArea request. + // + + IntCache *copyAreaGeomCache[6]; + + // + // CopyPlane request. + // + + IntCache *copyPlaneGeomCache[6]; + IntCache copyPlaneBitPlaneCache; + + // + // CreateGC request. + // + + IntCache createGCBitmaskCache; + IntCache *createGCAttrCache[23]; + + // + // CreatePixmap request. + // + + IntCache createPixmapIdCache; + unsigned int createPixmapLastId; + IntCache createPixmapXCache; + IntCache createPixmapYCache; + + // + // CreateWindow request. + // + + IntCache *createWindowGeomCache[6]; + IntCache createWindowBitmaskCache; + IntCache *createWindowAttrCache[15]; + + // + // FillPoly request. + // + + IntCache fillPolyNumPointsCache; + IntCache *fillPolyXRelCache[10]; + IntCache *fillPolyXAbsCache[10]; + IntCache *fillPolyYRelCache[10]; + IntCache *fillPolyYAbsCache[10]; + unsigned int fillPolyRecentX[8]; + unsigned int fillPolyRecentY[8]; + unsigned int fillPolyIndex; + + // + // GetSelectionOwner request. + // + + IntCache getSelectionOwnerSelectionCache; + + // + // GrabButton request (also used for GrabPointer). + // + + IntCache grabButtonEventMaskCache; + IntCache grabButtonConfineCache; + CharCache grabButtonButtonCache; + IntCache grabButtonModifierCache; + + // + // GrabKeyboard request. + // + + unsigned int grabKeyboardLastTimestamp; + + // + // ImageText8/16 request. + // + + IntCache imageTextLengthCache; + unsigned int imageTextLastX; + unsigned int imageTextLastY; + IntCache imageTextCacheX; + IntCache imageTextCacheY; + TextCompressor imageTextTextCompressor; + + // + // InternAtom request. + // + + TextCompressor internAtomTextCompressor; + + // + // OpenFont request. + // + + TextCompressor openFontTextCompressor; + + // + // PolyFillRectangle request. + // + + IntCache *polyFillRectangleCacheX[4]; + IntCache *polyFillRectangleCacheY[4]; + IntCache *polyFillRectangleCacheWidth[4]; + IntCache *polyFillRectangleCacheHeight[4]; + + // + // PolyLine request. + // + + IntCache *polyLineCacheX[2]; + IntCache *polyLineCacheY[2]; + + // + // PolyPoint request. + // + + IntCache *polyPointCacheX[2]; + IntCache *polyPointCacheY[2]; + + // + // PolyRectangle request. + // + + IntCache *polyRectangleGeomCache[4]; + + // + // PolySegment request. + // + + IntCache polySegmentCacheX; + IntCache polySegmentCacheY; + unsigned int polySegmentLastX[2]; + unsigned int polySegmentLastY[2]; + unsigned int polySegmentCacheIndex; + + // + // PolyText8/16 request. + // + + unsigned int polyTextLastX; + unsigned int polyTextLastY; + IntCache polyTextCacheX; + IntCache polyTextCacheY; + IntCache polyTextFontCache; + CharCache polyTextDeltaCache; + TextCompressor polyTextTextCompressor; + + // + // PutImage request. + // + + IntCache putImageWidthCache; + IntCache putImageHeightCache; + unsigned int putImageLastX; + unsigned int putImageLastY; + IntCache putImageXCache; + IntCache putImageYCache; + CharCache putImageLeftPadCache; + + // + // GetImage request. + // + + IntCache getImagePlaneMaskCache; + + // + // QueryColors request. + // + + unsigned int queryColorsLastPixel; + + // + // SetClipRectangles request. + // + + IntCache setClipRectanglesXCache; + IntCache setClipRectanglesYCache; + IntCache *setClipRectanglesGeomCache[4]; + + // + // SetDashes request. + // + + IntCache setDashesLengthCache; + IntCache setDashesOffsetCache; + CharCache setDashesDashCache_[2]; + + // + // SetSelectionOwner request. + // + + IntCache setSelectionOwnerCache; + IntCache setSelectionOwnerTimestampCache; + + // + // TranslateCoords request. + // + + IntCache translateCoordsSrcCache; + IntCache translateCoordsDstCache; + IntCache translateCoordsXCache; + IntCache translateCoordsYCache; + + // + // SendEvent request. + // + + IntCache sendEventMaskCache; + CharCache sendEventCodeCache; + CharCache sendEventByteDataCache; + unsigned int sendEventLastSequence; + IntCache sendEventIntDataCache; + CharCache sendEventEventCache; + + // + // PolyFillArc request. + // + + IntCache *polyFillArcCacheX[2]; + IntCache *polyFillArcCacheY[2]; + IntCache *polyFillArcCacheWidth[2]; + IntCache *polyFillArcCacheHeight[2]; + IntCache *polyFillArcCacheAngle1[2]; + IntCache *polyFillArcCacheAngle2[2]; + + // + // PolyArc request. + // + + IntCache *polyArcCacheX[2]; + IntCache *polyArcCacheY[2]; + IntCache *polyArcCacheWidth[2]; + IntCache *polyArcCacheHeight[2]; + IntCache *polyArcCacheAngle1[2]; + IntCache *polyArcCacheAngle2[2]; + + // + // PutPackedImage request. + // + + IntCache putPackedImageSrcLengthCache; + IntCache putPackedImageDstLengthCache; + + // + // Shape extension requests. + // + + CharCache shapeOpcodeCache; + IntCache *shapeDataCache[8]; + + // + // Generic requests. + // + + CharCache genericRequestOpcodeCache; + IntCache *genericRequestDataCache[8]; + + // + // Render extension requests. + // + + OpcodeCache renderOpcodeCache; + + CharCache renderOpCache; + + XidCache renderSrcPictureCache; + XidCache renderMaskPictureCache; + XidCache renderDstPictureCache; + FreeCache renderFreePictureCache; + + IntCache renderGlyphSetCache; + FreeCache renderFreeGlyphSetCache; + + IntCache renderIdCache; + IntCache renderLengthCache; + IntCache renderFormatCache; + IntCache renderValueMaskCache; + IntCache renderNumGlyphsCache; + + IntCache renderXCache; + IntCache renderYCache; + + unsigned int renderLastX; + unsigned int renderLastY; + + IntCache renderWidthCache; + IntCache renderHeightCache; + + unsigned int renderLastId; + + IntCache *renderDataCache[16]; + + TextCompressor renderTextCompressor; + + IntCache renderGlyphXCache; + IntCache renderGlyphYCache; + + unsigned int renderGlyphX; + unsigned int renderGlyphY; + + IntCache *renderCompositeGlyphsDataCache[16]; + unsigned int renderLastCompositeGlyphsData; + + IntCache *renderCompositeDataCache[3]; + + // + // SetCacheParameters request. + // + + IntCache setCacheParametersCache; + + // + // Encode new XID values based + // on the last value encoded. + // + + IntCache lastIdCache; + unsigned int lastId; +}; + +#endif /* ClientCache_H */ diff --git a/nxcomp/ClientChannel.cpp b/nxcomp/ClientChannel.cpp new file mode 100644 index 000000000..31d6d125a --- /dev/null +++ b/nxcomp/ClientChannel.cpp @@ -0,0 +1,8244 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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; + + // + // Disable image streaming if the remote + // doesn't support our proxy version. + // + + handleSplitEnable(); + + // + // Number of outstanding tainted replies. + // + + taintCounter_ = 0; + + #ifdef MEMORY + + *logofs << "ClientChannel: Created 1 ClientCache and 1 ServerCache. " + << "You have 30 seconds to check the allocated size.\n" + << logofs_flush; + + sleep(30); + + ClientCache *clientCacheTestArray[100]; + ServerCache *serverCacheTestArray[100]; + + for (int i = 0; i < 100; i++) + { + clientCacheTestArray[i] = new ClientCache(); + } + + *logofs << "ClientChannel: Created further 100 ClientCache. " + << "You have 30 seconds to check the allocated size.\n" + << logofs_flush; + + sleep(30); + + for (int i = 0; i < 100; i++) + { + serverCacheTestArray[i] = new ServerCache(); + } + + *logofs << "ClientChannel: Created further 100 ServerCache. " + << "You have 30 seconds to check the allocated size.\n" + << logofs_flush; + + sleep(30); + + for (int i = 0; i < 100; i++) + { + delete clientCacheTestArray[i]; + delete serverCacheTestArray[i]; + } + + *logofs << "ClientChannel: Deleted 100 ClientCache and 100 ServerCache. " + << "You have 30 seconds to check the allocated size.\n" + << logofs_flush; + + sleep(30); + + #endif + + #ifdef REFERENCES + *logofs << "ClientChannel: Created new object at " + << this << " for FD#" << fd_ << " out of " + << ++references_ << " allocated channels.\n" + << logofs_flush; + #endif +} + +ClientChannel::~ClientChannel() +{ + #ifdef REFERENCES + *logofs << "ClientChannel: Deleted object at " + << this << " for FD#" << fd_ << " out of " + << --references_ << " allocated channels.\n" + << logofs_flush; + #endif +} + +// +// Beginning of handleRead(). +// + +int ClientChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, + unsigned int length) +{ + #ifdef TEST + *logofs << "handleRead: Called for FD#" << fd_ + << " with " << encodeBuffer.getLength() + << " bytes already encoded.\n" + << logofs_flush; + #endif + + // + // Pointer to located message and + // its size in bytes. + // + + const unsigned char *inputMessage; + unsigned int inputLength; + + // + // Set when message is found in + // cache. + // + + int hit; + + // + // Check if we can borrow the buffer + // from the caller. + // + + if (message != NULL && length != 0) + { + readBuffer_.readMessage(message, length); + } + else + { + // + // Get the data from the transport. + // + + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: Trying to read from FD#" + << fd_ << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + int result = readBuffer_.readMessage(); + + #ifdef DEBUG + *logofs << "handleRead: Read result on FD#" << fd_ + << " is " << result << ".\n" + << logofs_flush; + #endif + + if (result < 0) + { + // + // Let the proxy close the channel. + // + + return -1; + } + else if (result == 0) + { + #if defined(TEST) || defined(INFO) + + *logofs << "handleRead: PANIC! No data read from FD#" + << fd_ << " while encoding messages.\n" + << logofs_flush; + + HandleCleanup(); + + #endif + + return 0; + } + } + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "handleRead: Encoding messages for FD#" << fd_ + << " with " << readBuffer_.getLength() << " bytes " + << "in the buffer.\n" << logofs_flush; + #endif + + // + // Extract any complete message which + // is available in the buffer. + // + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL) + { + hit = 0; + + if (firstRequest_) + { + // + // Need to add the length of the first + // request as it was not present in + // previous versions. + // + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeValue(inputLength, 8); + } + + for (unsigned int i = 0; i < inputLength; i++) + { + encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8); + } + + firstRequest_ = 0; + + #if defined(TEST) || defined(OPCODES) + + int bits = encodeBuffer.diffBits(); + + *logofs << "handleRead: Handled first request. " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + #endif + + priority_++; + + // + // Due to the way the loop was implemented + // we can't encode multiple messages if we + // are encoding the first request. + // + + if (control -> isProtoStep7() == 0) + { + if (proxy -> handleAsyncInit() < 0) + { + return -1; + } + } + } + else + { + // + // First of all we get the opcode. + // + + unsigned char inputOpcode = *inputMessage; + + #if defined(TEST) || defined(INFO) + + // + // This is used to test the synchronous + // flush in the parent proxy. + // + + lastRequest_ = inputOpcode; + + #endif + + // + // Check if the request is supported by the + // remote. If not, only handle it locally and + // taint the opcode as a X_NoOperation. Also + // try to short-circuit some replies at this + // side. XSync requests, for example, weight + // for half of the total round-trips. + // + + if (handleTaintRequest(inputOpcode, inputMessage, + inputLength) < 0) + { + return -1; + } + + encodeBuffer.encodeOpcodeValue(inputOpcode, clientCache_ -> opcodeCache); + + // + // Update the current sequence. + // + + clientSequence_++; + clientSequence_ &= 0xffff; + + #ifdef DEBUG + *logofs << "handleRead: Last client sequence number for FD#" + << fd_ << " is " << clientSequence_ << ".\n" + << logofs_flush; + #endif + + // + // If differential compression is disabled + // then use the most simple encoding. + // + + if (control -> LocalDeltaCompression == 0) + { + int result = handleFastReadRequest(encodeBuffer, inputOpcode, + inputMessage, inputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + // + // Go to the message's specific encoding. + // + + switch (inputOpcode) + { + case X_AllocColor: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, + clientCache_ -> colormapCache); + const unsigned char *nextSrc = inputMessage + 8; + unsigned int colorData[3]; + for (unsigned int i = 0; i < 3; i++) + { + unsigned int value = GetUINT(nextSrc, bigEndian_); + encodeBuffer.encodeCachedValue(value, 16, + *(clientCache_ -> allocColorRGBCache[i]), 4); + colorData[i] = value; + nextSrc += 2; + } + + sequenceQueue_.push(clientSequence_, inputOpcode, + colorData[0], colorData[1], colorData[2]); + + priority_++; + } + break; + case X_ReparentWindow: + { + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), + clientCache_ -> windowCache); + encodeBuffer.encodeValue(GetUINT(inputMessage + 12, bigEndian_), 16, 11); + encodeBuffer.encodeValue(GetUINT(inputMessage + 14, bigEndian_), 16, 11); + } + break; + case X_ChangeProperty: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ChangeProperty); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + unsigned char format = inputMessage[16]; + encodeBuffer.encodeCachedValue(format, 8, + clientCache_ -> changePropertyFormatCache); + unsigned int dataLength = GetULONG(inputMessage + 20, bigEndian_); + encodeBuffer.encodeValue(dataLength, 32, 6); + encodeBuffer.encodeValue(inputMessage[1], 2); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, + clientCache_ -> changePropertyPropertyCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29, + clientCache_ -> changePropertyTypeCache, 9); + const unsigned char *nextSrc = inputMessage + 24; + if (format == 8) + { + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, dataLength); + } + else + { + clientCache_ -> changePropertyTextCompressor.reset(); + for (unsigned int i = 0; i < dataLength; i++) + clientCache_ -> changePropertyTextCompressor. + encodeChar(*nextSrc++, encodeBuffer); + } + } + else if (format == 32) + { + for (unsigned int i = 0; i < dataLength; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32, + clientCache_ -> changePropertyData32Cache); + nextSrc += 4; + } + } + else + { + for (unsigned int i = 0; i < dataLength; i++) + { + encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16); + nextSrc += 2; + } + } + } + break; + case X_SendEvent: + { + // + // TODO: This can be improved. In the worst + // cases, it appears to provide a poor 1.6:1 + // ratio. + // + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_SendEvent); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + unsigned int window = GetULONG(inputMessage + 4, bigEndian_); + + if (window == 0 || window == 1) + { + encodeBuffer.encodeBoolValue(1); + encodeBuffer.encodeBoolValue(window); + } + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeXidValue(window, clientCache_ -> windowCache); + } + + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 32, + clientCache_ -> sendEventMaskCache, 9); + encodeBuffer.encodeCachedValue(*(inputMessage + 12), 8, + clientCache_ -> sendEventCodeCache); + encodeBuffer.encodeCachedValue(*(inputMessage + 13), 8, + clientCache_ -> sendEventByteDataCache); + + unsigned int newSeq = GetUINT(inputMessage + 14, bigEndian_); + unsigned int diffSeq = newSeq - clientCache_ -> sendEventLastSequence; + clientCache_ -> sendEventLastSequence = newSeq; + encodeBuffer.encodeValue(diffSeq, 16, 4); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 32, + clientCache_ -> sendEventIntDataCache); + + for (unsigned int i = 20; i < 44; i++) + { + encodeBuffer.encodeCachedValue((unsigned int) inputMessage[i], 8, + clientCache_ -> sendEventEventCache); + } + } + break; + case X_ChangeWindowAttributes: + { + encodeBuffer.encodeValue((inputLength - 12) >> 2, 4); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + unsigned int bitmask = GetULONG(inputMessage + 8, bigEndian_); + encodeBuffer.encodeCachedValue(bitmask, 15, + clientCache_ -> createWindowBitmaskCache); + const unsigned char *nextSrc = inputMessage + 12; + unsigned int mask = 0x1; + for (unsigned int j = 0; j < 15; j++) + { + if (bitmask & mask) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32, + *clientCache_ -> createWindowAttrCache[j]); + nextSrc += 4; + } + mask <<= 1; + } + } + break; + case X_ClearArea: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_ClearArea target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_ClearArea target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_ClearArea target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ClearArea); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + const unsigned char *nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 4; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *clientCache_ -> clearAreaGeomCache[i], 8); + nextSrc += 2; + } + } + break; + case X_CloseFont: + { + unsigned int font = GetULONG(inputMessage + 4, bigEndian_); + encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5); + clientCache_ -> lastFont = font; + } + break; + case X_ConfigureWindow: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ConfigureWindow); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + unsigned int bitmask = GetUINT(inputMessage + 8, bigEndian_); + encodeBuffer.encodeCachedValue(bitmask, 7, + clientCache_ -> configureWindowBitmaskCache); + unsigned int mask = 0x1; + const unsigned char *nextSrc = inputMessage + 12; + for (unsigned int i = 0; i < 7; i++) + { + if (bitmask & mask) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), + CONFIGUREWINDOW_FIELD_WIDTH[i], + *clientCache_ -> configureWindowAttrCache[i], 8); + nextSrc += 4; + } + mask <<= 1; + } + } + break; + case X_ConvertSelection: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, + clientCache_ -> convertSelectionRequestorCache, 9); + const unsigned char* nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, + *(clientCache_ -> convertSelectionAtomCache[i]), 9); + nextSrc += 4; + } + unsigned int timestamp = GetULONG(nextSrc, bigEndian_); + encodeBuffer.encodeValue(timestamp - + clientCache_ -> convertSelectionLastTimestamp, 32, 4); + clientCache_ -> convertSelectionLastTimestamp = timestamp; + } + break; + case X_CopyArea: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_CopyArea source id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_CopyArea source id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_CopyArea source id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + t_id = GetULONG(inputMessage + 8, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_CopyArea target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_CopyArea target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_CopyArea target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CopyArea); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 12, + bigEndian_), clientCache_ -> gcCache); + const unsigned char *nextSrc = inputMessage + 16; + for (unsigned int i = 0; i < 6; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *clientCache_ -> copyAreaGeomCache[i], 8); + nextSrc += 2; + } + } + break; + case X_CopyGC: + { + #ifdef TARGETS + + unsigned int s_g_id = GetULONG(inputMessage + 4, bigEndian_); + unsigned int d_g_id = GetULONG(inputMessage + 8, bigEndian_); + + *logofs << "handleRead: X_CopyGC source gcontext id is " << s_g_id + << " destination gcontext id is " << d_g_id << ".\n" + << logofs_flush; + + #endif + + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> gcCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, + bigEndian_), 23, clientCache_ -> createGCBitmaskCache); + } + break; + case X_CopyPlane: + { + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 12, + bigEndian_), clientCache_ -> gcCache); + const unsigned char *nextSrc = inputMessage + 16; + for (unsigned int i = 0; i < 6; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *clientCache_ -> copyPlaneGeomCache[i], 8); + nextSrc += 2; + } + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 28, bigEndian_), 32, + clientCache_ -> copyPlaneBitPlaneCache, 10); + } + break; + case X_CreateGC: + { + #ifdef TARGETS + + unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_); + unsigned int t_id = GetULONG(inputMessage + 8, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_CreateGC id " << g_id + << " target id is pixmap " << t_id + << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_CreateGC id " << g_id + << " target id is window " << t_id + << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_CreateGC id " << g_id + << " target id is unrecognized.\n" + << logofs_flush; + } + + gcontexts.insert(T_gcontexts::value_type(g_id, t_id)); + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CreateGC); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> lastId, clientCache_ -> lastIdCache, + clientCache_ -> gcCache, + clientCache_ -> freeGCCache); + } + else + { + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> gcCache); + } + + const unsigned char *nextSrc = inputMessage + 8; + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> drawableCache); + nextSrc += 4; + unsigned int bitmask = GetULONG(nextSrc, bigEndian_); + nextSrc += 4; + encodeBuffer.encodeCachedValue(bitmask, 23, + clientCache_ -> createGCBitmaskCache); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 23; i++) + { + if (bitmask & mask) + { + unsigned int value = GetULONG(nextSrc, bigEndian_); + nextSrc += 4; + unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i]; + if (fieldWidth <= 4) + { + encodeBuffer.encodeValue(value, fieldWidth); + } + else + { + encodeBuffer.encodeCachedValue(value, fieldWidth, + *clientCache_ -> createGCAttrCache[i]); + } + } + mask <<= 1; + } + } + break; + case X_ChangeGC: + { + #ifdef TARGETS + + unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_); + + T_gcontexts::iterator i = gcontexts.find(g_id); + + if (i != gcontexts.end()) + { + unsigned int t_id = i -> second; + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_ChangeGC gcontext id is " << g_id + << " target id is pixmap " << t_id << ".\n" + << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_ChangeGC gcontext id is " << g_id + << " target id is window " << t_id << ".\n" + << logofs_flush; + } + else + { + *logofs << "handleRead: X_ChangeGC gcontext is " << g_id + << " target id is unrecognized.\n" + << logofs_flush; + } + } + else + { + *logofs << "handleRead: X_ChangeGC gcontext id " << g_id + << " is unrecognized.\n" << logofs_flush; + } + + gcontexts.erase(g_id); + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ChangeGC); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> gcCache); + const unsigned char *nextSrc = inputMessage + 8; + unsigned int bitmask = GetULONG(nextSrc, bigEndian_); + nextSrc += 4; + encodeBuffer.encodeCachedValue(bitmask, 23, + clientCache_ -> createGCBitmaskCache); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 23; i++) + { + if (bitmask & mask) + { + unsigned int value = GetULONG(nextSrc, bigEndian_); + nextSrc += 4; + unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i]; + if (fieldWidth <= 4) + { + encodeBuffer.encodeValue(value, fieldWidth); + } + else + { + encodeBuffer.encodeCachedValue(value, fieldWidth, + *clientCache_ -> createGCAttrCache[i]); + } + } + mask <<= 1; + } + } + break; + case X_CreatePixmap: + { + #ifdef TARGETS + + *logofs << "handleRead: X_CreatePixmap depth " << (unsigned) inputMessage[1] + << ", pixmap id " << GetULONG(inputMessage + 4, bigEndian_) + << ", drawable " << GetULONG(inputMessage + 8, bigEndian_) + << ", width " << GetUINT(inputMessage + 12, bigEndian_) + << ", height " << GetUINT(inputMessage + 14, bigEndian_) + << ", size " << GetUINT(inputMessage + 2, bigEndian_) << 2 + << ".\n" << logofs_flush; + + unsigned int p_id = GetULONG(inputMessage + 4, bigEndian_); + unsigned short p_sx = GetUINT(inputMessage + 12, bigEndian_); + unsigned short p_sy = GetUINT(inputMessage + 14, bigEndian_); + + *logofs << "handleRead: X_CreatePixmap id is " << p_id + << " width is " << p_sx << " height is " << p_sy + << ".\n" << logofs_flush; + + if (p_sx * p_sy <= 64 * 64) + { + *logofs << "handleRead: X_CreatePixmap id " << p_id << " of size " + << p_sx << "x" << p_sy << "=" << p_sx * p_sy + << " will be painted at client side.\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_CreatePixmap id " << p_id << " of size " + << p_sx << "x" << p_sy << "=" << p_sx * p_sy + << " will be painted at server side.\n" << logofs_flush; + } + + pixmaps.insert(p_id); + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CreatePixmap); + + hit = handleEncode(encodeBuffer, clientCache_, messageStore, + inputOpcode, inputMessage, inputLength); + } + break; + case X_CreateWindow: + { + #ifdef TARGETS + + unsigned int w_id = GetULONG(inputMessage + 4, bigEndian_); + + *logofs << "handleRead: X_CreateWindow id is " << w_id + << ".\n" << logofs_flush; + + windows.insert(w_id); + + #endif + + unsigned bitmask = GetULONG(inputMessage + 28, bigEndian_); + encodeBuffer.encodeCachedValue((unsigned int) inputMessage[1], 8, + clientCache_ -> depthCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), + clientCache_ -> windowCache); + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> lastId, clientCache_ -> lastIdCache, + clientCache_ -> windowCache, + clientCache_ -> freeWindowCache); + } + else + { + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + } + const unsigned char *nextSrc = inputMessage + 12; + for (unsigned int i = 0; i < 6; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *clientCache_ -> createWindowGeomCache[i], 8); + nextSrc += 2; + } + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 24, + bigEndian_), 29, clientCache_ -> visualCache); + encodeBuffer.encodeCachedValue(bitmask, 15, + clientCache_ -> createWindowBitmaskCache); + nextSrc = inputMessage + 32; + unsigned int mask = 0x1; + for (unsigned int j = 0; j < 15; j++) + { + if (bitmask & mask) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32, + *clientCache_ -> createWindowAttrCache[j]); + nextSrc += 4; + } + mask <<= 1; + } + } + break; + case X_DeleteProperty: + { + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 29, 9); + } + break; + case X_FillPoly: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_FillPoly target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_FillPoly target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_FillPoly target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_FillPoly); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + unsigned int numPoints = ((inputLength - 16) >> 2); + + if (control -> isProtoStep10() == 1) + { + encodeBuffer.encodeCachedValue(numPoints, 16, + clientCache_ -> fillPolyNumPointsCache, 4); + } + else + { + encodeBuffer.encodeCachedValue(numPoints, 14, + clientCache_ -> fillPolyNumPointsCache, 4); + } + + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), + clientCache_ -> gcCache); + encodeBuffer.encodeValue((unsigned int) inputMessage[12], 2); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]); + int relativeCoordMode = (inputMessage[13] != 0); + const unsigned char *nextSrc = inputMessage + 16; + unsigned int pointIndex = 0; + + for (unsigned int i = 0; i < numPoints; i++) + { + if (relativeCoordMode) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *clientCache_ -> fillPolyXRelCache[pointIndex], 8); + nextSrc += 2; + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *clientCache_ -> fillPolyYRelCache[pointIndex], 8); + nextSrc += 2; + } + else + { + unsigned int x = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + unsigned int y = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + unsigned int j; + for (j = 0; j < 8; j++) + if ((x == clientCache_ -> fillPolyRecentX[j]) && + (y == clientCache_ -> fillPolyRecentY[j])) + break; + if (j < 8) + { + encodeBuffer.encodeBoolValue(1); + encodeBuffer.encodeValue(j, 3); + } + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeCachedValue(x, 16, + *clientCache_ -> fillPolyXAbsCache[pointIndex], 8); + encodeBuffer.encodeCachedValue(y, 16, + *clientCache_ -> fillPolyYAbsCache[pointIndex], 8); + clientCache_ -> fillPolyRecentX[clientCache_ -> fillPolyIndex] = x; + clientCache_ -> fillPolyRecentY[clientCache_ -> fillPolyIndex] = y; + clientCache_ -> fillPolyIndex++; + if (clientCache_ -> fillPolyIndex == 8) + clientCache_ -> fillPolyIndex = 0; + } + } + + if (++pointIndex == 10) pointIndex = 0; + } + } + break; + case X_FreeColors: + { + unsigned int numPixels = GetUINT(inputMessage + 2, bigEndian_) - 3; + encodeBuffer.encodeValue(numPixels, 16, 4); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, + clientCache_ -> colormapCache); + encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 32, 4); + const unsigned char *nextSrc = inputMessage + 12; + while (numPixels) + { + encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 8); + nextSrc += 4; + numPixels--; + } + } + break; + case X_FreeCursor: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, clientCache_ -> cursorCache, 9); + } + break; + case X_FreeGC: + { + #ifdef TARGETS + + unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_); + + T_gcontexts::iterator i = gcontexts.find(g_id); + + if (i != gcontexts.end()) + { + unsigned int t_id = i -> second; + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_FreeGC gcontext id is " << g_id + << " target id is pixmap " << t_id << ".\n" + << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_FreeGC gcontext id is " << g_id + << " target id is window " << t_id << ".\n" + << logofs_flush; + } + else + { + *logofs << "handleRead: X_FreeGC gcontext id is " << g_id + << " target id is unrecognized.\n" + << logofs_flush; + } + } + else + { + *logofs << "handleRead: X_FreeGC gcontext id " << g_id + << " is unrecognized.\n" << logofs_flush; + } + + gcontexts.erase(g_id); + + #endif + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> freeGCCache); + } + else + { + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> gcCache); + } + } + break; + case X_FreePixmap: + { + #ifdef TARGETS + + unsigned int p_id = GetULONG(inputMessage + 4, bigEndian_); + + *logofs << "handleRead: X_FreePixmap id is " << p_id << ".\n" << logofs_flush; + + pixmaps.erase(p_id); + + #endif + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> freeDrawableCache); + } + else + { + unsigned int pixmap = GetULONG(inputMessage + 4, bigEndian_); + unsigned int diff = pixmap - clientCache_ -> createPixmapLastId; + if (diff == 0) + { + encodeBuffer.encodeBoolValue(1); + } + else + { + encodeBuffer.encodeBoolValue(0); + clientCache_ -> createPixmapLastId = pixmap; + encodeBuffer.encodeValue(diff, 29, 4); + } + } + } + break; + case X_GetAtomName: + { + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 29, 9); + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GetGeometry: + { + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> drawableCache); + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GetInputFocus: + { + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GetModifierMapping: + { + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GetKeyboardMapping: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[4], 8); + encodeBuffer.encodeValue((unsigned int) inputMessage[5], 8); + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GetProperty: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_GetProperty); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + unsigned int property = GetULONG(inputMessage + 8, bigEndian_); + + sequenceQueue_.push(clientSequence_, inputOpcode, property); + + priority_++; + + hit = 1; + + break; + } + + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + unsigned int property = GetULONG(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(property, 29, 9); + encodeBuffer.encodeValue(GetULONG(inputMessage + 12, bigEndian_), 29, 9); + encodeBuffer.encodeValue(GetULONG(inputMessage + 16, bigEndian_), 32, 2); + encodeBuffer.encodeValue(GetULONG(inputMessage + 20, bigEndian_), 32, 8); + + sequenceQueue_.push(clientSequence_, inputOpcode, property); + + priority_++; + } + break; + case X_GetSelectionOwner: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, + clientCache_ -> getSelectionOwnerSelectionCache, 9); + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GrabButton: + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, + clientCache_ -> grabButtonEventMaskCache); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[10]); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[11]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29, + clientCache_ -> grabButtonConfineCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 29, + clientCache_ -> cursorCache, 9); + encodeBuffer.encodeCachedValue(inputMessage[20], 8, + clientCache_ -> grabButtonButtonCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 22, bigEndian_), 16, + clientCache_ -> grabButtonModifierCache); + } + break; + case X_GrabPointer: + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, + clientCache_ -> grabButtonEventMaskCache); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[10]); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[11]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, + bigEndian_), 29, + clientCache_ -> grabButtonConfineCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, + bigEndian_), 29, clientCache_ -> cursorCache, 9); + + unsigned int timestamp = GetULONG(inputMessage + 20, bigEndian_); + encodeBuffer.encodeValue(timestamp - + clientCache_ -> grabKeyboardLastTimestamp, 32, 4); + clientCache_ -> grabKeyboardLastTimestamp = timestamp; + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GrabKeyboard: + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + unsigned int timestamp = GetULONG(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(timestamp - + clientCache_ -> grabKeyboardLastTimestamp, 32, 4); + clientCache_ -> grabKeyboardLastTimestamp = timestamp; + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]); + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GrabServer: + case X_UngrabServer: + case X_NoOperation: + { + } + break; + case X_PolyText8: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_PolyText8 target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_PolyText8 target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_PolyText8 target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyText8); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + unsigned int x = GetUINT(inputMessage + 12, bigEndian_); + int xDiff = x - clientCache_ -> polyTextLastX; + clientCache_ -> polyTextLastX = x; + encodeBuffer.encodeCachedValue(xDiff, 16, + clientCache_ -> polyTextCacheX); + unsigned int y = GetUINT(inputMessage + 14, bigEndian_); + int yDiff = y - clientCache_ -> polyTextLastY; + clientCache_ -> polyTextLastY = y; + encodeBuffer.encodeCachedValue(yDiff, 16, + clientCache_ -> polyTextCacheY); + const unsigned char *end = inputMessage + inputLength - 1; + const unsigned char *nextSrc = inputMessage + 16; + while (nextSrc < end) + { + unsigned int textLength = (unsigned int) *nextSrc++; + encodeBuffer.encodeBoolValue(1); + encodeBuffer.encodeValue(textLength, 8); + if (textLength == 255) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, 1), 29, + clientCache_ -> polyTextFontCache); + nextSrc += 4; + } + else + { + encodeBuffer.encodeCachedValue(*nextSrc++, 8, + clientCache_ -> polyTextDeltaCache); + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, textLength); + + nextSrc += textLength; + } + else + { + clientCache_ -> polyTextTextCompressor.reset(); + for (unsigned int i = 0; i < textLength; i++) + clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); + } + } + } + encodeBuffer.encodeBoolValue(0); + } + break; + case X_PolyText16: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_PolyText16 target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_PolyText16 target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_PolyText16 target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyText16); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + unsigned int x = GetUINT(inputMessage + 12, bigEndian_); + int xDiff = x - clientCache_ -> polyTextLastX; + clientCache_ -> polyTextLastX = x; + encodeBuffer.encodeCachedValue(xDiff, 16, + clientCache_ -> polyTextCacheX); + unsigned int y = GetUINT(inputMessage + 14, bigEndian_); + int yDiff = y - clientCache_ -> polyTextLastY; + clientCache_ -> polyTextLastY = y; + encodeBuffer.encodeCachedValue(yDiff, 16, + clientCache_ -> polyTextCacheY); + const unsigned char *end = inputMessage + inputLength - 1; + const unsigned char *nextSrc = inputMessage + 16; + while (nextSrc < end) + { + unsigned int textLength = (unsigned int) *nextSrc++; + encodeBuffer.encodeBoolValue(1); + encodeBuffer.encodeValue(textLength, 8); + if (textLength == 255) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, 1), 29, + clientCache_ -> polyTextFontCache); + nextSrc += 4; + } + else + { + encodeBuffer.encodeCachedValue(*nextSrc++, 8, + clientCache_ -> polyTextDeltaCache); + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, textLength * 2); + + nextSrc += textLength * 2; + } + else + { + clientCache_ -> polyTextTextCompressor.reset(); + for (unsigned int i = 0; i < textLength * 2; i++) + clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); + } + } + } + encodeBuffer.encodeBoolValue(0); + } + break; + case X_ImageText8: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_ImageText8 target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_ImageText8 target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_ImageText8 target id " + << t_id << " is unrecognized.\n" + << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ImageText8); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + unsigned int textLength = (unsigned int) inputMessage[1]; + encodeBuffer.encodeCachedValue(textLength, 8, + clientCache_ -> imageTextLengthCache, 4); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + unsigned int x = GetUINT(inputMessage + 12, bigEndian_); + int xDiff = x - clientCache_ -> imageTextLastX; + clientCache_ -> imageTextLastX = x; + encodeBuffer.encodeCachedValue(xDiff, 16, + clientCache_ -> imageTextCacheX); + unsigned int y = GetUINT(inputMessage + 14, bigEndian_); + int yDiff = y - clientCache_ -> imageTextLastY; + clientCache_ -> imageTextLastY = y; + encodeBuffer.encodeCachedValue(yDiff, 16, + clientCache_ -> imageTextCacheY); + const unsigned char *nextSrc = inputMessage + 16; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, textLength); + } + else + { + clientCache_ -> imageTextTextCompressor.reset(); + for (unsigned int j = 0; j < textLength; j++) + clientCache_ -> imageTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); + } + } + break; + case X_ImageText16: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_ImageText16 target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_ImageText16 target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_ImageText16 target id " + << t_id << " is unrecognized.\n" + << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ImageText16); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + unsigned int textLength = (unsigned int) inputMessage[1]; + encodeBuffer.encodeCachedValue(textLength, 8, + clientCache_ -> imageTextLengthCache, 4); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + unsigned int x = GetUINT(inputMessage + 12, bigEndian_); + int xDiff = x - clientCache_ -> imageTextLastX; + clientCache_ -> imageTextLastX = x; + encodeBuffer.encodeCachedValue(xDiff, 16, + clientCache_ -> imageTextCacheX); + unsigned int y = GetUINT(inputMessage + 14, bigEndian_); + int yDiff = y - clientCache_ -> imageTextLastY; + clientCache_ -> imageTextLastY = y; + encodeBuffer.encodeCachedValue(yDiff, 16, + clientCache_ -> imageTextCacheY); + const unsigned char *nextSrc = inputMessage + 16; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, textLength * 2); + } + else + { + clientCache_ -> imageTextTextCompressor.reset(); + for (unsigned int j = 0; j < textLength * 2; j++) + clientCache_ -> imageTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); + } + } + break; + case X_InternAtom: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_InternAtom); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + sequenceQueue_.push(clientSequence_, inputOpcode); + + // + // Set the priority, also if doing so will + // penalize all the well written clients + // using XInternAtoms() to pipeline multi- + // ple replies. + // + + priority_++; + + hit = 1; + + break; + } + + unsigned int nameLength = GetUINT(inputMessage + 4, bigEndian_); + encodeBuffer.encodeValue(nameLength, 16, 6); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + const unsigned char *nextSrc = inputMessage + 8; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, nameLength); + } + else + { + clientCache_ -> internAtomTextCompressor.reset(); + for (unsigned int i = 0; i < nameLength; i++) + { + clientCache_ -> internAtomTextCompressor.encodeChar(*nextSrc++, encodeBuffer); + } + } + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_ListExtensions: + { + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_ListFonts: + { + unsigned int textLength = GetUINT(inputMessage + 6, bigEndian_); + encodeBuffer.encodeValue(textLength, 16, 6); + encodeBuffer.encodeValue(GetUINT(inputMessage + 4, bigEndian_), 16, 6); + const unsigned char* nextSrc = inputMessage + 8; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, textLength); + } + else + { + clientCache_ -> polyTextTextCompressor.reset(); + for (unsigned int i = 0; i < textLength; i++) + { + clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); + } + } + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_LookupColor: + case X_AllocNamedColor: + { + unsigned int textLength = GetUINT(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(textLength, 16, 6); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, clientCache_ -> colormapCache); + const unsigned char *nextSrc = inputMessage + 12; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, textLength); + } + else + { + clientCache_ -> polyTextTextCompressor.reset(); + for (unsigned int i = 0; i < textLength; i++) + { + clientCache_ -> polyTextTextCompressor.encodeChar(*nextSrc++, encodeBuffer); + } + } + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_MapWindow: + case X_UnmapWindow: + case X_MapSubwindows: + case X_GetWindowAttributes: + case X_DestroyWindow: + case X_DestroySubwindows: + case X_QueryPointer: + case X_QueryTree: + { + #ifdef TARGETS + + if (inputOpcode == X_DestroyWindow) + { + unsigned int w_id = GetULONG(inputMessage + 4, bigEndian_); + + *logofs << "handleRead: X_DestroyWindow id is " + << w_id << ".\n" << logofs_flush; + + windows.erase(w_id); + } + + #endif + + if (inputOpcode == X_DestroyWindow && control -> isProtoStep7() == 1) + { + encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> freeWindowCache); + } + else + { + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> windowCache); + } + + if ((inputOpcode == X_QueryPointer) || + (inputOpcode == X_GetWindowAttributes) || + (inputOpcode == X_QueryTree)) + { + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + } + break; + case X_OpenFont: + { + unsigned int nameLength = GetUINT(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(nameLength, 16, 7); + unsigned int font = GetULONG(inputMessage + 4, bigEndian_); + encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5); + clientCache_ -> lastFont = font; + const unsigned char *nextSrc = inputMessage + 12; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, nameLength); + } + else + { + clientCache_ -> openFontTextCompressor.reset(); + for (; nameLength; nameLength--) + { + clientCache_ -> openFontTextCompressor. + encodeChar(*nextSrc++, encodeBuffer); + } + } + } + break; + case X_PolyFillRectangle: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_PolyFillRectangle target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_PolyFillRectangle target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_PolyFillRectangle target id " + << t_id << " is unrecognized.\n" + << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyFillRectangle); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + unsigned int lastWidth = 0, lastHeight = 0; + + // + // TODO: Could send the size at the beginning + // instead of a bool at each iteration. + // + + for (unsigned int i = 12; i < inputLength;) + { + unsigned int x = GetUINT(inputMessage + i, bigEndian_); + unsigned int newX = x; + x -= lastX; + lastX = newX; + encodeBuffer.encodeCachedValue(x, 16, + *clientCache_ -> polyFillRectangleCacheX[index], 8); + i += 2; + unsigned int y = GetUINT(inputMessage + i, bigEndian_); + unsigned int newY = y; + y -= lastY; + lastY = newY; + encodeBuffer.encodeCachedValue(y, 16, + *clientCache_ -> polyFillRectangleCacheY[index], 8); + i += 2; + unsigned int width = GetUINT(inputMessage + i, bigEndian_); + unsigned int newWidth = width; + width -= lastWidth; + lastWidth = newWidth; + encodeBuffer.encodeCachedValue(width, 16, + *clientCache_ -> polyFillRectangleCacheWidth[index], 8); + i += 2; + unsigned int height = GetUINT(inputMessage + i, bigEndian_); + unsigned int newHeight = height; + height -= lastHeight; + lastHeight = newHeight; + encodeBuffer.encodeCachedValue(height, 16, + *clientCache_ -> polyFillRectangleCacheHeight[index], 8); + i += 2; + + if (++index == 4) index = 0; + + encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0); + } + } + break; + case X_PolyFillArc: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_PolyFillArc target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_PolyFillArc target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_PolyFillArc target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyFillArc); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + unsigned int lastWidth = 0, lastHeight = 0; + unsigned int lastAngle1 = 0, lastAngle2 = 0; + + // + // TODO: Could send the size at the beginning + // instead of a bool at each iteration. + // + + for (unsigned int i = 12; i < inputLength;) + { + unsigned int x = GetUINT(inputMessage + i, bigEndian_); + unsigned int newX = x; + x -= lastX; + lastX = newX; + encodeBuffer.encodeCachedValue(x, 16, + *clientCache_ -> polyFillArcCacheX[index], 8); + i += 2; + unsigned int y = GetUINT(inputMessage + i, bigEndian_); + unsigned int newY = y; + y -= lastY; + lastY = newY; + encodeBuffer.encodeCachedValue(y, 16, + *clientCache_ -> polyFillArcCacheY[index], 8); + i += 2; + unsigned int width = GetUINT(inputMessage + i, bigEndian_); + unsigned int newWidth = width; + width -= lastWidth; + lastWidth = newWidth; + encodeBuffer.encodeCachedValue(width, 16, + *clientCache_ -> polyFillArcCacheWidth[index], 8); + i += 2; + unsigned int height = GetUINT(inputMessage + i, bigEndian_); + unsigned int newHeight = height; + height -= lastHeight; + lastHeight = newHeight; + encodeBuffer.encodeCachedValue(height, 16, + *clientCache_ -> polyFillArcCacheHeight[index], 8); + i += 2; + unsigned int angle1 = GetUINT(inputMessage + i, bigEndian_); + unsigned int newAngle1 = angle1; + angle1 -= lastAngle1; + lastAngle1 = newAngle1; + encodeBuffer.encodeCachedValue(angle1, 16, + *clientCache_ -> polyFillArcCacheAngle1[index], 8); + i += 2; + unsigned int angle2 = GetUINT(inputMessage + i, bigEndian_); + unsigned int newAngle2 = angle2; + angle2 -= lastAngle2; + lastAngle2 = newAngle2; + encodeBuffer.encodeCachedValue(angle2, 16, + *clientCache_ -> polyFillArcCacheAngle2[index], 8); + i += 2; + + if (++index == 2) index = 0; + + encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0); + } + } + break; + case X_PolyArc: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_PolyArc target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_PolyArc target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_PolyArc target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyArc); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + unsigned int lastWidth = 0, lastHeight = 0; + unsigned int lastAngle1 = 0, lastAngle2 = 0; + + // + // TODO: Could send the size at the beginning + // instead of a bool at each iteration. + // + + for (unsigned int i = 12; i < inputLength;) + { + unsigned int x = GetUINT(inputMessage + i, bigEndian_); + unsigned int newX = x; + x -= lastX; + lastX = newX; + encodeBuffer.encodeCachedValue(x, 16, + *clientCache_ -> polyArcCacheX[index], 8); + i += 2; + unsigned int y = GetUINT(inputMessage + i, bigEndian_); + unsigned int newY = y; + y -= lastY; + lastY = newY; + encodeBuffer.encodeCachedValue(y, 16, + *clientCache_ -> polyArcCacheY[index], 8); + i += 2; + unsigned int width = GetUINT(inputMessage + i, bigEndian_); + unsigned int newWidth = width; + width -= lastWidth; + lastWidth = newWidth; + encodeBuffer.encodeCachedValue(width, 16, + *clientCache_ -> polyArcCacheWidth[index], 8); + i += 2; + unsigned int height = GetUINT(inputMessage + i, bigEndian_); + unsigned int newHeight = height; + height -= lastHeight; + lastHeight = newHeight; + encodeBuffer.encodeCachedValue(height, 16, + *clientCache_ -> polyArcCacheHeight[index], 8); + i += 2; + unsigned int angle1 = GetUINT(inputMessage + i, bigEndian_); + unsigned int newAngle1 = angle1; + angle1 -= lastAngle1; + lastAngle1 = newAngle1; + encodeBuffer.encodeCachedValue(angle1, 16, + *clientCache_ -> polyArcCacheAngle1[index], 8); + i += 2; + unsigned int angle2 = GetUINT(inputMessage + i, bigEndian_); + unsigned int newAngle2 = angle2; + angle2 -= lastAngle2; + lastAngle2 = newAngle2; + encodeBuffer.encodeCachedValue(angle2, 16, + *clientCache_ -> polyArcCacheAngle2[index], 8); + i += 2; + + if (++index == 2) index = 0; + + encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0); + } + } + break; + case X_PolyPoint: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_PolyPoint target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_PolyPoint target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_PolyPoint target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyPoint); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_) - 3, 16, 4); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_), + clientCache_ -> gcCache); + const unsigned char *nextSrc = inputMessage + 12; + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + + for (unsigned int i = 12; i < inputLength; i += 4) + { + unsigned int x = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + unsigned int tmp = x; + x -= lastX; + lastX = tmp; + encodeBuffer.encodeCachedValue(x, 16, + *clientCache_ -> polyPointCacheX[index], 8); + unsigned int y = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + tmp = y; + y -= lastY; + lastY = tmp; + encodeBuffer.encodeCachedValue(y, 16, + *clientCache_ -> polyPointCacheY[index], 8); + + if (++index == 2) index = 0; + } + } + break; + case X_PolyLine: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_PolyLine target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_PolyLine target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_PolyLine target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyLine); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_) - 3, 16, 4); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + const unsigned char *nextSrc = inputMessage + 12; + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + + for (unsigned int i = 12; i < inputLength; i += 4) + { + unsigned int x = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + unsigned int tmp = x; + x -= lastX; + lastX = tmp; + encodeBuffer.encodeCachedValue(x, 16, + *clientCache_ -> polyLineCacheX[index], 8); + unsigned int y = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + tmp = y; + y -= lastY; + lastY = tmp; + encodeBuffer.encodeCachedValue(y, 16, + *clientCache_ -> polyLineCacheY[index], 8); + + if (++index == 2) index = 0; + } + } + break; + case X_PolyRectangle: + { + encodeBuffer.encodeValue((GetUINT(inputMessage + 2, + bigEndian_) - 3) >> 1, 16, 3); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + const unsigned char *end = inputMessage + inputLength; + const unsigned char *nextSrc = inputMessage + 12; + while (nextSrc < end) + { + for (unsigned int i = 0; i < 4; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *clientCache_ -> polyRectangleGeomCache[i], 8); + nextSrc += 2; + } + } + } + break; + case X_PolySegment: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_PolySegment target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_PolySegment target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_PolySegment target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolySegment); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + encodeBuffer.encodeValue((GetUINT(inputMessage + 2, + bigEndian_) - 3) >> 1, 16, 4); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, + bigEndian_), clientCache_ -> gcCache); + const unsigned char *end = inputMessage + inputLength; + const unsigned char *nextSrc = inputMessage + 12; + // unsigned int index = 0; + // unsigned int lastX1, lastY1, lastX2, lastY2; + while (nextSrc < end) + { + unsigned int x = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + unsigned int xDiff0 = + x - clientCache_ -> polySegmentLastX[0]; + unsigned int xDiff1 = + x - clientCache_ -> polySegmentLastX[1]; + int xDiff0Abs = (int) xDiff0; + if (xDiff0Abs < 0) + xDiff0Abs = -xDiff0Abs; + int xDiff1Abs = (int) xDiff1; + if (xDiff1Abs < 0) + xDiff1Abs = -xDiff1Abs; + + unsigned int y = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + unsigned int yDiff0 = + y - clientCache_ -> polySegmentLastY[0]; + unsigned int yDiff1 = + y - clientCache_ -> polySegmentLastY[1]; + int yDiff0Abs = (int) yDiff0; + if (yDiff0Abs < 0) + yDiff0Abs = -yDiff0Abs; + int yDiff1Abs = (int) yDiff1; + if (yDiff1Abs < 0) + yDiff1Abs = -yDiff1Abs; + + int diff0 = xDiff0Abs + yDiff0Abs; + int diff1 = xDiff1Abs + yDiff1Abs; + if (diff0 < diff1) + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeCachedValue(xDiff0, 16, + clientCache_ -> polySegmentCacheX, 6); + encodeBuffer.encodeCachedValue(yDiff0, 16, + clientCache_ -> polySegmentCacheY, 6); + } + else + { + encodeBuffer.encodeBoolValue(1); + encodeBuffer.encodeCachedValue(xDiff1, 16, + clientCache_ -> polySegmentCacheX, 6); + encodeBuffer.encodeCachedValue(yDiff1, 16, + clientCache_ -> polySegmentCacheY, 6); + } + + clientCache_ -> polySegmentLastX[clientCache_ -> polySegmentCacheIndex] = x; + clientCache_ -> polySegmentLastY[clientCache_ -> polySegmentCacheIndex] = y; + + clientCache_ -> polySegmentCacheIndex = + clientCache_ -> polySegmentCacheIndex == 1 ? 0 : 1; + } + } + break; + case X_PutImage: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_PutImage target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_PutImage target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_PutImage target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PutImage); + + hit = handleEncode(encodeBuffer, clientCache_, messageStore, + inputOpcode, inputMessage, inputLength); + } + break; + case X_QueryBestSize: + { + encodeBuffer.encodeValue((unsigned int)inputMessage[1], 2); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + encodeBuffer.encodeValue(GetUINT(inputMessage + 8, bigEndian_), 16, 8); + encodeBuffer.encodeValue(GetUINT(inputMessage + 10, bigEndian_), 16, 8); + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_QueryColors: + { + // Differential encoding. + encodeBuffer.encodeBoolValue(1); + + unsigned int numColors = ((inputLength - 8) >> 2); + encodeBuffer.encodeValue(numColors, 16, 5); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, + clientCache_ -> colormapCache); + const unsigned char *nextSrc = inputMessage + 8; + unsigned int predictedPixel = clientCache_ -> queryColorsLastPixel; + for (unsigned int i = 0; i < numColors; i++) + { + unsigned int pixel = GetULONG(nextSrc, bigEndian_); + nextSrc += 4; + if (pixel == predictedPixel) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(pixel, 32, 9); + } + if (i == 0) + clientCache_ -> queryColorsLastPixel = pixel; + predictedPixel = pixel + 1; + } + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_QueryExtension: + { + #ifdef TEST + + char data[256]; + + int length = GetUINT(inputMessage + 4, bigEndian_); + + if (length > 256) + { + length = 256; + } + + strncpy(data, (char *) inputMessage + 8, length); + + *(data + length) = '\0'; + + *logofs << "handleRead: Going to query extension '" + << data << "' for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + unsigned int nameLength = GetUINT(inputMessage + 4, bigEndian_); + encodeBuffer.encodeValue(nameLength, 16, 6); + const unsigned char *nextSrc = inputMessage + 8; + + for (; nameLength; nameLength--) + { + encodeBuffer.encodeValue((unsigned int) *nextSrc++, 8); + } + + unsigned int extension = 0; + + if (strncmp((char *) inputMessage + 8, "SHAPE", 5) == 0) + { + extension = X_NXInternalShapeExtension; + } + else if (strncmp((char *) inputMessage + 8, "RENDER", 6) == 0) + { + extension = X_NXInternalRenderExtension; + } + + sequenceQueue_.push(clientSequence_, inputOpcode, extension); + + priority_++; + } + break; + case X_QueryFont: + { + unsigned int font = GetULONG(inputMessage + 4, bigEndian_); + encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5); + clientCache_ -> lastFont = font; + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_SetClipRectangles: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_SetClipRectangles); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + hit = 1; + + break; + } + + unsigned int numRectangles = ((inputLength - 12) >> 3); + + if (control -> isProtoStep9() == 1) + { + encodeBuffer.encodeValue(numRectangles, 15, 4); + } + else + { + encodeBuffer.encodeValue(numRectangles, 13, 4); + } + + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> gcCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, + clientCache_ -> setClipRectanglesXCache, 8); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 10, bigEndian_), 16, + clientCache_ -> setClipRectanglesYCache, 8); + const unsigned char *nextSrc = inputMessage + 12; + for (unsigned int i = 0; i < numRectangles; i++) + { + for (unsigned int j = 0; j < 4; j++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *clientCache_ -> setClipRectanglesGeomCache[j], 8); + nextSrc += 2; + } + } + } + break; + case X_SetDashes: + { + unsigned int numDashes = GetUINT(inputMessage + 10, bigEndian_); + encodeBuffer.encodeCachedValue(numDashes, 16, + clientCache_ -> setDashesLengthCache, 5); + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_), + clientCache_ -> gcCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, + clientCache_ -> setDashesOffsetCache, 5); + const unsigned char *nextSrc = inputMessage + 12; + for (unsigned int i = 0; i < numDashes; i++) + encodeBuffer.encodeCachedValue(*nextSrc++, 8, + clientCache_ -> setDashesDashCache_[i & 1], 5); + } + break; + case X_SetSelectionOwner: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, + clientCache_ -> setSelectionOwnerCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, + clientCache_ -> getSelectionOwnerSelectionCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 32, + clientCache_ -> setSelectionOwnerTimestampCache, 9); + } + break; + case X_TranslateCoords: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_TranslateCoords source id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_TranslateCoords source id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_TranslateCoords source id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + t_id = GetULONG(inputMessage + 8, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_TranslateCoords target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_TranslateCoords target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_TranslateCoords target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_TranslateCoords); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + + hit = 1; + + break; + } + + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, + clientCache_ -> translateCoordsSrcCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, + clientCache_ -> translateCoordsDstCache, 9); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), 16, + clientCache_ -> translateCoordsXCache, 8); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 14, bigEndian_), 16, + clientCache_ -> translateCoordsYCache, 8); + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GetImage: + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_GetImage source id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_GetImage source id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_GetImage source id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_GetImage); + + if (handleEncodeCached(encodeBuffer, clientCache_, messageStore, + inputMessage, inputLength)) + { + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + + hit = 1; + + break; + } + + // Format. + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2); + // Drawable. + encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, + bigEndian_), clientCache_ -> drawableCache); + // X. + unsigned int x = GetUINT(inputMessage + 8, bigEndian_); + int xDiff = x - clientCache_ -> putImageLastX; + clientCache_ -> putImageLastX = x; + encodeBuffer.encodeCachedValue(xDiff, 16, + clientCache_ -> putImageXCache, 8); + // Y. + unsigned int y = GetUINT(inputMessage + 10, bigEndian_); + int yDiff = y - clientCache_ -> putImageLastY; + clientCache_ -> putImageLastY = y; + encodeBuffer.encodeCachedValue(yDiff, 16, + clientCache_ -> putImageYCache, 8); + // Width. + unsigned int width = GetUINT(inputMessage + 12, bigEndian_); + encodeBuffer.encodeCachedValue(width, 16, + clientCache_ -> putImageWidthCache, 8); + // Height. + unsigned int height = GetUINT(inputMessage + 14, bigEndian_); + encodeBuffer.encodeCachedValue(height, 16, + clientCache_ -> putImageHeightCache, 8); + // Plane mask. + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 32, + clientCache_ -> getImagePlaneMaskCache, 5); + + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GetPointerMapping: + { + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + case X_GetKeyboardControl: + { + sequenceQueue_.push(clientSequence_, inputOpcode); + + priority_++; + } + break; + default: + { + if (inputOpcode == opcodeStore_ -> renderExtension) + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalRenderExtension); + + hit = handleEncode(encodeBuffer, clientCache_, messageStore, + inputOpcode, inputMessage, inputLength); + } + else if (inputOpcode == opcodeStore_ -> shapeExtension) + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalShapeExtension); + + hit = handleEncode(encodeBuffer, clientCache_, messageStore, + inputOpcode, inputMessage, inputLength); + } + else if (inputOpcode == opcodeStore_ -> putPackedImage) + { + #ifdef TARGETS + + unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_); + + if (pixmaps.find(t_id) != pixmaps.end()) + { + *logofs << "handleRead: X_NXPutPackedImage target id is pixmap " + << t_id << ".\n" << logofs_flush; + } + else if (windows.find(t_id) != windows.end()) + { + *logofs << "handleRead: X_NXPutPackedImage target id is window " + << t_id << ".\n" << logofs_flush; + } + else + { + *logofs << "handleRead: X_NXPutPackedImage target id " << t_id + << " is unrecognized.\n" << logofs_flush; + } + + #endif + + #ifdef DEBUG + *logofs << "handleRead: Encoding packed image request for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + // + // The field carries the destination data + // length. We add the request's size of + // the final X_PutImage. + // + + unsigned int outputLength = GetULONG(inputMessage + 20, bigEndian_) + 24; + + statistics -> addPackedBytesIn(inputLength); + + statistics -> addPackedBytesOut(outputLength); + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXPutPackedImage); + + hit = handleEncode(encodeBuffer, clientCache_, messageStore, + inputOpcode, inputMessage, inputLength); + } + else if (inputOpcode == opcodeStore_ -> setUnpackColormap) + { + #ifdef DEBUG + *logofs << "handleRead: Encoding set unpack colormap request " + << "for FD#" << fd_ << " with size " << inputLength + << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackColormap); + + hit = handleEncode(encodeBuffer, clientCache_, messageStore, + inputOpcode, inputMessage, inputLength); + } + else if (inputOpcode == opcodeStore_ -> setUnpackAlpha) + { + #ifdef DEBUG + *logofs << "handleRead: Encoding set unpack alpha request " + << "for FD#" << fd_ << " with size " << inputLength + << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackAlpha); + + hit = handleEncode(encodeBuffer, clientCache_, messageStore, + inputOpcode, inputMessage, inputLength); + } + else if (inputOpcode == opcodeStore_ -> setUnpackGeometry) + { + #ifdef DEBUG + *logofs << "handleRead: Encoding set unpack geometry request " + << "for FD#" << fd_ << " with size " << inputLength + << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackGeometry); + + hit = handleEncode(encodeBuffer, clientCache_, messageStore, + inputOpcode, inputMessage, inputLength); + } + else if (inputOpcode == opcodeStore_ -> startSplit) + { + if (handleStartSplitRequest(encodeBuffer, inputOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + else if (inputOpcode == opcodeStore_ -> endSplit) + { + if (handleEndSplitRequest(encodeBuffer, inputOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + else if (inputOpcode == opcodeStore_ -> commitSplit) + { + if (handleCommitSplitRequest(encodeBuffer, inputOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + else if (inputOpcode == opcodeStore_ -> abortSplit) + { + if (handleAbortSplitRequest(encodeBuffer, inputOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + else if (inputOpcode == opcodeStore_ -> finishSplit) + { + if (handleFinishSplitRequest(encodeBuffer, inputOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + else if (inputOpcode == opcodeStore_ -> freeSplit) + { + #ifdef DEBUG + *logofs << "handleRead: Encoding free split request " + << "for FD#" << fd_ << " with size " << inputLength + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8, + clientCache_ -> resourceCache); + } + else if (inputOpcode == opcodeStore_ -> freeUnpack) + { + #ifdef DEBUG + *logofs << "handleRead: Encoding free unpack request " + << "for FD#" << fd_ << " with size " << inputLength + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8, + clientCache_ -> resourceCache); + } + else if (inputOpcode == opcodeStore_ -> getControlParameters) + { + #ifdef DEBUG + *logofs << "handleRead: Encoding get control parameters " + << "request for FD#" << fd_ << " with size " + << inputLength << ".\n" << logofs_flush; + #endif + + // + // Add the reply to the write buffer. If found + // to contain a message, it it will be flushed + // to the X client before leaving the loop. + // + + unsigned char *reply = writeBuffer_.addMessage(32); + + *(reply + 0) = X_Reply; + + PutUINT(clientSequence_, reply + 2, bigEndian_); + + PutULONG(0, reply + 4, bigEndian_); + + // + // Save the sequence number we used + // to auto-generate this reply. + // + + lastSequence_ = clientSequence_; + + #ifdef TEST + *logofs << "handleRead: Registered " << lastSequence_ + << " as last auto-generated sequence number.\n" + << logofs_flush; + #endif + + *(reply + 1) = control -> LinkMode; + + *(reply + 8) = control -> LocalVersionMajor; + *(reply + 9) = control -> LocalVersionMinor; + *(reply + 10) = control -> LocalVersionPatch; + + *(reply + 11) = control -> RemoteVersionMajor; + *(reply + 12) = control -> RemoteVersionMinor; + *(reply + 13) = control -> RemoteVersionPatch; + + PutUINT(control -> SplitTimeout, reply + 14, bigEndian_); + PutUINT(control -> MotionTimeout, reply + 16, bigEndian_); + + *(reply + 18) = control -> SplitMode; + + PutULONG(control -> SplitDataThreshold, reply + 20, bigEndian_); + + *(reply + 24) = control -> PackMethod; + *(reply + 25) = control -> PackQuality; + + *(reply + 26) = control -> LocalDataCompressionLevel; + *(reply + 27) = control -> LocalStreamCompressionLevel; + *(reply + 28) = control -> LocalDeltaCompression; + + *(reply + 29) = (control -> LocalDeltaCompression == 1 && + control -> PersistentCacheEnableLoad == 1); + *(reply + 30) = (control -> LocalDeltaCompression == 1 && + control -> PersistentCacheEnableSave == 1); + *(reply + 31) = (control -> LocalDeltaCompression == 1 && + control -> PersistentCacheEnableLoad == 1 && + control -> PersistentCacheName != NULL); + + if (handleFlush(flush_if_any) < 0) + { + return -1; + } + } + else if (inputOpcode == opcodeStore_ -> getCleanupParameters) + { + #ifdef WARNING + *logofs << "handleRead: WARNING! Encoding fake get cleanup " + << "parameters request for FD#" << fd_ << " with size " + << inputLength << ".\n" << logofs_flush; + #endif + } + else if (inputOpcode == opcodeStore_ -> getImageParameters) + { + #ifdef WARNING + *logofs << "handleRead: WARNING! Encoding fake get cleanup " + << "parameters request for FD#" << fd_ << " with size " + << inputLength << ".\n" << logofs_flush; + #endif + } + else if (inputOpcode == opcodeStore_ -> getUnpackParameters) + { + #ifdef DEBUG + *logofs << "handleRead: Encoding get unpack parameters " + << "request for FD#" << fd_ << " with size " + << inputLength << ".\n" << logofs_flush; + #endif + + sequenceQueue_.push(clientSequence_, inputOpcode); + } + else if (inputOpcode == opcodeStore_ -> getShmemParameters) + { + if (handleShmemRequest(encodeBuffer, inputOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + else if (inputOpcode == opcodeStore_ -> setExposeParameters) + { + // + // Enable or disable expose events + // coming from the real server. + // + + encodeBuffer.encodeBoolValue(*(inputMessage + 4)); + encodeBuffer.encodeBoolValue(*(inputMessage + 5)); + encodeBuffer.encodeBoolValue(*(inputMessage + 6)); + } + else if (inputOpcode == opcodeStore_ -> setCacheParameters) + { + if (handleCacheRequest(encodeBuffer, inputOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + else if (inputOpcode == opcodeStore_ -> getFontParameters) + { + if (handleFontRequest(encodeBuffer, inputOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + else + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalGenericRequest); + + hit = handleEncode(encodeBuffer, clientCache_, messageStore, + inputOpcode, inputMessage, inputLength); + + // + // Don't flush if the opcode is unrecognized. + // We may optionally flush it is an extension + // but would penalize the well written clients. + // + // if (inputOpcode > 127) + // { + // priority_++; + // } + // + } + } + } // End of switch on opcode. + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + const char *cacheString = (hit ? "cached " : ""); + + *logofs << "handleRead: Handled " << cacheString << "request OPCODE#" + << (unsigned int) inputOpcode << " (" << DumpOpcode(inputOpcode) + << ")" << " for FD#" << fd_ << " sequence " << clientSequence_ + << ". " << inputLength << " bytes in, " << bits << " bits (" + << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; + + #endif + + if (hit) + { + statistics -> addCachedRequest(inputOpcode); + } + + statistics -> addRequestBits(inputOpcode, inputLength << 3, bits); + + if (inputOpcode == opcodeStore_ -> renderExtension) + { + if (hit) + { + statistics -> addRenderCachedRequest(*(inputMessage + 1)); + } + + statistics -> addRenderRequestBits(*(inputMessage + 1), inputLength << 3, bits); + } + + } // End if (firstRequest_)... else ... + + } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != 0) ... + + // + // Check if we need to flush because of + // prioritized data. + // + + if (priority_ > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: WARNING! Requesting flush " + << "because of " << priority_ << " prioritized " + << "messages for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncPriority() < 0) + { + return -1; + } + + // + // Reset the priority flag. + // + + priority_ = 0; + } + + // + // Flush if we exceeded the token length. + // + + if (proxy -> canAsyncFlush() == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: WARNING! Requesting flush " + << "because of token length exceeded.\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + } + + #if defined(TEST) || defined(INFO) + + if (transport_ -> pending() != 0 || + readBuffer_.checkMessage() != 0) + { + *logofs << "handleRead: PANIC! Buffer for X descriptor FD#" + << fd_ << " has " << transport_ -> pending() + << " bytes to read.\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // Reset the read buffer. + // + + readBuffer_.fullReset(); + + return 1; +} + +// +// End of handleRead(). +// + +// +// Beginning of handleWrite(). +// + +int ClientChannel::handleWrite(const unsigned char *message, unsigned int length) +{ + #ifdef TEST + *logofs << "handleWrite: Called for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Create the buffer from which to + // decode messages. + // + + DecodeBuffer decodeBuffer(message, length); + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "handleWrite: Decoding messages for FD#" << fd_ + << " with " << length << " bytes in the buffer.\n" + << logofs_flush; + #endif + + if (firstReply_) + { + #ifdef TEST + *logofs << "handleWrite: First reply detected.\n" << logofs_flush; + #endif + + unsigned int outputOpcode; + + decodeBuffer.decodeValue(outputOpcode, 8); + unsigned int secondByte; + decodeBuffer.decodeValue(secondByte, 8); + unsigned int major; + decodeBuffer.decodeValue(major, 16); + unsigned int minor; + decodeBuffer.decodeValue(minor, 16); + unsigned int extraLength; + decodeBuffer.decodeValue(extraLength, 16); + unsigned int outputLength = 8 + (extraLength << 2); + + unsigned char *outputMessage = writeBuffer_.addMessage(outputLength); + *outputMessage = (unsigned char) outputOpcode; + outputMessage[1] = (unsigned char) secondByte; + PutUINT(major, outputMessage + 2, bigEndian_); + PutUINT(minor, outputMessage + 4, bigEndian_); + PutUINT(extraLength, outputMessage + 6, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + unsigned int cached; + decodeBuffer.decodeBoolValue(cached); + + if (cached) + { + memcpy(nextDest, ServerCache::lastInitReply.getData(), outputLength - 8); + } + else + { + for (unsigned i = 8; i < outputLength; i++) + { + unsigned int nextByte; + decodeBuffer.decodeValue(nextByte, 8); + *nextDest++ = (unsigned char) nextByte; + } + + ServerCache::lastInitReply.set(outputLength - 8, outputMessage + 8); + } + + imageByteOrder_ = outputMessage[30]; + bitmapBitOrder_ = outputMessage[31]; + scanlineUnit_ = outputMessage[32]; + scanlinePad_ = outputMessage[33]; + + firstReply_ = 0; + + } // End of if (firstReply_) + + // + // This was previously in a 'else' block. + // Due to the way the first request was + // handled, we could not decode multiple + // messages in the first frame. + // + + { // Start of the decoding block. + + #ifdef DEBUG + *logofs << "handleWrite: Starting loop on opcodes.\n" + << logofs_flush; + #endif + + unsigned char outputOpcode; + + // + // NX client needs this line to consider + // the initialization phase successfully + // completed. + // + + if (firstClient_ == -1) + { + cerr << "Info" << ": Established X client connection.\n" ; + + firstClient_ = fd_; + } + + while (decodeBuffer.decodeOpcodeValue(outputOpcode, serverCache_ -> opcodeCache, 1)) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoded a new OPCODE#" + << (unsigned int) outputOpcode << ".\n" + << logofs_flush; + #endif + + unsigned char *outputMessage = NULL; + unsigned int outputLength = 0; + + // + // General-purpose temp variables + // for decoding ints and chars. + // + + unsigned int value = 0; + unsigned char cValue = 0; + + // + // Check first if we need to abort any split, + // then if this is a reply, finally if it is + // en event or error. + // + + if (outputOpcode == opcodeStore_ -> splitEvent) + { + // + // It's an abort split, not a normal + // burst of proxy data. + // + + handleSplitEvent(decodeBuffer); + + continue; + } + else if (outputOpcode == X_Reply) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding sequence number of reply.\n" + << logofs_flush; + #endif + + unsigned int sequenceNum; + unsigned int sequenceDiff; + + decodeBuffer.decodeCachedValue(sequenceDiff, 16, + serverCache_ -> replySequenceCache, 7); + + sequenceNum = (serverSequence_ + sequenceDiff) & 0xffff; + + serverSequence_ = sequenceNum; + + #ifdef DEBUG + *logofs << "handleWrite: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + // + // In case of reply we can follow the X server and + // override any event's sequence number generated + // by this side. + // + + #ifdef TEST + *logofs << "handleWrite: Updating last event's sequence " + << lastSequence_ << " to reply's sequence number " + << serverSequence_ << " for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + lastSequence_ = serverSequence_; + + unsigned short int requestSequenceNum; + unsigned char requestOpcode; + + #ifdef DEBUG + + requestSequenceNum = 0; + requestOpcode = 0; + + *logofs << "handleWrite: Peek of sequence number returns "; + + *logofs << sequenceQueue_.peek(requestSequenceNum, requestOpcode); + + *logofs << " with sequence " << requestSequenceNum << " and opcode " + << (unsigned int) requestOpcode << ".\n" << logofs_flush; + + #endif + + if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) == 1 && + (requestSequenceNum == sequenceNum)) + { + unsigned int requestData[3]; + + sequenceQueue_.pop(requestSequenceNum, requestOpcode, + requestData[0], requestData[1], requestData[2]); + + #ifdef DEBUG + *logofs << "handleWrite: Identified reply to OPCODE#" + << (unsigned int) requestOpcode << ".\n" + << logofs_flush; + #endif + + // + // Is differential encoding disabled? + // + + if (control -> RemoteDeltaCompression == 0) + { + int result = handleFastWriteReply(decodeBuffer, requestOpcode, + outputMessage, outputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + switch (requestOpcode) + { + case X_AllocColor: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + unsigned char *nextDest = outputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeBoolValue(value); + if (value) + { + PutUINT(requestData[i], nextDest, bigEndian_); + } + else + { + decodeBuffer.decodeValue(value, 16, 6); + PutUINT(requestData[i] + value, nextDest, bigEndian_); + } + nextDest += 2; + } + decodeBuffer.decodeValue(value, 32, 9); + PutULONG(value, outputMessage + 16, bigEndian_); + } + break; + case X_GetAtomName: + { + unsigned int nameLength; + decodeBuffer.decodeValue(nameLength, 16, 6); + outputLength = RoundUp4(nameLength) + 32; + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(nameLength, outputMessage + 8, bigEndian_); + unsigned char* nextDest = outputMessage + 32; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, nameLength); + } + else + { + serverCache_ -> getAtomNameTextCompressor.reset(); + for (unsigned int i = 0; i < nameLength; i++) + { + *nextDest++ = serverCache_ -> getAtomNameTextCompressor. + decodeChar(decodeBuffer); + } + } + } + break; + case X_GetGeometry: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(cValue, 8, + serverCache_ -> depthCache); + outputMessage[1] = cValue; + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> getGeometryRootCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + for (unsigned int i = 0; i < 5; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *serverCache_ -> getGeometryGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + } + break; + case X_GetInputFocus: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> getInputFocusWindowCache); + PutULONG(value, outputMessage + 8, bigEndian_); + } + break; + case X_GetKeyboardMapping: + { + decodeBuffer.decodeBoolValue(value); + if (value) + { + unsigned int dataLength = + ServerCache::getKeyboardMappingLastMap.getLength(); + outputLength = 32 + dataLength; + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = + ServerCache::getKeyboardMappingLastKeysymsPerKeycode; + memcpy(outputMessage + 32, + ServerCache::getKeyboardMappingLastMap.getData(), + dataLength); + break; + } + unsigned int numKeycodes; + decodeBuffer.decodeValue(numKeycodes, 8); + unsigned int keysymsPerKeycode; + decodeBuffer.decodeValue(keysymsPerKeycode, 8, 4); + ServerCache::getKeyboardMappingLastKeysymsPerKeycode = + keysymsPerKeycode; + outputLength = 32 + numKeycodes * keysymsPerKeycode * 4; + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) keysymsPerKeycode; + unsigned char *nextDest = outputMessage + 32; + unsigned char previous = 0; + for (unsigned int count = numKeycodes * keysymsPerKeycode; + count; --count) + { + decodeBuffer.decodeBoolValue(value); + if (value) + PutULONG((unsigned int) NoSymbol, nextDest, bigEndian_); + else + { + unsigned int keysym; + decodeBuffer.decodeCachedValue(keysym, 24, + serverCache_ -> getKeyboardMappingKeysymCache, 9); + decodeBuffer.decodeCachedValue(cValue, 8, + serverCache_ -> getKeyboardMappingLastByteCache, 5); + previous += cValue; + PutULONG((keysym << 8) | previous, nextDest, bigEndian_); + } + nextDest += 4; + } + ServerCache::getKeyboardMappingLastMap.set(outputLength - 32, + outputMessage + 32); + } + break; + case X_GetModifierMapping: + { + unsigned int keycodesPerModifier; + decodeBuffer.decodeValue(keycodesPerModifier, 8); + outputLength = 32 + (keycodesPerModifier << 3); + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) keycodesPerModifier; + unsigned char *nextDest = outputMessage + 32; + decodeBuffer.decodeBoolValue(value); + if (value) + { + memcpy(outputMessage + 32, + ServerCache::getModifierMappingLastMap.getData(), + ServerCache::getModifierMappingLastMap.getLength()); + break; + } + for (unsigned int count = outputLength - 32; count; count--) + { + decodeBuffer.decodeBoolValue(value); + if (value) + *nextDest++ = 0; + else + { + decodeBuffer.decodeValue(value, 8); + *nextDest++ = value; + } + } + ServerCache::getModifierMappingLastMap.set(outputLength - 32, + outputMessage + 32); + } + break; + case X_GetProperty: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_GetProperty); + + handleDecode(decodeBuffer, serverCache_, messageStore, + requestOpcode, outputMessage, outputLength); + } + break; + case X_GetSelectionOwner: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> getSelectionOwnerCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + } + break; + case X_GetWindowAttributes: + { + outputLength = 44; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> visualCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> getWindowAttributesClassCache, 3); + PutUINT(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(cValue, 8, + serverCache_ -> getWindowAttributesBitGravityCache); + outputMessage[14] = cValue; + decodeBuffer.decodeCachedValue(cValue, 8, + serverCache_ -> getWindowAttributesWinGravityCache); + outputMessage[15] = cValue; + decodeBuffer.decodeCachedValue(value, 32, + serverCache_ -> getWindowAttributesPlanesCache, 9); + PutULONG(value, outputMessage + 16, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + serverCache_ -> getWindowAttributesPixelCache, 9); + PutULONG(value, outputMessage + 20, bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[24] = (unsigned char) value; + decodeBuffer.decodeBoolValue(value); + outputMessage[25] = (unsigned char) value; + decodeBuffer.decodeValue(value, 2); + outputMessage[26] = (unsigned char) value; + decodeBuffer.decodeBoolValue(value); + outputMessage[27] = (unsigned char) value; + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> colormapCache, 9); + PutULONG(value, outputMessage + 28, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + serverCache_ -> getWindowAttributesAllEventsCache); + PutULONG(value, outputMessage + 32, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + serverCache_ -> getWindowAttributesYourEventsCache); + PutULONG(value, outputMessage + 36, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> getWindowAttributesDontPropagateCache); + PutUINT(value, outputMessage + 40, bigEndian_); + } + break; + case X_GrabKeyboard: + case X_GrabPointer: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 3); + outputMessage[1] = (unsigned char) value; + } + break; + case X_InternAtom: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 29, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + } + break; + case X_ListExtensions: + { + decodeBuffer.decodeValue(value, 32, 8); + outputLength = 32 + (value << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + unsigned int numExtensions; + decodeBuffer.decodeValue(numExtensions, 8); + outputMessage[1] = (unsigned char) numExtensions; + unsigned char *nextDest = outputMessage + 32; + for (; numExtensions; numExtensions--) + { + unsigned int length; + decodeBuffer.decodeValue(length, 8); + *nextDest++ = (unsigned char) length; + for (; length; length--) + { + decodeBuffer.decodeValue(value, 8); + *nextDest++ = value; + } + } + } + break; + case X_ListFonts: + { + // + // Differential compression can achieve a 12:1 to 14:1 + // ratio, while the best ZLIB compression can achieve + // a mere 4:1 to 5:1. In the first case, though, the + // huge amount of data constituting the message would + // be stored uncompressed at the remote side. We need + // to find a compromise. The solution is to use diffe- + // rential compression at startup and ZLIB compression + // later on. + // + + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_ListFonts); + + if (handleDecodeCached(decodeBuffer, serverCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + decodeBuffer.decodeValue(value, 32, 8); + outputLength = 32 + (value << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + unsigned int numFonts; + decodeBuffer.decodeValue(numFonts, 16, 6); + PutUINT(numFonts, outputMessage + 8, bigEndian_); + + // Differential or plain data compression? + decodeBuffer.decodeBoolValue(value); + + if (value) + { + unsigned char* nextDest = outputMessage + 32; + for (; numFonts; numFonts--) + { + unsigned int length; + decodeBuffer.decodeValue(length, 8); + *nextDest++ = (unsigned char)length; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, length); + + nextDest += length; + } + else + { + serverCache_ -> getPropertyTextCompressor.reset(); + for (; length; length--) + { + *nextDest++ = serverCache_ -> getPropertyTextCompressor. + decodeChar(decodeBuffer); + } + } + } + + handleSave(messageStore, outputMessage, outputLength); + } + else + { + const unsigned char *compressedData = NULL; + unsigned int compressedDataSize = 0; + + int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset, + outputMessage, outputLength, compressedData, + compressedDataSize); + if (decompressed < 0) + { + return -1; + } + else if (decompressed > 0) + { + handleSave(messageStore, outputMessage, outputLength, + compressedData, compressedDataSize); + } + else + { + handleSave(messageStore, outputMessage, outputLength); + } + } + } + break; + case X_LookupColor: + case X_AllocNamedColor: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + unsigned char *nextDest = outputMessage + 8; + if (requestOpcode == X_AllocNamedColor) + { + decodeBuffer.decodeValue(value, 32, 9); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + unsigned int count = 3; + do + { + decodeBuffer.decodeValue(value, 16, 9); + PutUINT(value, nextDest, bigEndian_); + unsigned int visualColor; + decodeBuffer.decodeValue(visualColor, 16, 5); + visualColor += value; + visualColor &= 0xffff; + PutUINT(visualColor, nextDest + 6, bigEndian_); + nextDest += 2; + } + while (--count); + } + break; + case X_QueryBestSize: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 16, 8); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 16, 8); + PutUINT(value, outputMessage + 10, bigEndian_); + } + break; + case X_QueryColors: + { + // Differential or plain data compression? + decodeBuffer.decodeBoolValue(value); + + if (value) + { + decodeBuffer.decodeBoolValue(value); + if (value) + { + unsigned int numColors = + serverCache_ -> queryColorsLastReply.getLength() / 6; + outputLength = 32 + (numColors << 3); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(numColors, outputMessage + 8, bigEndian_); + const unsigned char *nextSrc = + serverCache_ -> queryColorsLastReply.getData(); + unsigned char *nextDest = outputMessage + 32; + for (; numColors; numColors--) + { + for (unsigned int i = 0; i < 6; i++) + *nextDest++ = *nextSrc++; + nextDest += 2; + } + } + else + { + unsigned int numColors; + decodeBuffer.decodeValue(numColors, 16, 5); + outputLength = 32 + (numColors << 3); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(numColors, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 32; + for (unsigned int c = 0; c < numColors; c++) + { + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeValue(value, 16); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + } + serverCache_ -> queryColorsLastReply.set(numColors * 6, + outputMessage + 32); + const unsigned char *nextSrc = nextDest - 1; + nextDest = outputMessage + 32 + ((numColors - 1) << 3) + 5; + for (; numColors > 1; numColors--) + { + for (unsigned int i = 0; i < 6; i++) + *nextDest-- = *nextSrc--; + nextDest -= 2; + } + } + } + else + { + // Reply length. + unsigned int numColors; + decodeBuffer.decodeValue(numColors, 16, 5); + outputLength = 32 + (numColors << 3); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(numColors, outputMessage + 8, bigEndian_); + + const unsigned char *compressedData = NULL; + unsigned int compressedDataSize = 0; + + int decompressed = handleDecompress(decodeBuffer, requestOpcode, 32, + outputMessage, outputLength, compressedData, + compressedDataSize); + if (decompressed < 0) + { + return -1; + } + } + } + break; + case X_QueryExtension: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[8] = (unsigned char) value; + decodeBuffer.decodeValue(value, 8); + outputMessage[9] = (unsigned char) value; + decodeBuffer.decodeValue(value, 8); + outputMessage[10] = (unsigned char) value; + decodeBuffer.decodeValue(value, 8); + outputMessage[11] = (unsigned char) value; + + // + // We use a predefined opcode to address + // extensions' message stores, while real + // opcodes are used for communication with + // X server and clients. + // + + if (requestData[0] == X_NXInternalShapeExtension) + { + opcodeStore_ -> shapeExtension = outputMessage[9]; + + #ifdef TEST + *logofs << "handleWrite: Shape extension opcode for FD#" << fd_ + << " is " << (unsigned int) opcodeStore_ -> shapeExtension + << ".\n" << logofs_flush; + #endif + } + else if (requestData[0] == X_NXInternalRenderExtension) + { + opcodeStore_ -> renderExtension = outputMessage[9]; + + #ifdef TEST + *logofs << "handleWrite: Render extension opcode for FD#" << fd_ + << " is " << (unsigned int) opcodeStore_ -> renderExtension + << ".\n" << logofs_flush; + #endif + } + } + break; + case X_QueryFont: + { + // + // Use differential compression at startup and plain + // data compression later. Check X_ListFonts message + // for an explaination. + // + + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_QueryFont); + + if (handleDecodeCached(decodeBuffer, serverCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + // Differential or plain data compression? + decodeBuffer.decodeBoolValue(value); + + if (value) + { + unsigned int numProperties; + unsigned int numCharInfos; + decodeBuffer.decodeValue(numProperties, 16, 8); + decodeBuffer.decodeValue(numCharInfos, 32, 10); + outputLength = 60 + numProperties * 8 + numCharInfos * 12; + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(numProperties, outputMessage + 46, bigEndian_); + PutULONG(numCharInfos, outputMessage + 56, bigEndian_); + handleDecodeCharInfo(decodeBuffer, outputMessage + 8); + handleDecodeCharInfo(decodeBuffer, outputMessage + 24); + decodeBuffer.decodeValue(value, 16, 9); + PutUINT(value, outputMessage + 40, bigEndian_); + decodeBuffer.decodeValue(value, 16, 9); + PutUINT(value, outputMessage + 42, bigEndian_); + decodeBuffer.decodeValue(value, 16, 9); + PutUINT(value, outputMessage + 44, bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[48] = (unsigned char) value; + decodeBuffer.decodeValue(value, 8); + outputMessage[49] = (unsigned char) value; + decodeBuffer.decodeValue(value, 8); + outputMessage[50] = (unsigned char) value; + decodeBuffer.decodeBoolValue(value); + outputMessage[51] = (unsigned char) value; + decodeBuffer.decodeValue(value, 16, 9); + PutUINT(value, outputMessage + 52, bigEndian_); + decodeBuffer.decodeValue(value, 16, 9); + PutUINT(value, outputMessage + 54, bigEndian_); + unsigned char *nextDest = outputMessage + 60; + decodeBuffer.decodeBoolValue(value); + + int end = 0; + + if (value == 1) + { + unsigned int index; + decodeBuffer.decodeValue(index, 4); + unsigned int length; + const unsigned char *data; + ServerCache::queryFontFontCache.get(index, length, data); + memcpy(nextDest, data, length); + + end = 1; + } + + if (end == 0) + { + unsigned char *saveDest = nextDest; + unsigned int length = numProperties * 8 + numCharInfos * 12; + for (; numProperties; numProperties--) + { + decodeBuffer.decodeValue(value, 32, 9); + PutULONG(value, nextDest, bigEndian_); + decodeBuffer.decodeValue(value, 32, 9); + PutULONG(value, nextDest + 4, bigEndian_); + nextDest += 8; + } + for (; numCharInfos; numCharInfos--) + { + handleDecodeCharInfo(decodeBuffer, nextDest); + + nextDest += 12; + } + ServerCache::queryFontFontCache.set(length, saveDest); + } + + handleSave(messageStore, outputMessage, outputLength); + } + else + { + // Reply length. + unsigned int replyLength; + decodeBuffer.decodeValue(replyLength, 32, 16); + outputLength = 32 + (replyLength << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + + const unsigned char *compressedData = NULL; + unsigned int compressedDataSize = 0; + + int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset, + outputMessage, outputLength, compressedData, + compressedDataSize); + if (decompressed < 0) + { + return -1; + } + else if (decompressed > 0) + { + handleSave(messageStore, outputMessage, outputLength, + compressedData, compressedDataSize); + } + else + { + handleSave(messageStore, outputMessage, outputLength); + } + } + } + break; + case X_QueryPointer: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> queryPointerRootCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> queryPointerChildCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> motionNotifyRootXCache, 8); + serverCache_ -> motionNotifyLastRootX += value; + PutUINT(serverCache_ -> motionNotifyLastRootX, outputMessage + 16, + bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> motionNotifyRootYCache, 8); + serverCache_ -> motionNotifyLastRootY += value; + PutUINT(serverCache_ -> motionNotifyLastRootY, outputMessage + 18, + bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> motionNotifyEventXCache, 8); + PutUINT(serverCache_ -> motionNotifyLastRootX + value, + outputMessage + 20, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> motionNotifyEventYCache, 8); + PutUINT(serverCache_ -> motionNotifyLastRootY + value, + outputMessage + 22, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> motionNotifyStateCache); + PutUINT(value, outputMessage + 24, bigEndian_); + } + break; + case X_QueryTree: + { + unsigned int children; + decodeBuffer.decodeValue(children, 16, 8); + + outputLength = 32 + (children << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + + PutULONG(outputLength, outputMessage + 4, bigEndian_); + + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> queryTreeWindowCache); + + PutULONG(value, outputMessage + 8, bigEndian_); + + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> queryTreeWindowCache); + + PutULONG(value, outputMessage + 12, bigEndian_); + + unsigned char *next = outputMessage + 32; + + PutUINT(children, outputMessage + 16, bigEndian_); + + for (unsigned int i = 0; i < children; i++) + { + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> queryTreeWindowCache); + + PutULONG(value, next + (i * 4), bigEndian_); + } + } + break; + case X_TranslateCoords: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> translateCoordsChildCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> translateCoordsXCache, 8); + PutUINT(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> translateCoordsYCache, 8); + PutUINT(value, outputMessage + 14, bigEndian_); + } + break; + case X_GetImage: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_GetImage); + + if (handleDecodeCached(decodeBuffer, serverCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + // Depth. + decodeBuffer.decodeCachedValue(cValue, 8, + serverCache_ -> depthCache); + // Reply length. + unsigned int replyLength; + decodeBuffer.decodeValue(replyLength, 32, 9); + outputLength = 32 + (replyLength << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) cValue; + // Visual. + unsigned int visual; + decodeBuffer.decodeCachedValue(visual, 29, + serverCache_ -> visualCache); + PutULONG(visual, outputMessage + 8, bigEndian_); + + if (control -> isProtoStep8() == 0) + { + const unsigned char *compressedData = NULL; + unsigned int compressedDataSize = 0; + + int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset, + outputMessage, outputLength, compressedData, + compressedDataSize); + if (decompressed < 0) + { + return -1; + } + else if (decompressed > 0) + { + handleSave(messageStore, outputMessage, outputLength, + compressedData, compressedDataSize); + } + else + { + handleSave(messageStore, outputMessage, outputLength); + } + } + else + { + handleCopy(decodeBuffer, requestOpcode, messageStore -> + dataOffset, outputMessage, outputLength); + + handleSave(messageStore, outputMessage, outputLength); + } + } + break; + case X_GetPointerMapping: + { + unsigned int nextByte; + decodeBuffer.decodeValue(nextByte, 8, 4); + unsigned int replyLength; + decodeBuffer.decodeValue(replyLength, 32, 4); + outputLength = 32 + (replyLength << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) nextByte; + unsigned char *nextDest = outputMessage + 32; + for (unsigned int i = 32; i < outputLength; i++) + { + decodeBuffer.decodeValue(nextByte, 8, 4); + *nextDest++ = (unsigned char) nextByte; + } + } + break; + case X_GetKeyboardControl: + { + unsigned int nextByte; + decodeBuffer.decodeValue(nextByte, 8, 2); + unsigned int replyLength; + decodeBuffer.decodeValue(replyLength, 32, 8); + outputLength = 32 + (replyLength << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) nextByte; + unsigned char *nextDest = outputMessage + 8; + for (unsigned int i = 8; i < outputLength; i++) + { + decodeBuffer.decodeValue(nextByte, 8, 4); + *nextDest++ = (unsigned char) nextByte; + } + } + break; + default: + { + if (requestOpcode == opcodeStore_ -> getUnpackParameters) + { + #ifdef TEST + *logofs << "handleWrite: Received get unpack parameters reply " + << "OPCODE#" << (unsigned int) opcodeStore_ -> getUnpackParameters + << ".\n" << logofs_flush; + #endif + + outputLength = 32 + PACK_METHOD_LIMIT; + + outputMessage = writeBuffer_.addMessage(outputLength); + + unsigned int method; + + // + // Let agent use only the unpack methods + // implemented at both sides. + // + + for (int i = 0; i < PACK_METHOD_LIMIT; i++) + { + decodeBuffer.decodeBoolValue(method); + + control -> RemoteUnpackMethods[i] = method; + + *(outputMessage + 32 + i) = + (control -> LocalUnpackMethods[i] == 1 && + method == 1); + } + } + else if (requestOpcode == opcodeStore_ -> getShmemParameters) + { + if (handleShmemReply(decodeBuffer, requestOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else if (requestOpcode == opcodeStore_ -> getFontParameters) + { + if (handleFontReply(decodeBuffer, requestOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else + { + #ifdef PANIC + *logofs << "handleWrite: PANIC! No matching request for " + << "reply with sequence number " << sequenceNum + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": No matching request for " + << "reply with sequence number " << sequenceNum + << ".\n"; + + return -1; + } + } + } + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleWrite: Handled reply to OPCODE#" + << (unsigned) requestOpcode << " (" << DumpOpcode(requestOpcode) + << ")" << " for FD#" << fd_ << " with sequence " << serverSequence_ + << ". Output size is " << outputLength << ".\n" << logofs_flush; + #endif + + statistics -> addRepliedRequest(requestOpcode); + } + else // End of if (sequenceQueue_.peek() && ...) + { + // + // Reply didn't match any request opcode. + // Check again if differential encoding + // is disabled. + // + + #ifdef DEBUG + *logofs << "handleWrite: Identified generic reply.\n" + << logofs_flush; + #endif + + requestOpcode = X_Reply; + + if (control -> RemoteDeltaCompression == 0) + { + int result = handleFastWriteReply(decodeBuffer, requestOpcode, + outputMessage, outputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + // + // All replies whose opcode is not pushed in + // sequence number queue are cached together. + // Among such replies are those to extension + // requests. + // + + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_NXInternalGenericReply); + + handleDecode(decodeBuffer, serverCache_, messageStore, + requestOpcode, outputMessage, outputLength); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleWrite: Handled generic reply for FD#" << fd_ + << " with sequence " << serverSequence_ << ". Output size is " + << outputLength << ".\n" << logofs_flush; + #endif + + statistics -> addRepliedRequest(requestOpcode); + + } // End of if (sequenceQueue_.peek() && ...) else ... + + // + // If any output was produced then write opcode, + // sequence number and size to the buffer. + // + + if (outputLength > 0) + { + *outputMessage = outputOpcode; + + PutUINT(serverSequence_, outputMessage + 2, bigEndian_); + + PutULONG((outputLength - 32) >> 2, outputMessage + 4, bigEndian_); + } + + } // End of if (outputOpcode == 1)... + else + { + // + // It's an event or error. + // + + unsigned int sequenceNum; + unsigned int sequenceDiff; + + decodeBuffer.decodeCachedValue(sequenceDiff, 16, + serverCache_ -> eventSequenceCache, 7); + + sequenceNum = (serverSequence_ + sequenceDiff) & 0xffff; + + serverSequence_ = sequenceNum; + + #ifdef DEBUG + *logofs << "handleWrite: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + // + // Check if this is an error that matches + // a sequence number for which we were + // expecting a reply. + // + + if (outputOpcode == X_Error) + { + unsigned short int errorSequenceNum; + unsigned char errorOpcode; + + if (sequenceQueue_.peek(errorSequenceNum, errorOpcode) && + ((unsigned) errorSequenceNum == serverSequence_)) + { + // + // Remove the queued sequence of the reply. + // + + #ifdef TEST + *logofs << "handleWrite: WARNING! Removing reply to OPCODE#" + << (unsigned) errorOpcode << " sequence " + << errorSequenceNum << " for FD#" << fd_ + << " due to error.\n" << logofs_flush; + #endif + + sequenceQueue_.pop(errorSequenceNum, errorOpcode); + + // + // Send to the client the current sequence + // number, not the number that matched the + // reply. Because we are generating replies + // at our side, Xlib can incur in a sequence + // lost if the error comes after the auto- + // generated reply. + // + + if (control -> SessionMode == session_proxy) + { + #ifdef TEST + *logofs << "handleWrite: Updating last event's sequence " + << lastSequence_ << " to X server's error sequence " + << "number " << serverSequence_ << " for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + lastSequence_ = serverSequence_; + } + } + + // + // In case of errors always send to client the + // original X server's sequence associated to + // the failing request. + // + + if (control -> SessionMode != session_proxy) + { + #ifdef TEST + *logofs << "handleWrite: Updating last event's sequence " + << lastSequence_ << " to X server's error sequence " + << "number " << serverSequence_ << " for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + lastSequence_ = serverSequence_; + } + } + + // + // Check if by producing events at client side we + // have modified the events' sequence numbering. + // In this case taint the original sequence to + // comply with the last one known by client. + // + +/* +FIXME: Recover the sequence number if the proxy + is not connected to an agent. +*/ + if (serverSequence_ > lastSequence_ || + control -> SessionMode != session_proxy) + { + #ifdef DEBUG + *logofs << "handleWrite: Updating last event's sequence " + << lastSequence_ << " to X server's sequence number " + << serverSequence_ << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + lastSequence_ = serverSequence_; + } + #ifdef DEBUG + else if (serverSequence_ < lastSequence_) + { + // + // Use our last auto-generated sequence. + // + + *logofs << "handleWrite: Tainting sequence number " + << serverSequence_ << " to last event's sequence " + << lastSequence_ << " for FD#" << fd_ << ".\n" + << logofs_flush; + } + #endif + + // + // Check if remote side used fast encoding. + // + + if (control -> RemoteDeltaCompression == 0) + { + int result = handleFastWriteEvent(decodeBuffer, outputOpcode, + outputMessage, outputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + // + // Make space for message in the outgoing buffer + // and write opcode and sequence number. + // + + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + + *outputMessage = outputOpcode; + + PutUINT(lastSequence_, outputMessage + 2, bigEndian_); + + #ifdef DEBUG + *logofs << "handleWrite: Going to handle event or error OPCODE#" + << (unsigned int) outputOpcode << " for FD#" << fd_ + << " sequence " << lastSequence_ << " (real was " + << serverSequence_ << ").\n" << logofs_flush; + #endif + + switch (outputOpcode) + { + case X_Error: + { + unsigned char code; + decodeBuffer.decodeCachedValue(code, 8, + serverCache_ -> errorCodeCache); + outputMessage[1] = code; + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleWrite: Handled error ERR_CODE#" + << (unsigned int) code << " for FD#" << fd_; + #endif + + if ((code != 11) && (code != 8) && + (code != 15) && (code != 1)) + { + decodeBuffer.decodeValue(value, 32, 16); + PutULONG(value, outputMessage + 4, bigEndian_); + + #if defined(TEST) || defined(OPCODES) + *logofs << " RES_ID#" << value; + #endif + } + + if (code >= 18) + { + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> errorMinorCache); + PutUINT(value, outputMessage + 8, bigEndian_); + + #if defined(TEST) || defined(OPCODES) + *logofs << " MIN_OP#" << value; + #endif + } + + decodeBuffer.decodeCachedValue(cValue, 8, + serverCache_ -> errorMajorCache); + outputMessage[10] = cValue; + + #if defined(TEST) || defined(OPCODES) + *logofs << " MAJ_OP#" << (unsigned int) cValue; + #endif + + if (code >= 18) + { + unsigned char *nextDest = outputMessage + 11; + for (unsigned int i = 11; i < 32; i++) + { + decodeBuffer.decodeValue(value, 8); + *nextDest++ = (unsigned char) cValue; + } + } + + #if defined(TEST) || defined(OPCODES) + *logofs << " sequence " << lastSequence_ << " (real was " + << serverSequence_ << ") . Size is " + << (unsigned int) outputLength << ".\n" + << logofs_flush; + #endif + } + break; + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + case MotionNotify: + case EnterNotify: + case LeaveNotify: + { + if (outputOpcode == MotionNotify) + { + decodeBuffer.decodeBoolValue(value); + } + else if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify) + { + decodeBuffer.decodeValue(value, 3); + } + else if (outputOpcode == KeyRelease) + { + decodeBuffer.decodeBoolValue(value); + if (value) + { + value = serverCache_ -> keyPressLastKey; + } + else + { + decodeBuffer.decodeValue(value, 8); + } + } + else if (outputOpcode == ButtonPress || outputOpcode == ButtonRelease) + { + decodeBuffer.decodeCachedValue(cValue, 8, + serverCache_ -> buttonCache); + value = (unsigned int) cValue; + } + else + { + decodeBuffer.decodeValue(value, 8); + } + + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeCachedValue(value, 32, + serverCache_ -> motionNotifyTimestampCache, 9); + serverCache_ -> lastTimestamp += value; + PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4, + bigEndian_); + unsigned char *nextDest = outputMessage + 8; + int skipRest = 0; + if (outputOpcode == KeyRelease) + { + decodeBuffer.decodeBoolValue(value); + if (value) + { + for (unsigned int i = 0; i < 23; i++) + { + *nextDest++ = serverCache_ -> keyPressCache[i]; + } + skipRest = 1; + } + } + + if (!skipRest) + { + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeCachedValue(value, 29, + *serverCache_ -> motionNotifyWindowCache[i], 6); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> motionNotifyRootXCache, 6); + serverCache_ -> motionNotifyLastRootX += value; + PutUINT(serverCache_ -> motionNotifyLastRootX, outputMessage + 20, + bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> motionNotifyRootYCache, 6); + serverCache_ -> motionNotifyLastRootY += value; + PutUINT(serverCache_ -> motionNotifyLastRootY, outputMessage + 22, + bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> motionNotifyEventXCache, 6); + PutUINT(serverCache_ -> motionNotifyLastRootX + value, + outputMessage + 24, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> motionNotifyEventYCache, 6); + PutUINT(serverCache_ -> motionNotifyLastRootY + value, + outputMessage + 26, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> motionNotifyStateCache); + PutUINT(value, outputMessage + 28, bigEndian_); + if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify) + { + decodeBuffer.decodeValue(value, 2); + } + else + { + decodeBuffer.decodeBoolValue(value); + } + outputMessage[30] = (unsigned char) value; + if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify) + { + decodeBuffer.decodeValue(value, 2); + outputMessage[31] = (unsigned char) value; + } + else if (outputOpcode == KeyPress) + { + serverCache_ -> keyPressLastKey = outputMessage[1]; + for (unsigned int i = 8; i < 31; i++) + { + serverCache_ -> keyPressCache[i - 8] = outputMessage[i]; + } + } + } + } + break; + case ColormapNotify: + { + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> colormapNotifyWindowCache, 8); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> colormapNotifyColormapCache, 8); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[12] = (unsigned char) value; + decodeBuffer.decodeBoolValue(value); + outputMessage[13] = (unsigned char) value; + } + break; + case ConfigureNotify: + { + unsigned char *nextDest = outputMessage + 4; + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeCachedValue(value, 29, + *serverCache_ -> configureNotifyWindowCache[i], 9); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + for (unsigned int j = 0; j < 5; j++) + { + decodeBuffer.decodeCachedValue(value, 16, + *serverCache_ -> configureNotifyGeomCache[j], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + decodeBuffer.decodeBoolValue(value); + *nextDest = value; + } + break; + case CreateNotify: + { + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> createNotifyWindowCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 29, 5); + serverCache_ -> createNotifyLastWindow += value; + serverCache_ -> createNotifyLastWindow &= 0x1fffffff; + PutULONG(serverCache_ -> createNotifyLastWindow, outputMessage + 8, + bigEndian_); + unsigned char* nextDest = outputMessage + 12; + for (unsigned int i = 0; i < 5; i++) + { + decodeBuffer.decodeValue(value, 16, 9); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + decodeBuffer.decodeBoolValue(value); + *nextDest = (unsigned char) value; + } + break; + case Expose: + { + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> exposeWindowCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + for (unsigned int i = 0; i < 5; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *serverCache_ -> exposeGeomCache[i], 6); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + } + break; + case FocusIn: + case FocusOut: + { + decodeBuffer.decodeValue(value, 3); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> focusInWindowCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 2); + outputMessage[8] = (unsigned char) value; + } + break; + case KeymapNotify: + { + decodeBuffer.decodeBoolValue(value); + if (value) + memcpy(outputMessage + 1, ServerCache::lastKeymap.getData(), 31); + else + { + unsigned char *nextDest = outputMessage + 1; + for (unsigned int i = 1; i < 32; i++) + { + decodeBuffer.decodeValue(value, 8); + *nextDest++ = (unsigned char) value; + } + ServerCache::lastKeymap.set(31, outputMessage + 1); + } + } + break; + case MapNotify: + case UnmapNotify: + case DestroyNotify: + { + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> mapNotifyEventCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> mapNotifyWindowCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + if (outputOpcode == MapNotify || outputOpcode == UnmapNotify) + { + decodeBuffer.decodeBoolValue(value); + outputMessage[12] = (unsigned char) value; + } + } + break; + case NoExpose: + { + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> noExposeDrawableCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + serverCache_ -> noExposeMinorCache); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(cValue, 8, + serverCache_ -> noExposeMajorCache); + outputMessage[10] = cValue; + } + break; + case PropertyNotify: + { + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> propertyNotifyWindowCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> propertyNotifyAtomCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 32, 9); + serverCache_ -> lastTimestamp += value; + PutULONG(serverCache_ -> lastTimestamp, outputMessage + 12, + bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[16] = (unsigned char) value; + } + break; + case ReparentNotify: + { + unsigned char* nextDest = outputMessage + 4; + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> reparentNotifyWindowCache, 9); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + decodeBuffer.decodeValue(value, 16, 6); + PutUINT(value, nextDest, bigEndian_); + decodeBuffer.decodeValue(value, 16, 6); + PutUINT(value, nextDest + 2, bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[20] = (unsigned char)value; + } + break; + case SelectionClear: + { + decodeBuffer.decodeValue(value, 32, 9); + serverCache_ -> lastTimestamp += value; + PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4, + bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> selectionClearWindowCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> selectionClearAtomCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + } + break; + case SelectionRequest: + { + decodeBuffer.decodeValue(value, 32, 9); + serverCache_ -> lastTimestamp += value; + PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4, + bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> selectionClearWindowCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> selectionClearWindowCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> selectionClearAtomCache, 9); + PutULONG(value, outputMessage + 16, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> selectionClearAtomCache, 9); + PutULONG(value, outputMessage + 20, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> selectionClearAtomCache, 9); + PutULONG(value, outputMessage + 24, bigEndian_); + } + break; + case VisibilityNotify: + { + decodeBuffer.decodeCachedValue(value, 29, + serverCache_ -> visibilityNotifyWindowCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 2); + outputMessage[8] = (unsigned char) value; + } + break; + default: + { + #ifdef TEST + *logofs << "handleWrite: Using generic event compression " + << "for OPCODE#" << (unsigned int) outputOpcode + << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(*(outputMessage + 1), 8, + serverCache_ -> genericEventCharCache); + + for (unsigned int i = 0; i < 14; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *serverCache_ -> genericEventIntCache[i]); + + PutUINT(value, outputMessage + i * 2 + 4, bigEndian_); + } + } + } // End of switch (outputOpcode)... + + #if defined(TEST) || defined(OPCODES) + if (outputOpcode != X_Error) + { + *logofs << "handleWrite: Handled event OPCODE#" + << (unsigned int) outputOpcode << " for FD#" + << fd_ << " sequence " << lastSequence_ << " (real was " + << serverSequence_ << "). Size is " << outputLength + << ".\n" << logofs_flush; + } + #endif + + // + // Check if we need to suppress the error. + // + + if (outputOpcode == X_Error && + handleTaintSyncError(*(outputMessage + 10)) > 0) + { + #if defined(TEST) || defined(OPCODES) + *logofs << "handleWrite: WARNING! Suppressed error OPCODE#" + << (unsigned int) outputOpcode << " for FD#" + << fd_ << " sequence " << lastSequence_ << ".\n" + << logofs_flush; + #endif + + writeBuffer_.removeMessage(32); + } + + } // End of if (outputOpcode == 1)... else ... + + // + // Check if we produced enough data. We need to + // decode all provided messages. Just update the + // finish flag in case of failure. + // + + handleFlush(flush_if_needed); + + } // End of while (decodeBuffer.decodeOpcodeValue(outputOpcode, 8, ... + + } // End of the decoding block. + + // + // Write any remaining data to the X connection. + // + + if (handleFlush(flush_if_any) < 0) + { + return -1; + } + + return 1; +} + +// +// End of handleWrite(). +// + +// +// Other members. +// + +int ClientChannel::handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store, + T_store_action action, int position, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + #if defined(TEST) || defined(SPLIT) + + if (control -> isProtoStep8() == 1) + { + *logofs << "handleSplit: PANIC! SPLIT! Split should " + << "not be enabled for message " << "OPCODE#" + << (unsigned int) store -> opcode() << ".\n" + << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // Never split the message if connected to + // an old proxy version. Also refuse the + // split if we it is not introduced by a + // start split. + // + + if (control -> isProtoStep7() == 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Ignoring the split with " + << "an old proxy version.\n" << logofs_flush; + #endif + + if (action == IS_ADDED || action == is_discarded) + { + encodeBuffer.encodeBoolValue(0); + } + + return 0; + } + else if (splitState_.resource == nothing || enableSplit_ == 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Nothing to do for message " + << "OPCODE#" << (unsigned int) store -> opcode() + << " of size " << size << " position " << position + << " with action [" << DumpAction(action) << "] at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeBoolValue(0); + + return 0; + } + + // + // It's not advisable to allocate the store at + // the time we receive the start-split because + // we may process all the splits received and + // deallocate the store even before we receive + // the end split. Another message for the same + // split sequence may then come and we would + // have a null split store. + // + + handleSplitStoreAlloc(&splitResources_, splitState_.resource); + + // + // Check if the split was actually requested by + // the agent and if the request was saved in the + // message store. The split can also be refused + // if the message is smaller than the threshold + // or if the split store is already full. + // + + if (mustSplitMessage(splitState_.resource) == 0) + { + if (action == IS_HIT || canSplitMessage(splitState_.mode, size) == 0) + { + #if defined(TEST) || defined(SPLIT) + + if (splitState_.mode == split_none) + { + #ifdef PANIC + *logofs << "handleSplit: PANIC! SPLIT! Split state has " + << "mode 'none'.\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + if (action != IS_HIT && (int) size >= + control -> SplitDataThreshold) + { + #ifdef WARNING + *logofs << "handleSplit: WARNING! SPLIT! Split stores have " + << clientStore_ -> getSplitTotalSize() << " messages " + << "and " << clientStore_ -> getSplitTotalStorageSize() + << " allocated bytes.\n" << logofs_flush; + #endif + } + + #endif + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Message OPCODE#" + << (unsigned int) store -> opcode() << " of size " << size + << " [not split] with resource " << splitState_.resource + << " mode " << splitState_.mode << " position " << position + << " and action [" << DumpAction(action) << "] at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeBoolValue(0); + + return 0; + } + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Message OPCODE#" + << (unsigned int) store -> opcode() << " of size " << size + << " [split] with resource " << splitState_.resource + << " mode " << splitState_.mode << " position " << position + << " and action [" << DumpAction(action) << "] at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeBoolValue(1); + + T_checksum checksum = NULL; + + if (action == IS_ADDED) + { + checksum = store -> getChecksum(position); + } + else if (action == is_discarded) + { + // + // Generate the checksum on the fly. + // + + checksum = store -> getChecksum(buffer, size, bigEndian_); + } + + // + // The method must abort the connection + // if it can't allocate the split. + // + + Split *splitMessage = clientStore_ -> getSplitStore(splitState_.resource) -> + add(store, splitState_.resource, splitState_.mode, + position, action, checksum, buffer, size); + + // + // Send the checksum. By using the checksum, + // the remote end will try to locate the + // message and load it from disk. + // + + if (action == IS_HIT) + { + splitMessage -> setState(split_loaded); + } + else if (handleSplitChecksum(encodeBuffer, checksum) == 0) + { + // + // If the checksum is not sent, for example + // because loading of messages from disk is + // disabled, then mark the split as missed. + // + + #ifdef WARNING + *logofs << "handleSplit: WARNING! Checksum not sent. " + << "Marking the split as [missed].\n" + << logofs_flush; + #endif + + splitMessage -> setState(split_missed); + } + + if (action == is_discarded) + { + delete [] checksum; + } + + // + // Check if we are ready to send a new split + // for this store. + // + + handleSplitPending(splitState_.resource); + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleSplit: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.resource); + + #endif + + return 1; +} + +int ClientChannel::handleSplit(EncodeBuffer &encodeBuffer) +{ + // + // Determine the maximum amount of bytes + // we can write in this iteration. + // + + int total = control -> SplitDataPacketLimit; + + int bytes = total; + int splits = 0; + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Handling splits " + << "for FD#" << fd_ << " with " << clientStore_ -> + getSplitTotalSize() << " elements and " << total + << " bytes to write at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Looping to find " + << "if there is any split to send.\n" + << logofs_flush; + #endif + + SplitStore *splitStore; + + Split *splitMessage; + + // + // Divide the available bandwidth among all the active + // split stores by implementing a simple round-robin + // mechanism. This can be extended by using an external + // function returning the number of bytes to be written + // based on the state of the split (splits which didn't + // receive yet a confirmation event could be delayed), + // the current bitrate, and by letting the agent asso- + // ciate a priority to the resource in the start split + // operation. + // + + splitState_.pending = 0; + + splitResources_.rotate(); + + // + // Copy the list since elements can be removed + // in the middle of the loop. + // + + T_list splitList = splitResources_.copyList(); + + for (T_list::iterator j = splitList.begin(); + j != splitList.end(); j++) + { + int resource = *j; + + #ifdef DEBUG + *logofs << "handleSplit: SPLIT! Looping with current " + << "resource " << resource << ".\n" + << logofs_flush; + #endif + + splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore != NULL) + { + // + // Don't send more than the the packet size + // bytes but ensure that we abort any split + // found in the disk cache. + // + + for (;;) + { + #if defined(TEST) || defined(SPLIT) + + clientStore_ -> dumpSplitStore(resource); + + #endif + + splitMessage = splitStore -> getFirstSplit(); + + if (splitMessage == NULL) + { + // + // We have created the store after a start + // split but no message was added yet. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: WARNING! SPLIT! The split store " + << "is still empty.\n" << logofs_flush; + #endif + + break; + } + + // + // Splits already aborted can't be in the + // split store. + // + + #if defined(TEST) || defined(SPLIT) + + if (splitMessage -> getState() == split_aborted) + { + *logofs << "handleSplit: PANIC! SPLIT! Found an " + << "aborted split in store [" << resource + << "].\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // Check if there are more messages in the + // store that can be aborted or if we have + // exceeded the number of bytes we can send + // for this iteration. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Checking closure " + << "of the inner loop with " << bytes + << " bytes to write and split state [" + << DumpState(splitMessage -> getState()) + << "].\n" << logofs_flush; + #endif + + if ((splitMessage -> getMode() == split_sync && + splitMessage -> getState() == split_added) || + (bytes <= 0 && splitMessage -> + getState() != split_loaded)) + { + break; + } + + // + // If the split was loaded at the remote + // side abort it immediately. + // + + if (splitMessage -> getState() == split_loaded) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Sending more data " + << "for store [" << resource << "] with " + << "a split to be aborted.\n" + << logofs_flush; + #endif + + if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0) + { + return -1; + } + } + else if (bytes > 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Sending more data " + << "for store [" << resource << "] with " + << bytes << " bytes to send.\n" + << logofs_flush; + #endif + + if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0) + { + return -1; + } + } + + // + // Check if the split store was deleted. + // + + splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore == NULL) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Exiting from the " + << "inner loop with split store [" << resource + << "] destroyed.\n" << logofs_flush; + #endif + + break; + } + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Completed handling splits " + << "for store [" << resource << "] with " << bytes + << " bytes still to send.\n" << logofs_flush; + #endif + + // + // Check if there is still a split to + // send for the store just processed. + // + + handleSplitPending(resource); + } + } + + #if defined(TEST) || defined(SPLIT) + + if (splits == 0) + { + #ifdef PANIC + *logofs << "handleSplit: PANIC! Function called but " + << "no split message was sent.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + + *logofs << "handleSplit: SPLIT! Sent " << splits + << " splits and " << total - bytes << " bytes for FD#" << fd_ + << " with " << clientStore_ -> getSplitTotalStorageSize() + << " bytes and [" << clientStore_ -> getSplitTotalSize() + << "] splits remaining.\n" << logofs_flush; + + *logofs << "handleSplit: SPLIT! The pending split flag is " + << splitState_.pending << " with " << clientStore_ -> + getSplitTotalSize() << " splits in the split stores.\n" + << logofs_flush; + + clientStore_ -> dumpSplitStores(); + + #endif + + return 1; +} + +int ClientChannel::handleSplitSend(EncodeBuffer &encodeBuffer, int resource, + int &splits, int &bytes) +{ + #if defined(TEST) || defined(SPLIT) + + SplitStore *splitStore = clientStore_ -> getSplitStore(resource); + + Split *splitMessage = splitStore -> getFirstSplit(); + + if (splitStore -> getResource() != resource || + splitMessage -> getResource() != resource) + { + #ifdef PANIC + *logofs << "handleSplitSend: PANIC! The resource doesn't " + << "match the split store.\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + *logofs << "handleSplitSend: SPLIT! Sending message " + << "OPCODE#" << (unsigned) opcodeStore_ -> splitData + << " for resource " << splitMessage -> getResource() + << " with request " << splitMessage -> getRequest() + << " position " << splitMessage -> getPosition() + << " and " << bytes << " bytes to write.\n" + << logofs_flush; + #endif + + // + // Use a special opcode to signal the other + // side this is part of a split and not a + // new message. + // + + encodeBuffer.encodeOpcodeValue(opcodeStore_ -> splitData, + clientCache_ -> opcodeCache); + + encodeBuffer.encodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + int result = clientStore_ -> getSplitStore(resource) -> + send(encodeBuffer, bytes); + + if (result < 0) + { + #ifdef PANIC + *logofs << "handleSplit: PANIC! Error sending splits for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Error sending splits for FD#" + << fd_ << ".\n"; + + return -1; + } + + // + // Get the bits written and update the + // statistics for this special opcode. + // + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(SPLIT)|| defined(OPCODES) + *logofs << "handleSplitSend: SPLIT! Handled request OPCODE#" + << (unsigned int) opcodeStore_ -> splitData << " (" + << DumpOpcode(opcodeStore_ -> splitData) << ")" << " for FD#" + << fd_ << " sequence none. 0 bytes in, " << bits << " bits (" + << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; + #endif + + statistics -> addRequestBits(opcodeStore_ -> splitData, 0, bits); + + bytes -= bits >> 3; + + splits++; + + if (result == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitSend: SPLIT! Split at the head " + << "of the list was completely transferred.\n" + << logofs_flush; + #endif + + // + // The split at the head of the list was + // completely transferred. + // + + handleRestart(sequence_deferred, resource); + } + #if defined(TEST) || defined(SPLIT) + else + { + *logofs << "handleSplitSend: SPLIT! More data to send " + << "for the split at the head of the list.\n" + << logofs_flush; + } + #endif + + return result; +} + +int ClientChannel::handleSplitChecksum(EncodeBuffer &encodeBuffer, T_checksum checksum) +{ + // + // Send the checksum only if the loading + // or the saving of the message to the + // persistent image cache is enabled. + // + + if ((control -> ImageCacheEnableLoad == 1 || + control -> ImageCacheEnableSave == 1) && + (enableLoad_ == 1 || enableSave_ == 1)) + { + encodeBuffer.encodeBoolValue(1); + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + encodeBuffer.encodeValue((unsigned int) checksum[i], 8); + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitChecksum: SPLIT! Sent checksum " + << "[" << DumpChecksum(checksum) << "].\n" + << logofs_flush; + #endif + + return 1; + } + else + { + encodeBuffer.encodeBoolValue(0); + + return 0; + } +} + +void ClientChannel::handleSplitPending() +{ + #if defined(TEST) || defined(SPLIT) + + int previous = splitState_.pending; + + #endif + + if (clientStore_ -> getSplitTotalSize() == 0) + { + splitState_.pending = 0; + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitPending: SPLIT! Set the pending " + << "split flag to " << splitState_.pending + << " with split stores empty.\n" + << logofs_flush; + #endif + } + else + { + // + // Loop through the stores to find if + // there is any split that has become + // ready. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitPending: WARNING! SPLIT! Looping to " + << "find if there is any split pending.\n" + << logofs_flush; + #endif + + splitState_.pending = 0; + + T_list &splitList = splitResources_.getList(); + + for (T_list::iterator j = splitList.begin(); + j != splitList.end(); j++) + { + int resource = *j; + + SplitStore *splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore != NULL) + { + #if defined(TEST) || defined(SPLIT) + + clientStore_ -> dumpSplitStore(resource); + + #endif + + Split *splitMessage = splitStore -> getFirstSplit(); + + if (splitMessage != NULL && canSendSplit(splitMessage) == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitPending: SPLIT! Found a pending " + << "split in store [" << resource << "].\n" + << logofs_flush; + #endif + + splitState_.pending = 1; + + #if defined(TEST) || defined(SPLIT) + + if (splitMessage -> getState() == split_loaded) + { + *logofs << "handleSplitPending: PANIC! SPLIT! Found a " + << "loaded split in store [" << resource + << "].\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + break; + } + } + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitPending: SPLIT! Set the pending " + << "split flag to " << splitState_.pending + << " with " << clientStore_ -> getSplitTotalSize() + << " splits in the split stores.\n" + << logofs_flush; + #endif + } + + #if defined(TEST) || defined(SPLIT) + + if (splitState_.pending != previous) + { + *logofs << "handleSplitPending: SPLIT! Pending state " + << "changed from " << previous << " to " + << splitState_.pending << ".\n" + << logofs_flush; + } + + #endif +} + +int ClientChannel::handleSplitEvent(EncodeBuffer &encodeBuffer, Split *splitMessage) +{ + SplitStore *splitStore; + + int resource = splitMessage -> getResource(); + + #if defined(TEST) || defined(INFO) + + splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore == NULL) + { + #ifdef PANIC + *logofs << "handleSplitEvent: PANIC! The split store can't " + << "be NULL handling abort splits.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + else if (splitMessage -> getState() != split_loaded) + { + *logofs << "handleSplitEvent: PANIC! Can't find the split " + << "to be aborted.\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // Send any split that it is possible to + // abort until the store is either empty + // or the next split can't be aborted. + // + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + while ((splitStore = clientStore_ -> + getSplitStore(resource)) != NULL && + (splitMessage = splitStore -> getFirstSplit()) != NULL && + splitMessage -> getState() == split_loaded) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Aborting split with " + << "checksum [" << DumpChecksum(splitMessage -> + getChecksum()) << "] for resource " << resource + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + int any = 0; + + if (handleSplitSend(encodeBuffer, resource, any, any) < 0) + { + return -1; + } + } + + #if defined(TEST) || defined(SPLIT) + + if ((splitStore = clientStore_ -> + getSplitStore(resource)) == NULL) + { + *logofs << "handleSplitEvent: SPLIT! The split store [" + << resource << "] has been destroyed.\n" + << logofs_flush; + } + else if ((splitMessage = splitStore -> + getFirstSplit()) == NULL) + { + *logofs << "handleSplitEvent: SPLIT! The split store [" + << resource << "] is empty.\n" + << logofs_flush; + } + else if (splitMessage -> getState() != split_loaded) + { + *logofs << "handleSplitEvent: SPLIT! The split at the " + << "head of store [" << resource << "] doesn't " + << "need to be aborted.\n" << logofs_flush; + } + + #endif + + return 1; +} + +int ClientChannel::handleSplitEvent(DecodeBuffer &decodeBuffer) +{ + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Handling abort " + << "split messages for FD#" << fd_ << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + if (control -> isProtoStep7() == 0) + { + #ifdef PANIC + *logofs << "handleSplitEvent: PANIC! The split can't " + << "be aborted when connected to an old " + << "proxy version.\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + // + // Decode the information about the + // message to be updated. + // + + unsigned char resource; + + decodeBuffer.decodeCachedValue(resource, 8, + serverCache_ -> resourceCache); + + unsigned int loaded; + + decodeBuffer.decodeBoolValue(loaded); + + unsigned char request; + unsigned int size; + + if (loaded == 1) + { + decodeBuffer.decodeOpcodeValue(request, serverCache_ -> abortOpcodeCache); + + decodeBuffer.decodeValue(size, 32, 14); + } + else + { + request = 0; + size = 0; + } + + unsigned int value; + + md5_byte_t checksum[MD5_LENGTH]; + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + decodeBuffer.decodeValue(value, 8); + + checksum[i] = (unsigned char) value; + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Checking split " + << "with checksum [" << DumpChecksum(checksum) + << "] loaded " << loaded << " request " << (unsigned int) + request << " compressed size " << size << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + Split *splitMessage = handleSplitFind(checksum, resource); + + if (splitMessage != NULL) + { + if (loaded == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Marked split with " + << "checksum [" << DumpChecksum(checksum) << "] " + << "as [loaded] at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + splitMessage -> setState(split_loaded); + + #if defined(TEST) || defined(SPLIT) + + if (splitMessage -> compressedSize() != (int) size) + { + *logofs << "handleSplitEvent: WARNING! SPLIT! Updating " + << "compressed data size from " << splitMessage -> + compressedSize() << " to " << size << ".\n" + << logofs_flush; + } + + #endif + + splitMessage -> compressedSize(size); + + // + // The splits to be aborted are checked by the split + // store at the time we are going to send a new chunk + // of split data. The splits must be strictly handled + // in the same order as they were added to the split + // store and the split we want to abort here may be + // not at the head of the list. + // + + if (splitMessage == clientStore_ -> + getSplitStore(resource) -> getFirstSplit()) + { + // + // We don't need to flush this packet immediately. + // The abort can be sent at any time to the remote + // proxy. What's important is that we restart the + // agent resource as soon as possible. + // + + #if defined(TEST) || defined(SPLIT) + + T_timestamp startTs = getTimestamp(); + + *logofs << "handleSplitEvent: SPLIT! Encoding abort " + << "split events for FD#" << fd_ << " with " + << "resource " << (unsigned) resource << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0) + { + return -1; + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Spent " + << diffTimestamp(startTs, getTimestamp()) << " Ms " + << "handling abort split events for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Check if we can clear the pending flag. + // + + handleSplitPending(); + } + #if defined(TEST) || defined(SPLIT) + else + { + *logofs << "handleSplitEvent: WARNING! SPLIT! Abort split " + << "event not sent because not at the head " + << "of the list.\n" << logofs_flush; + } + #endif + } + else + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Marked split with " + << "checksum [" << DumpChecksum(checksum) << "] " + << "as [missed] at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + splitMessage -> setState(split_missed); + + // + // Check if we can set the pending flag. + // + + handleSplitPending(resource); + } + } + else + { + // + // The split report came after the split was already + // sent or the split store deleted. If the message + // had been loaded from disk by the remote side, we + // need to update the compressed size in our message + // store or the checksum will not match at the time + // we will try to save the message store on disk. + // + + if (loaded == 1 && size != 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: WARNING! SPLIT! Can't find " + << "the split. Updating in the message store.\n" + << logofs_flush; + #endif + + MessageStore *store = clientStore_ -> getRequestStore(request); + + if (store != NULL) + { + store -> updateData(checksum, size); + } + #if defined(TEST) || defined(SPLIT) + else + { + #ifdef PANIC + *logofs << "handleSplitEvent: PANIC! The message store " + << "can't be null.\n" << logofs_flush; + #endif + + HandleCleanup(); + } + #endif + } + #if defined(TEST) || defined(SPLIT) + else + { + *logofs << "handleSplitEvent: WARNING! SPLIT! No need to " + << "update the store with loaded " << loaded + << " and compressed size " << size << ".\n" + << logofs_flush; + } + #endif + } + + return 1; +} + +Split *ClientChannel::handleSplitFind(T_checksum checksum, int resource) +{ + // + // It can be that we handled all the splits, + // restarted the resource and deleted the + // store before the event could even reach + // our side. + // + + SplitStore *splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore != NULL) + { + Split *splitMessage; + + T_splits *splitList = splitStore -> getSplits(); + + for (T_splits::iterator i = splitList -> begin(); + i != splitList -> end(); i++) + { + splitMessage = (*i); + + if (splitMessage -> getChecksum() != NULL) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitFind: SPLIT! Comparing with message [" + << DumpChecksum(splitMessage -> getChecksum()) + << "].\n" << logofs_flush; + #endif + + if (memcmp(checksum, splitMessage -> getChecksum(), MD5_LENGTH) == 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitFind: SPLIT! Located split for " + << "checksum [" << DumpChecksum(checksum) << "] " + << "in store [" << splitStore -> getResource() + << "].\n" << logofs_flush; + #endif + + return splitMessage; + } + } + } + } + #if defined(TEST) || defined(SPLIT) + else + { + *logofs << "handleSplitFind: WARNING! SPLIT! The split store " + << "was already deleted.\n" << logofs_flush; + } + #endif + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitFind: WARNING! SPLIT! Can't find the " + << "split for checksum [" << DumpChecksum(checksum) + << "].\n" << logofs_flush; + #endif + + return NULL; +} + +int ClientChannel::handleRestart(T_sequence_mode mode, int resource) +{ + // + // The agent must send a start-split message, followed by the + // X messages that may be optionally split by the proxy. Usu- + // ally, in the middle of a start-split/end-split sequence is + // a single PutImage() or PutPackedImage(), that, in turn, + // can generate multiple partial requests, like a SetUnpack- + // Colormap() and SetUnpackAlpha() followed by the image that + // must be transferred. Multiple requests may be also genera- + // ted because the maximum size of a X request has been exce- + // eded, so that Xlib has divided the single image in multi- + // ple sub-image requests. The agent doesn't need to take care + // of that, except tracking the result of the split operation. + // + // By monitoring the notify events sent by the proxy, the + // agent will have to implement its own strategy to deal with + // its resources (for example its clients). For example: + // + // - It will issue a new image request and suspend a client + // if the image was not entirely sent in the main X oputput + // stream. + // + // - It will choose to commit or discard the messages after + // they are recomposed at the remote side. The set of mes- + // sages that will have to be committed will include all + // messages that were part of the split (the colormap, the + // alpha channel). + // + // - It will restart its own client, in the case it had been + // suspended. + // + // A more useful strategy would be to replace the original im- + // age with a tiny 'placeholder' if a split took place, and + // synchronize the content of the drawable at later time. This + // is generally referred as 'lazy encoding'. + // + // The agent will be able to identify the original split ope- + // ration (the one marked with the start-spit) by the small + // integer number (0-255) referred to as the 'resource' field. + // + // Before the proxy will be able to report the status of the + // split, the agent will have to close the sequence by issueing + // an end-split. The proxy will then report the result of the + // operation, so that the agent will have the option of suspend- + // ing the client or marking the drawable as dirty and take + // care of synchronizing it at later time. + // + // One of the following cases may be encountered: + // + // notify_no_split: All messages were sent in the main out- + // put stream, so that no split actually + // took place. + // + // notify_start_split: One or more messages were split, so, + // at discrection of the agent, the client + // may be suspended until the transferral + // is completed. + // + // notify_commit_split: One of the requests that made up the + // split was recomposed. The agent should + // either commit the given request or tell + // the proxy to discard it. + // + // notify_end_split: The split was duly completed. The agent + // can restart the client. + // + // notify_empty_split: No more split operation are pending. + // The agent can use this information to + // implement specific strategies requiring + // that all messages have been recomposed + // at the remote end, like updating the + // drawables that were not synchronized + // because of the lazy encoding. + // + // By checking the split and commit store we can determine if we + // need to send a new notification event to the agent. There can + // be four different cases: + // + // - If the split store is not null and not empty, we are still + // in the middle of a split. + // + // - If the commit store is not empty, we completely recomposed + // a full message and can send a new commit notify. + // + // - If the split store has become empty, we recomposed all the + // messages added for the given resource, and so will be able + // to restart the resource. + // + // - If no more messages are in the split stores, we can notify + // an empty split event to the agent. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleRestart: SPLIT! Handling [" + << (mode == sequence_immediate ? "immediate" : "deferred") + << "] restart events for resource " << resource << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + SplitStore *splitStore = clientStore_ -> getSplitStore(resource); + + if (mode == sequence_immediate) + { + // + // We have received an end-split request. If the store + // was not deleted already, we mark the last split added + // as the one ending the row for this resource. If the + // commit() function returns 0 it means that the split + // store is either empty or that we did not add any split + // for this resource. This is because when connected to + // an old proxy version we only have a single store for + // all the resources. + // + // It can happen that all the split messages that were + // originally appended to the list were completely sent + // before our client had the chance of ending the split + // sequence. In this case the split store will be empty + // or already deleted and so we will be able to restart + // the resource. + // + + #if defined(TEST) || defined(SPLIT) + + if (splitStore == NULL) + { + *logofs << "handleRestart: WARNING! SPLIT! Split store [" + << resource << "] was already deleted.\n" + << logofs_flush; + } + else + { + clientStore_ -> dumpSplitStore(resource); + } + + #endif + + if (splitStore == NULL || splitStore -> getSize() == 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleRestart: SPLIT! Immediate agent split event " + << "TYPE#" << (unsigned) opcodeStore_ -> noSplitNotify + << " [no split] with resource " << resource + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + if (handleNotify(notify_no_split, sequence_immediate, + resource, nothing, nothing) < 0) + { + return -1; + } + } + else + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleRestart: SPLIT! Immediate agent split event " + << "TYPE#" << (unsigned) opcodeStore_ -> startSplitNotify + << " [start split] with resource " << resource + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + if (handleNotify(notify_start_split, sequence_immediate, + resource, nothing, nothing) < 0) + { + return -1; + } + } + } + else + { + // + // We have completely transferred a message + // that was put in the split store. + // + // The id of the resource can be different + // than the index of the store if we are + // connected to an old proxy. + // + + #if defined(TEST) || defined(SPLIT) + + if (splitStore == NULL) + { + #ifdef PANIC + *logofs << "handleRestart: PANIC! The split store can't " + << "be NULL handling deferred restart events.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + else + { + clientStore_ -> dumpSplitStore(resource); + } + + #endif + + CommitStore *commitStore = clientStore_ -> getCommitStore(); + + #if defined(TEST) || defined(SPLIT) + + clientStore_ -> dumpCommitStore(); + + #endif + + // + // Check if there is any commit to notify. + // + + Split *split; + + T_splits *commitList = commitStore -> getSplits(); + + for (T_splits::iterator i = commitList -> begin(); + i != commitList -> end(); i++) + { + split = *i; + + if (split -> getState() != split_notified) + { + #if defined(TEST) || defined(SPLIT) + + if (split -> getResource() != resource) + { + #ifdef PANIC + *logofs << "handleSplitSend: PANIC! The resource doesn't " + << "match the split store.\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + #endif + + int request = split -> getRequest(); + int position = split -> getPosition(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleRestart: SPLIT! Deferred agent split event " + << "TYPE#" << (unsigned) opcodeStore_ -> commitSplitNotify + << " [commit split] with resource " << resource << " request " + << request << " position " << position << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + if (handleNotify(notify_commit_split, sequence_deferred, + resource, request, position) < 0) + { + return -1; + } + + // + // Don't send the notification again. + // + + split -> setState(split_notified); + } + #if defined(TEST) || defined(SPLIT) + else + { + *logofs << "handleRestart: SPLIT! Split for request " + << split -> getRequest() << " and position " + << split -> getPosition() << " was already " + << "notified.\n" << logofs_flush; + } + #endif + } + + // + // Don't send the end split if we are still + // in the middle of a start-split/end-split + // sequence. We'll send a no-split at the + // time the end-split is received. + // + + if (splitStore -> getSize() == 0 && + splitStore -> getResource() != splitState_.resource) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleRestart: SPLIT! Deferred agent split event " + << "TYPE#" << (unsigned) opcodeStore_ -> endSplitNotify + << " [end split] with resource " << resource << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + if (handleNotify(notify_end_split, sequence_deferred, + resource, nothing, nothing) < 0) + { + return -1; + } + } + #if defined(TEST) || defined(SPLIT) + else if (splitStore -> getSize() == 0 && + splitStore -> getResource() == splitState_.resource) + { + *logofs << "handleRestart: SPLIT! WARNING! The split store " + << "for resource " << resource << " was emptied in the " + << "split sequence at " << strMsTimestamp() << ".\n" + << logofs_flush; + } + #endif + } + + // + // Remove the split store if it's empty. + // + + if (splitStore != NULL && splitStore -> getSize() == 0 && + splitStore -> getResource() != splitState_.resource) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleRestart: SPLIT! Removing the split store [" + << resource << "] at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + handleSplitStoreRemove(&splitResources_, resource); + + if (clientStore_ -> getSplitTotalSize() == 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleRestart: SPLIT! Deferred agent split event " + << "TYPE#" << (unsigned) opcodeStore_ -> emptySplitNotify + << " [empty split] for FD#" << fd_ << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + if (handleNotify(notify_empty_split, sequence_deferred, + nothing, nothing, nothing) < 0) + { + return -1; + } + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleRestart: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + if ((clientStore_ -> getSplitTotalSize() != 0 && + clientStore_ -> getSplitTotalStorageSize() == 0) || + (clientStore_ -> getSplitTotalSize() == 0 && + clientStore_ -> getSplitTotalStorageSize() != 0)) + { + #ifdef PANIC + *logofs << "handleRestart: PANIC! Inconsistency detected " + << "while handling the split stores.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + + #endif + } + + return 1; +} + +int ClientChannel::handleTaintCacheRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size) +{ + #ifdef TEST + *logofs << "handleTaintCacheRequest: Tainting cache request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + // + // The save and load flags would affect + // the decoding side but the decoding + // side doesn't support the request. + // + + enableCache_ = *(buffer + 4); + enableSplit_ = *(buffer + 5); + + handleSplitEnable(); + + #ifdef TEST + *logofs << "handleTaintCacheRequest: Set cache parameters to " + << "cache " << enableCache_ << " split " << enableSplit_ + << " load " << enableLoad_ << " save " << enableSave_ + << ".\n" << logofs_flush; + #endif + + // + // Taint the request to a X_NoOperation. + // + + opcode = X_NoOperation; + + return 0; +} + +int ClientChannel::handleTaintFontRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size) +{ + // + // The remote end doesn't support this + // request so generate an empty reply + // at the local side. + // + + #ifdef TEST + *logofs << "handleTaintFontRequest: Suppressing font " + << "request for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + // + // The client sequence number has not + // been incremented yet in the loop. + // + + unsigned int sequence = (clientSequence_ + 1) & 0xffff; + + #ifdef TEST + *logofs << "handleTaintFontRequest: Opcode is " << (unsigned) opcode + << " expected client sequence is " << sequence + << ".\n" << logofs_flush; + #endif + + unsigned char *reply = writeBuffer_.addMessage(36); + + *(reply + 0) = X_Reply; + + PutUINT(sequence, reply + 2, bigEndian_); + + PutULONG(1, reply + 4, bigEndian_); + + // + // Set the length of the returned + // path to 0. + // + + *(reply + 32) = 0; + + // + // Save the sequence number, not incremented + // yet, we used to auto-generate this reply. + // + + lastSequence_ = clientSequence_ + 1; + + #ifdef TEST + *logofs << "handleTaintFontRequest: Registered " << lastSequence_ + << " as last auto-generated sequence number.\n" + << logofs_flush; + #endif + + // + // Taint the request to a X_NoOperation. + // + + opcode = X_NoOperation; + + if (handleFlush(flush_if_any) < 0) + { + return -1; + } + + return 1; +} + +int ClientChannel::handleTaintSplitRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size) +{ + #ifdef TEST + + if (opcode == opcodeStore_ -> abortSplit) + { + *logofs << "handleTaintSplitRequest: Tainting abort split " + << "request for FD#" << fd_ << ".\n" + << logofs_flush; + } + else if (opcode == opcodeStore_ -> finishSplit) + { + *logofs << "handleTaintSplitRequest: Tainting finish split " + << "request for FD#" << fd_ << ".\n" + << logofs_flush; + } + else + { + *logofs << "handleTaintSplitRequest: Tainting free split " + << "request for FD#" << fd_ << ".\n" + << logofs_flush; + } + + #endif + + // + // Taint the request to a X_NoOperation. + // + + opcode = X_NoOperation; + + return 1; +} + +int ClientChannel::handleTaintLameRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size) +{ + // + // Test the efficiency of the encoding + // without these RENDER requests. + // + + if (opcode == opcodeStore_ -> renderExtension && + (*(buffer + 1) == X_RenderCompositeGlyphs8 || + *(buffer + 1) == X_RenderCompositeGlyphs16 || + *(buffer + 1) == X_RenderCompositeGlyphs32 || + *(buffer + 1) == X_RenderAddGlyphs || + *(buffer + 1) == X_RenderTrapezoids)) + { + #ifdef TEST + *logofs << "handleTaintLameRequest: Tainting request " + << "OPCODE#" << (unsigned int) opcode << " MINOR#" + << (unsigned int) *(buffer + 1) << " for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + opcode = X_NoOperation; + + return 1; + } + + return 0; +} + +int ClientChannel::handleTaintSyncRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size) +{ + // + // Should short-circuit other common replies + // whose values could be queried only once. + // Examples are X_InterAtom, X_ListExtension + // and X_QueryExtension. + // + + if (taintCounter_ >= control -> TaintThreshold) + { + #ifdef DEBUG + *logofs << "handleTaintSyncRequest: Reset taint counter after " + << taintCounter_ << " replies managed.\n" + << logofs_flush; + #endif + + taintCounter_ = 0; + + return 0; + } + + // + // Check if we are rolling the counter. + // The client sequence number has not + // been incremented yet in the loop. + // + + unsigned int sequence = (clientSequence_ + 1) & 0xffff; + + #ifdef DEBUG + *logofs << "handleTaintSyncRequest: Opcode is " << (unsigned) opcode + << " expected client sequence is " << sequence + << ".\n" << logofs_flush; + #endif + + if (sequence == 0xffff) + { + return 0; + } + + unsigned short t1; + unsigned char t2; + + // + // Check if there is a previous reply + // pending. + // + + if (sequenceQueue_.peek(t1, t2) != 0) + { + #ifdef DEBUG + *logofs << "handleTaintSyncRequest: Skipping taint of reply due to " + << "pending request OPCODE#" << t1 << " with sequence " + << (unsigned int) t2 << ".\n" << logofs_flush; + #endif + + return 0; + } + + #ifdef DEBUG + *logofs << "handleTaintSyncRequest: Suppressing get input focus " + << "request for FD#" << fd_ << " with sequence " + << sequence << ".\n" << logofs_flush; + #endif + + unsigned char *reply = writeBuffer_.addMessage(32); + + *(reply + 0) = X_Reply; + + PutUINT(sequence, reply + 2, bigEndian_); + + PutULONG(0, reply + 4, bigEndian_); + + // + // Set revert-to to none. + // + + *(reply + 1) = 0; + + // + // Set focus to none. + // + + PutULONG(0, reply + 8, bigEndian_); + + // + // Save the sequence number, not incremented + // yet, we used to auto-generate this reply. + // + + lastSequence_ = clientSequence_ + 1; + + #ifdef TEST + *logofs << "handleTaintSyncRequest: Registered " << lastSequence_ + << " as last auto-generated sequence number.\n" + << logofs_flush; + #endif + + // + // Taint the request to a X_NoOperation. + // + + opcode = X_NoOperation; + + // + // We may assume that the client has finished + // drawing and flush immediately, even if this + // seems to perceively affect the performance. + // + // priority_++; + // + + if (handleFlush(flush_if_any) < 0) + { + return -1; + } + + taintCounter_++; + + return 1; +} + +int ClientChannel::handleTaintSyncError(unsigned char opcode) +{ + if (control -> TaintReplies > 0) + { + // + // By enabling short-circuiting of replies + // some window managers can get confused + // by some otherwise innocuous X errors. + // + + if (opcode == X_GrabKey || opcode == X_ReparentWindow || + opcode == X_ConfigureWindow) + { + #if defined(TEST) || defined(OPCODES) + *logofs << "handleTaintSyncError: WARNING! Suppressed error " + << "on OPCODE#" << (unsigned int) opcode << " for FD#" + << fd_ << " sequence " << lastSequence_ << " (real was " + << serverSequence_ << ").\n" << logofs_flush; + #endif + + return 1; + } + } + + return 0; +} + +int ClientChannel::handleNotify(T_notification_type type, T_sequence_mode mode, + int resource, int request, int position) +{ + if (finish_ == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleNotify: Discarding notification on " + << "channel for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + return 0; + } + + // + // Add a new message to the write buffer. + // + + unsigned char *event = writeBuffer_.addMessage(32); + + // + // Event is ClientMessage, atom and + // window are 0, format is 32. + // + + *(event + 0) = ClientMessage; + + PutULONG(0, event + 4, bigEndian_); + PutULONG(0, event + 8, bigEndian_); + + *(event + 1) = 32; + + // + // If the event follows immediately the request (that is the + // sequence mode is 'immediate') then the sequence number is + // the one of the last request, else it should be the last + // sequence number encoded by peer proxy but, as we are ins- + // erting events in the stream, we must ensure that the se- + // quence we send is not less than the last sequence we have + // auto-generated. + // + + if (mode == sequence_immediate) + { + // + // Save the sequence number we used + // to auto-generate this event. + // + + lastSequence_ = clientSequence_; + + #if defined(TEST) || defined(INFO) + *logofs << "handleNotify: Registered " << lastSequence_ + << " as last auto-generated sequence number.\n" + << logofs_flush; + #endif + } + else + { + if (serverSequence_ > lastSequence_) + { + #ifdef DEBUG + *logofs << "handleNotify: Updating last event's sequence " + << lastSequence_ << " to X server's sequence number " + << serverSequence_ << " for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + lastSequence_ = serverSequence_; + } + #ifdef DEBUG + else if (serverSequence_ < lastSequence_) + { + // + // Use our last auto-generated sequence. + // + + *logofs << "handleNotify: Tainting sequence number " + << serverSequence_ << " to last event's sequence " + << lastSequence_ << " for FD#" << fd_ << ".\n" + << logofs_flush; + } + #endif + } + + PutUINT(lastSequence_, event + 2, bigEndian_); + + // + // Be sure we set to void the fields that + // are not significant for the specific + // notification message. + // + + PutULONG(nothing, event + 16, bigEndian_); + PutULONG(nothing, event + 20, bigEndian_); + PutULONG(nothing, event + 24, bigEndian_); + + switch (type) + { + case notify_no_split: + { + PutULONG(opcodeStore_ -> noSplitNotify, + event + 12, bigEndian_); + + PutULONG(resource, event + 16, bigEndian_); + + break; + } + case notify_start_split: + { + PutULONG(opcodeStore_ -> startSplitNotify, + event + 12, bigEndian_); + + PutULONG(resource, event + 16, bigEndian_); + + break; + } + case notify_commit_split: + { + PutULONG(opcodeStore_ -> commitSplitNotify, + event + 12, bigEndian_); + + PutULONG(resource, event + 16, bigEndian_); + + PutULONG(request, event + 20, bigEndian_); + + PutULONG(position, event + 24, bigEndian_); + + break; + } + case notify_end_split: + { + PutULONG(opcodeStore_ -> endSplitNotify, + event + 12, bigEndian_); + + PutULONG(resource, event + 16, bigEndian_); + + break; + } + case notify_empty_split: + { + PutULONG(opcodeStore_ -> emptySplitNotify, + event + 12, bigEndian_); + break; + } + default: + { + #ifdef PANIC + *logofs << "handleNotify: PANIC! Unrecognized notify " + << "TYPE#" << type << ".\n" + << logofs_flush; + #endif + + return -1; + } + } + + #if defined(TEST) || defined(INFO) || defined (SPLIT) + + *logofs << "handleNotify: Sending " + << (mode == sequence_immediate ? "immediate " : "deferred ") + << "agent notify event TYPE#" << GetULONG(event + 12, bigEndian_) + << logofs_flush; + + if (resource != nothing) + { + *logofs << " with resource " << GetULONG(event + 16, bigEndian_) + << logofs_flush; + + if (request != nothing && position != nothing) + { + *logofs << " request " << GetULONG(event + 20, bigEndian_) + << " position " << GetULONG(event + 24, bigEndian_) + << logofs_flush; + } + } + + *logofs << ".\n" << logofs_flush; + + #endif + + // + // Send the notification now. + // + + if (handleFlush(flush_if_any) < 0) + { + return -1; + } + + return 1; +} + +int ClientChannel::handleCommitSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + // + // Get the data of the request to be + // committed. + // + + unsigned char request = *(buffer + 5); + + MessageStore *store = clientStore_ -> getRequestStore(request); + + if (store == NULL) + { + #ifdef PANIC + *logofs << "handleCommitSplitRequest: PANIC! Can't commit split for " + << "request OPCODE#" << (unsigned int) request + << ". No message store found.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't commit split for request " + << "OPCODE#" << (unsigned int) request + << ". No message store found.\n"; + + return -1; + } + + // + // The position in cache of the message + // to commit. Encode it as difference in + // respect to the last encoded value. + // + + unsigned int position = GetULONG(buffer + 8, bigEndian_); + + unsigned char resource = *(buffer + 1); + unsigned int commit = *(buffer + 4); + + #if defined(TEST) || defined(SPLIT) + + if (commit == 1) + { + *logofs << "handleCommitSplitRequest: SPLIT! Committing request " + << "OPCODE#" << (unsigned) request << " at position " + << position << " for FD#" << fd_ << " with resource " + << (unsigned) resource << ".\n" << logofs_flush; + } + else + { + *logofs << "handleCommitSplitRequest: SPLIT! Discarding request " + << "OPCODE#" << (unsigned) request << " at position " + << position << " for FD#" << fd_ << " with resource " + << (unsigned) resource << ".\n" << logofs_flush; + } + + #endif + + encodeBuffer.encodeOpcodeValue(request, clientCache_ -> opcodeCache); + + int diffCommit = position - splitState_.commit; + + splitState_.commit = position; + + encodeBuffer.encodeValue(diffCommit, 32, 5); + + // + // Send the resource id and the commit + // flag. + // + + encodeBuffer.encodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + encodeBuffer.encodeBoolValue(commit); + + // + // Remove the split from the split queue. + // + + Split *split = handleSplitCommitRemove(request, resource, splitState_.commit); + + if (split == NULL) + { + return -1; + } + + clientStore_ -> getCommitStore() -> update(split); + + // + // Free the split. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleCommitSplitRequest: SPLIT! Freeing up the " + << "committed split.\n" << logofs_flush; + #endif + + delete split; + + return 1; +} + +int ClientChannel::handleAbortSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + unsigned char resource = *(buffer + 1); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Handling abort split " + << "request for FD#"<< fd_ << " and resource " + << (unsigned int) resource << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + SplitStore *splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore == NULL) + { + #ifdef WARNING + *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The split " + << "store [" << (unsigned int) resource << "] " + << "is already empty.\n" << logofs_flush; + #endif + + return 0; + } + + // + // Loop through the messages in the split + // store and discard from the memory cache + // the messages that are still incomplete. + // Then remove the message from the split + // store. + // + + #if defined(TEST) || defined(SPLIT) + + clientStore_ -> dumpSplitStore(resource); + + #endif + + int splits = 0; + + Split *splitMessage; + + for (;;) + { + splitMessage = splitStore -> getFirstSplit(); + + if (splitMessage == NULL) + { + // + // Check if we had created the store + // but no message was added yet. + // + + #ifdef WARNING + + if (splits == 0) + { + *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The " + << "split store [" << (unsigned int) resource + << "] is unexpectedly empty.\n" + << logofs_flush; + } + + #endif + + break; + } + + // + // Splits already aborted can't be in the + // split store. + // + + #if defined(TEST) || defined(SPLIT) + + if (splitMessage -> getState() == split_aborted) + { + *logofs << "handleAbortSplitRequest: PANIC! SPLIT! Found an " + << "aborted split in store [" << (unsigned int) resource + << "].\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + if (splitMessage -> getAction() == IS_HIT) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Removing the " + << "split from the memory cache.\n" + << logofs_flush; + #endif + + splitMessage -> getStore() -> remove(splitMessage -> getPosition(), + use_checksum, discard_data); + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Removing the " + << "split from the split store.\n" + << logofs_flush; + #endif + + splitMessage = splitStore -> pop(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Freeing up the " + << "aborted split.\n" << logofs_flush; + #endif + + delete splitMessage; + + splits++; + } + + // + // If the start-split/end-split sequence + // was closed, send the notification now, + // else wait for the end-split. + // + + if (resource != splitState_.resource) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Sending the " + << "deferred [end split] event.\n" + << logofs_flush; + #endif + + handleRestart(sequence_deferred, resource); + } + #if defined(TEST) || defined(SPLIT) + else + { + *logofs << "handleAbortSplitRequest: WARNING! SPLIT! Still " + << "waiting for the closure of the split " + << "sequence.\n" << logofs_flush; + } + #endif + + // + // Check if there is any other store + // having splits to send. + // + + handleSplitPending(); + + return (splits > 0); +} + +int ClientChannel::handleFinishSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + unsigned char resource = *(buffer + 1); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleFinishSplitRequest: SPLIT! Handling finish split " + << "request for FD#"<< fd_ << " and resource " + << (unsigned int) resource << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + // + // We need to get the protocol statistics + // for the finish message we are handling + // here because sending a new split will + // reset the bits counter. + // + + int bits = encodeBuffer.diffBits(); + + statistics -> addRequestBits(opcode, size << 3, bits); + + SplitStore *splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore == NULL) + { + #ifdef WARNING + *logofs << "handleFinishSplitRequest: WARNING! SPLIT! The split " + << "store [" << (unsigned int) resource << "] " + << "is already empty.\n" << logofs_flush; + #endif + + return 0; + } + + // + // Send all the split queued for the given + // resource until the split store becomes + // empty. + // + + #if defined(TEST) || defined(SPLIT) + + clientStore_ -> dumpSplitStore(resource); + + #endif + + Split *splitMessage; + + int total = MESSAGE_DATA_LIMIT; + + int bytes = total; + int splits = 0; + + for (;;) + { + splitMessage = splitStore -> getFirstSplit(); + + if (splitMessage == NULL) + { + // + // We have presumably created the store + // after a start split but no message + // was added yet. + // + + #ifdef WARNING + *logofs << "handleFinishSplitRequest: WARNING! SPLIT! The " + << "split store [" << (unsigned int) resource + << "] is unexpectedly empty.\n" + << logofs_flush; + #endif + + break; + } + + // + // Splits already aborted can't be in the + // split store. + // + + #if defined(TEST) || defined(SPLIT) + + if (splitMessage -> getState() == split_aborted) + { + *logofs << "handleFinishSplitRequest: PANIC! SPLIT! Found an " + << "aborted split in store [" << (unsigned int) resource + << "].\n" << logofs_flush; + + HandleCleanup(); + } + + *logofs << "handleFinishSplitRequest: SPLIT! Sending more " + << "data for store [" << (unsigned int) resource + << "].\n" << logofs_flush; + #endif + + if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0) + { + return -1; + } + + // + // Check if the split store was deleted. + // + + if (clientStore_ -> getSplitStore(resource) == NULL) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleFinishSplitRequest: SPLIT! Exiting " + << "from the finish loop with split store [" + << (unsigned int) resource << "] destroyed.\n" + << logofs_flush; + #endif + + break; + } + } + + // + // Check if there is any other store + // having splits to send. + // + + handleSplitPending(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleFinishSplitRequest: SPLIT! Sent " << splits + << " splits and " << total - bytes << " bytes for FD#" << fd_ + << " with " << clientStore_ -> getSplitTotalStorageSize() + << " bytes and [" << clientStore_ -> getSplitTotalSize() + << "] splits remaining.\n" << logofs_flush; + #endif + + return (splits > 0); +} + +int ClientChannel::handleConfiguration() +{ + #ifdef TEST + *logofs << "ClientChannel: Setting new buffer parameters.\n" + << logofs_flush; + #endif + + readBuffer_.setSize(control -> ClientInitialReadSize, + control -> ClientMaximumBufferSize); + + writeBuffer_.setSize(control -> TransportXBufferSize, + control -> TransportXBufferThreshold, + control -> TransportMaximumBufferSize); + + transport_ -> setSize(control -> TransportXBufferSize, + control -> TransportXBufferThreshold, + control -> TransportMaximumBufferSize); + + return 1; +} + +int ClientChannel::handleFinish() +{ + #ifdef TEST + *logofs << "ClientChannel: Finishing channel for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + congestion_ = 0; + priority_ = 0; + + finish_ = 1; + + taintCounter_ = 0; + + splitState_.resource = nothing; + splitState_.pending = 0; + splitState_.commit = 0; + splitState_.mode = split_none; + + transport_ -> finish(); + + return 1; +} + +// +// If differential compression is disabled then use the +// most simple encoding but handle the image requests +// and the X_ListExtensions and X_QueryExtension messa- +// ges (needed to detect the opcode of the shape or the +// other extensions) in the usual way. +// + +int ClientChannel::handleFastReadRequest(EncodeBuffer &encodeBuffer, const unsigned char &opcode, + const unsigned char *&buffer, const unsigned int &size) +{ + // + // All the NX requests are handled in the + // main message loop. The X_PutImage can + // be handled here only if the split was + // not requested. + // + + if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) || + (control -> isProtoStep7() == 1 && opcode == X_PutImage && + splitState_.resource != nothing) || opcode == X_ListExtensions || + opcode == X_QueryExtension) + { + return 0; + } + + #ifdef DEBUG + *logofs << "handleFastReadRequest: Encoding raw request OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << " with size " << size << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeMemory(buffer, size); + + // + // Put request on the fast track + // if it needs a reply. + // + + switch (opcode) + { + case X_GetAtomName: + case X_GetGeometry: + case X_GetInputFocus: + case X_GetModifierMapping: + case X_GetKeyboardMapping: + case X_GetProperty: + case X_GetSelectionOwner: + case X_GrabPointer: + case X_GrabKeyboard: + case X_ListExtensions: + case X_ListFonts: + case X_LookupColor: + case X_AllocNamedColor: + case X_QueryPointer: + case X_GetWindowAttributes: + case X_QueryTree: + case X_QueryBestSize: + case X_QueryColors: + case X_QueryFont: + case X_TranslateCoords: + case X_GetImage: + case X_GetPointerMapping: + case X_GetKeyboardControl: + case X_InternAtom: + case X_AllocColor: + { + sequenceQueue_.push(clientSequence_, opcode); + + priority_++; + + break; + } + default: + { + break; + } + } + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + *logofs << "handleFastReadRequest: Handled raw request OPCODE#" + << (unsigned int) opcode << " (" << DumpOpcode(opcode) << ")" + << " for FD#" << fd_ << " sequence " << clientSequence_ + << ". " << size << " bytes in, " << bits << " bits (" + << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; + + #endif + + statistics -> addRequestBits(opcode, size << 3, bits); + + if (opcode == opcodeStore_ -> renderExtension) + { + statistics -> addRenderRequestBits(*(buffer + 1), size << 3, bits); + } + + return 1; +} + +int ClientChannel::handleFastWriteReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + if ((opcode >= X_NXFirstOpcode && + opcode <= X_NXLastOpcode) || + opcode == X_ListExtensions || + opcode == X_QueryExtension) + { + return 0; + } + + #ifdef DEBUG + *logofs << "handleFastWriteReply: Decoding raw reply OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + buffer = writeBuffer_.addMessage(8); + + #ifndef __sun + + unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(8); + + *((unsigned int *) buffer) = *next++; + *((unsigned int *) (buffer + 4)) = *next; + + #else /* #ifndef __sun */ + + memcpy(buffer, decodeBuffer.decodeMemory(8), 8); + + #endif /* #ifndef __sun */ + + size = 32 + (GetULONG(buffer + 4, bigEndian_) << 2); + + writeBuffer_.registerPointer(&buffer); + + if (writeBuffer_.getAvailable() < size - 8 || + (int) size >= control -> TransportFlushBufferSize) + { + #ifdef DEBUG + *logofs << "handleFastWriteReply: Using scratch buffer for OPCODE#" + << (unsigned int) opcode << " with size " << size << " and " + << writeBuffer_.getLength() << " bytes in buffer.\n" + << logofs_flush; + #endif + + writeBuffer_.removeMessage(8); + + buffer = writeBuffer_.addScratchMessage(((unsigned char *) + decodeBuffer.decodeMemory(size - 8)) - 8, size); + } + else + { + writeBuffer_.addMessage(size - 8); + + #ifndef __sun + + if (size == 32) + { + next = (unsigned int *) decodeBuffer.decodeMemory(size - 8); + + for (int i = 8; i < 32; i += sizeof(unsigned int)) + { + *((unsigned int *) (buffer + i)) = *next++; + } + } + else + { + memcpy(buffer + 8, decodeBuffer.decodeMemory(size - 8), size - 8); + } + + #else /* #ifndef __sun */ + + memcpy(buffer + 8, decodeBuffer.decodeMemory(size - 8), size - 8); + + #endif /* #ifndef __sun */ + } + + writeBuffer_.unregisterPointer(); + + // + // We don't need to write our local sequence + // number. Replies are always sent with the + // original X server's sequence number. + // + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleFastWriteReply: Handled raw reply OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ << " with sequence " + << serverSequence_ << ". Output size is " << size << ".\n" + << logofs_flush; + #endif + + #ifdef DEBUG + *logofs << "handleFastWriteReply: Length of sequence queue is " + << sequenceQueue_.length() << ".\n" << logofs_flush; + #endif + + statistics -> addRepliedRequest(opcode); + + handleFlush(flush_if_needed); + + return 1; +} + +int ClientChannel::handleFastWriteEvent(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + #ifdef DEBUG + *logofs << "handleFastWriteEvent: Decoding raw " + << (opcode == X_Error ? "error" : "event") << " OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + size = 32; + + buffer = writeBuffer_.addMessage(size); + + #ifndef __sun + + unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(size); + + for (int i = 0; i < 32; i += sizeof(unsigned int)) + { + *((unsigned int *) (buffer + i)) = *next++; + } + + #else /* #ifndef __sun */ + + memcpy(buffer, decodeBuffer.decodeMemory(size), size); + + #endif /* #ifndef __sun */ + + // + // Use our local sequence number. + // + + PutUINT(lastSequence_, buffer + 2, bigEndian_); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleFastWriteEvent: Handled raw " + << (opcode == X_Error ? "error" : "event") << " OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ << " with sequence " + << lastSequence_ << ". Output size is " << size << ".\n" + << logofs_flush; + #endif + + // + // Check if we need to suppress the error. + // + + if (opcode == X_Error && handleTaintSyncError(*(buffer + 10)) > 0) + { + #if defined(TEST) || defined(OPCODES) + *logofs << "handleFastWriteEvent: WARNING! Suppressed error OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << " with sequence " << lastSequence_ << ".\n" + << logofs_flush; + #endif + + writeBuffer_.removeMessage(32); + } + + handleFlush(flush_if_needed); + + return 1; +} + +int ClientChannel::handleShmemRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + // + // Will push sequence and set + // priority according to stage. + // + + unsigned int stage = *(buffer + 1); + + #ifdef TEST + *logofs << "handleShmemRequest: Encoding shmem request " + << "OPCODE#" << (unsigned int) opcode << " for FD#" + << fd_ << " with size " << size << " at stage " + << stage << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeValue(stage, 2); + + if (stage == 0) + { + unsigned int enableClient = 0; + unsigned int enableServer = 0; + + if (control -> ShmemClient == 1) + { + enableClient = *(buffer + 4); + } + + if (control -> ShmemServer == 1) + { + enableServer = *(buffer + 5); + } + + encodeBuffer.encodeBoolValue(enableClient); + encodeBuffer.encodeBoolValue(enableServer); + + unsigned int clientSegment = GetULONG(buffer + 8, bigEndian_); + unsigned int serverSegment = GetULONG(buffer + 12, bigEndian_); + + encodeBuffer.encodeValue(clientSegment, 29, 9); + encodeBuffer.encodeValue(serverSegment, 29, 9); + + #ifdef TEST + *logofs << "handleShmemRequest: Enable client is " + << enableClient << " enable server is " << enableServer + << " client segment is " << (void *) clientSegment + << " server segment is " << (void *) serverSegment + << ".\n" << logofs_flush; + #endif + + #ifdef TEST + *logofs << "handleShmemRequest: Size of the shared memory " + << "segment will be " << control -> ShmemServerSize + << ".\n" << logofs_flush; + #endif + } + + if (stage != 1) + { + sequenceQueue_.push(clientSequence_, opcodeStore_ -> + getShmemParameters); + + priority_++; + } + + return 1; +} + +int ClientChannel::handleShmemReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + #ifdef TEST + *logofs << "handleShmemReply: Received shmem parameters " + << "reply OPCODE#" << (unsigned int) opcode + << ".\n" << logofs_flush; + #endif + + size = 32; + buffer = writeBuffer_.addMessage(size); + + unsigned int stage; + + decodeBuffer.decodeValue(stage, 2); + + *(buffer + 1) = stage; + + if (stage == 2) + { + unsigned int clientEnabled; + unsigned int serverEnabled; + + decodeBuffer.decodeBoolValue(clientEnabled); + decodeBuffer.decodeBoolValue(serverEnabled); + + // + // Client support is not implemented + // and not useful. It is here only + // for compatibility. + // + + clientEnabled = 0; + + *(buffer + 8) = clientEnabled; + *(buffer + 9) = serverEnabled; + + PutULONG(0, buffer + 12, bigEndian_); + + if (serverEnabled == 1) + { + #ifdef TEST + *logofs << "handleShmemReply: Enabled shared memory " + << "support in X server with segment size " + << control -> ShmemServerSize << ".\n" + << logofs_flush; + #endif + + PutULONG(control -> ShmemServerSize, buffer + 16, bigEndian_); + } + else + { + PutULONG(0, buffer + 16, bigEndian_); + } + } + else + { + *(buffer + 8) = 0; + *(buffer + 9) = 0; + + PutULONG(0, buffer + 12, bigEndian_); + PutULONG(0, buffer + 16, bigEndian_); + } + + return 1; +} + +int ClientChannel::handleFontRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + #ifdef TEST + *logofs << "handleFontRequest: Encoding font request " + << "OPCODE#" << (unsigned int) opcode << " for FD#" + << fd_ << " with size " << size << ".\n" + << logofs_flush; + #endif + + sequenceQueue_.push(clientSequence_, opcodeStore_ -> + getFontParameters); + + return 1; +} + +int ClientChannel::handleFontReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + #ifdef TEST + *logofs << "handleFontReply: Received font operation " + << "reply OPCODE#" << (unsigned int) opcode + << ".\n" << logofs_flush; + #endif + + unsigned int length; + + decodeBuffer.decodeValue(length, 8); + + size = 32 + RoundUp4(length + 1); + buffer = writeBuffer_.addMessage(size); + + unsigned char *next = buffer + 32; + + *next++ = length; + + decodeBuffer.decodeTextData(next, length); + + #ifdef TEST + + *logofs << "handleFontReply: Received tunneled font server " + << "path '"; + + for (unsigned int i = 0; i < length; i++) + { + *logofs << *(buffer + 32 + 1 + i); + } + + *logofs << "' for FD#" << fd_ << ".\n" << logofs_flush; + + #endif + + if (fontPort_ == -1) + { + // + // The local side is not going to forward + // the font server connections. + // + + #ifdef TEST + *logofs << "handleFontReply: WARNING! Returning an empty " + << "font server path.\n" << logofs_flush; + #endif + + writeBuffer_.removeMessage(size); + + size = 36; + buffer = writeBuffer_.addMessage(size); + + // + // Set the length of the returned + // path to 0. + // + + *(buffer + 32) = 0; + } + #ifdef TEST + else + { + *logofs << "handleFontReply: Returning the received " + << "font server path.\n" << logofs_flush; + } + #endif + + return 1; +} + +int ClientChannel::handleCacheRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + #ifdef TEST + *logofs << "handleCacheRequest: Handling cache request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + enableCache_ = *(buffer + 4); + enableSplit_ = *(buffer + 5); + enableSave_ = *(buffer + 6); + enableLoad_ = *(buffer + 7); + + handleSplitEnable(); + + #ifdef TEST + *logofs << "handleCacheRequest: Set cache parameters to " + << " cache " << enableCache_ << " split " << enableSplit_ + << " save " << enableSave_ << " load " << enableLoad_ + << ".\n" << logofs_flush; + #endif + + // + // Encode all the parameters as a + // single unsigned int so we can + // use an int cache. + // + + unsigned int mask = enableSave_ << 8 | enableLoad_; + + encodeBuffer.encodeCachedValue(mask, 32, clientCache_ -> + setCacheParametersCache); + return 0; +} + +int ClientChannel::handleStartSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + #if defined(TEST) || defined(SPLIT) + *logofs << "handleStartSplitRequest: SPLIT! Handling start split " + << "request for FD#"<< fd_ << ".\n" << logofs_flush; + #endif + + if (splitState_.resource != nothing) + { + #ifdef PANIC + *logofs << "handleStartSplitRequest: PANIC! SPLIT! Split requested " + << "for resource id " << (unsigned int) *(buffer + 1) + << " while handling resource " << splitState_.resource + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Split requested for " + << "resource id " << (unsigned int) *(buffer + 1) + << " while handling resource " << splitState_.resource + << ".\n"; + + return -1; + } + else if (fd_ != firstClient_) + { + // + // It can be that an auxiliary channel is the + // first to connect, then comes the agent that + // is actually using the NX opcodes. + // + + #ifdef WARNING + *logofs << "handleStartSplitRequest: WARNING SPLIT! Split requested " + << "on FD#" << fd_ << " while expecting FD#" << firstClient_ + << ".\n" << logofs_flush; + #endif + + firstClient_ = fd_; + } + + // + // Set the agent's resource for which we are + // going to split the request. + // + + splitState_.resource = *(buffer + 1); + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleStartSplitRequest: SPLIT! Registered id " + << splitState_.resource << " as resource " + << "waiting for a split.\n" << logofs_flush; + + if (clientStore_ -> getSplitStore(splitState_.resource) != NULL) + { + *logofs << "handleStartSplitRequest: WARNING! SPLIT! A split " + << "store for resource id " << splitState_.resource + << " already exists.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.resource); + } + + #endif + + // + // Send the selected resource to the remote. + // + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeCachedValue(splitState_.resource, 8, + clientCache_ -> resourceCache); + } + + splitState_.mode = (T_split_mode) *(buffer + 4); + + if (splitState_.mode != NXSplitModeAsync && + splitState_.mode != NXSplitModeSync) + { + splitState_.mode = (T_split_mode) control -> SplitMode; + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleStartSplitRequest: SPLIT! Set split " + << "mode to '" << splitState_.mode << "' with " + << "provided value '" << (unsigned) *(buffer + 4) + << "'.\n" << logofs_flush; + #endif + } + + #if defined(TEST) || defined(SPLIT) + + if (splitState_.mode == NXSplitModeAsync) + { + *logofs << "handleStartSplitRequest: SPLIT! Selected split " + << "mode is [split_async].\n" << logofs_flush; + } + else if (splitState_.mode == NXSplitModeSync) + { + *logofs << "handleStartSplitRequest: SPLIT! Selected split " + << "mode is [split_sync].\n" << logofs_flush; + } + + clientStore_ -> dumpSplitStores(); + + #endif + + return 1; +} + +int ClientChannel::handleEndSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + #if defined(TEST) || defined(SPLIT) + *logofs << "handleEndSplitRequest: SPLIT! Handling end split " + << "request for FD#"<< fd_ << ".\n" << logofs_flush; + #endif + + // + // Verify that the agent resource matches. + // + + if (splitState_.resource == nothing) + { + #ifdef PANIC + *logofs << "handleEndSplitRequest: PANIC! SPLIT! Received an end of " + << "split for resource id " << (unsigned int) *(buffer + 1) + << " without a previous start.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Received an end of split " + << "for resource id " << (unsigned int) *(buffer + 1) + << " without a previous start.\n"; + + return -1; + } + else if (splitState_.resource != *(buffer + 1)) + { + #ifdef PANIC + *logofs << "handleEndSplitRequest: PANIC! SPLIT! Invalid resource id " + << (unsigned int) *(buffer + 1) << " received while " + << "waiting for resource id " << splitState_.resource + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid resource id " + << (unsigned int) *(buffer + 1) << " received while " + << "waiting for resource id " << splitState_.resource + << ".\n"; + + return -1; + } + + // + // Send the selected resource to the remote. + // + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeCachedValue(splitState_.resource, 8, + clientCache_ -> resourceCache); + } + + // + // Send the split notification events + // to the agent. + // + + handleRestart(sequence_immediate, splitState_.resource); + + // + // Check if we still have splits to send. + // + + handleSplitPending(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleEndSplitRequest: SPLIT! Reset id " + << splitState_.resource << " as resource " + << "selected for splits.\n" << logofs_flush; + #endif + + splitState_.resource = nothing; + splitState_.mode = split_none; + + #if defined(TEST) || defined(SPLIT) + + clientStore_ -> dumpSplitStores(); + + #endif + + return 1; +} + +void ClientChannel::handleDecodeCharInfo(DecodeBuffer &decodeBuffer, unsigned char *nextDest) +{ + unsigned int value; + + decodeBuffer.decodeCachedValue(value, 32, + *serverCache_ -> queryFontCharInfoCache[0], 6); + + PutUINT(value & 0xffff, nextDest, bigEndian_); + PutUINT(value >> 16, nextDest + 10, bigEndian_); + + nextDest += 2; + + for (unsigned int i = 1; i < 5; i++) + { + unsigned int value; + + decodeBuffer.decodeCachedValue(value, 16, + *serverCache_ -> queryFontCharInfoCache[i], 6); + + PutUINT(value, nextDest, bigEndian_); + + nextDest += 2; + } +} + +int ClientChannel::setBigEndian(int flag) +{ + bigEndian_ = flag; + + return 1; +} + +int ClientChannel::setReferences() +{ + #ifdef TEST + *logofs << "ClientChannel: Initializing the static " + << "members for the client channels.\n" + << logofs_flush; + #endif + + #ifdef REFERENCES + + references_ = 0; + + #endif + + return 1; +} diff --git a/nxcomp/ClientChannel.h b/nxcomp/ClientChannel.h new file mode 100644 index 000000000..9924bb263 --- /dev/null +++ b/nxcomp/ClientChannel.h @@ -0,0 +1,466 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ClientChannel_H +#define ClientChannel_H + +#include "List.h" +#include "Channel.h" + +#include "SequenceQueue.h" + +#include "ClientReadBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// If defined, the client channel will +// have the chance of suppressing more +// opcodes for test purposes. +// + +#undef LAME + +// +// Define this to log a line when a +// channel is created or destroyed. +// + +#undef REFERENCES + +// +// This class implements the X client +// side compression of the protocol. +// + +class ClientChannel : public Channel +{ + public: + + ClientChannel(Transport *transport, StaticCompressor *compressor); + + virtual ~ClientChannel(); + + virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, + unsigned int length); + + virtual int handleWrite(const unsigned char *message, unsigned int length); + + virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store, + T_store_action action, int position, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, + T_store_action action, int position, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) + { + return 0; + } + + virtual int handleSplit(EncodeBuffer &encodeBuffer); + + virtual int handleSplit(DecodeBuffer &decodeBuffer) + { + return 0; + } + + virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split); + + virtual int handleSplitEvent(DecodeBuffer &decodeBuffer); + + virtual int handleMotion(EncodeBuffer &encodeBuffer) + { + return 0; + } + + virtual int handleCompletion(EncodeBuffer &encodeBuffer) + { + return 0; + } + + virtual int handleConfiguration(); + + virtual int handleFinish(); + + virtual int handleAsyncEvents() + { + return 0; + } + + virtual int needSplit() const + { + #if defined(TEST) || defined(SPLIT) + *logofs << "needSplit: SPLIT! Returning pending split " + << "flag " << splitState_.pending << " with " + << clientStore_ -> getSplitTotalSize() + << " splits in the split stores.\n" + << logofs_flush; + #endif + + return splitState_.pending; + } + + virtual int needMotion() const + { + return 0; + } + + virtual T_channel_type getType() const + { + return channel_x11; + } + + int setBigEndian(int flag); + + // + // Initialize the static members. + // + + static int setReferences(); + + private: + + int handleFastReadRequest(EncodeBuffer &encodeBuffer, const unsigned char &opcode, + const unsigned char *&buffer, const unsigned int &size); + + int handleFastWriteReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + int handleFastWriteEvent(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + // + // Intercept the request before the opcode + // is encoded. + // + + int handleTaintRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size) + { + if (control -> isProtoStep7() == 0) + { + if (opcode == X_NXFreeSplit || opcode == X_NXAbortSplit || + opcode == X_NXFinishSplit) + { + return handleTaintSplitRequest(opcode, buffer, size); + } + else if (opcode == X_NXSetCacheParameters) + { + return handleTaintCacheRequest(opcode, buffer, size); + } + else if (opcode == X_NXGetFontParameters) + { + return handleTaintFontRequest(opcode, buffer, size); + } + } + + if (control -> TaintReplies > 0 && + opcode == X_GetInputFocus) + { + return handleTaintSyncRequest(opcode, buffer, size); + } + + #ifdef LAME + + return handleTaintLameRequest(opcode, buffer, size); + + #endif + + return 0; + } + + int handleTaintCacheRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size); + + int handleTaintFontRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size); + + int handleTaintSplitRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size); + + int handleTaintLameRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size); + + int handleTaintSyncRequest(unsigned char &opcode, const unsigned char *&buffer, + unsigned int &size); + + int handleTaintSyncError(unsigned char opcode); + + // + // How to handle sequence counter + // in notification event. + // + + enum T_sequence_mode + { + sequence_immediate, + sequence_deferred + }; + + // + // Send split notifications to the + // agent. + // + + int handleRestart(T_sequence_mode mode, int resource); + + int handleNotify(T_notification_type type, T_sequence_mode mode, + int resource, int request, int position); + + // + // Other utility functions used in + // handling of the image streaming. + // + + int mustSplitMessage(int resource) + { + return (clientStore_ -> getSplitStore(resource) -> + getSize() != 0); + } + + int canSplitMessage(T_split_mode mode, unsigned int size) + { + return ((int) size >= control -> SplitDataThreshold && + (clientStore_ -> getSplitTotalStorageSize() < control -> + SplitTotalStorageSize && clientStore_ -> + getSplitTotalSize() < control -> SplitTotalSize)); + } + + int canSendSplit(Split *split) + { + return (split -> getMode() != split_sync || + split -> getState() == split_missed || + split -> getState() == split_loaded); + } + + int handleSplitSend(EncodeBuffer &encodeBuffer, int resource, + int &total, int &bytes); + + Split *handleSplitFind(T_checksum checksum, int resource); + + int handleSplitChecksum(EncodeBuffer &encodeBuffer, T_checksum checksum); + + void handleSplitEnable() + { + if (control -> isProtoStep7() == 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEnable: WARNING! Disabling split " + << "with an old proxy version.\n" + << logofs_flush; + #endif + + enableSplit_ = 0; + } + } + + void handleSplitPending(int resource) + { + if (splitState_.pending == 0) + { + if (clientStore_ -> getSplitStore(resource) != NULL && + clientStore_ -> getSplitStore(resource) -> + getFirstSplit() != NULL) + { + splitState_.pending = canSendSplit(clientStore_ -> + getSplitStore(resource) -> getFirstSplit()); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitPending: SPLIT! Set the pending " + << "split flag to " << splitState_.pending + << " with " << clientStore_ -> getSplitTotalSize() + << " splits in the split stores.\n" + << logofs_flush; + #endif + } + } + } + + // + // Scan all the split stores to find + // if there is any split to send. + // + + void handleSplitPending(); + + // + // Handle the MIT-SHM initialization + // messages exchanged with the remote + // proxy. + // + + int handleShmemRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + int handleShmemReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + // + // Query the port used to tunnel + // the font server connections. + // + + int handleFontRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + int handleFontReply(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + // + // Let the agent set the cache + // policy for image requests. + // + + int handleCacheRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + // + // Encode the start and end split + // requests. + // + + int handleStartSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + int handleEndSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + // + // Empty a split store and send the + // restart event. + // + + int handleAbortSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + // + // Force the proxy to finalize all + // the pending split operations and + // restart a resource. + // + + int handleFinishSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + // + // Tell the remote peer to send the + // split requests to the X server. + // + + int handleCommitSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + // + // Other utilities. + // + + void handleDecodeCharInfo(DecodeBuffer &, unsigned char *); + + // + // Own read buffer. It is able to identify + // full messages read from the X descriptor. + // + + ClientReadBuffer readBuffer_; + + // + // Sequence number of last request coming + // from the X client or the X server. + // + + unsigned int clientSequence_; + unsigned int serverSequence_; + + // + // Last sequence number known by client. It can + // be the real sequence generated by server or + // the one of the last auto-generated event. + // + + unsigned int lastSequence_; + + // + // Used to identify replies based on sequence + // number of original request. + // + + SequenceQueue sequenceQueue_; + + // + // This is used to test the synchronous flush + // in the proxy. + // + + int lastRequest_; + + // + // Current resource id selected as target and + // other information related to the image split. + // The pending and abort flags are set when we + // want the proxy to give us a chance to send + // more split data. We also save the position + // of the last commit operation performed by + // channel so we can differentially encode the + // position of next message to commit. + // + + typedef struct + { + int resource; + int pending; + int commit; + T_split_mode mode; + + } T_split_state; + + T_split_state splitState_; + + // + // List of agent resources. + // + + List splitResources_; + + // + // How many sync requests we + // have tainted so far. + // + + int taintCounter_; + + private: + + // + // Keep track of object + // creation and deletion. + // + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +#endif /* ClientChannel_H */ diff --git a/nxcomp/ClientProxy.cpp b/nxcomp/ClientProxy.cpp new file mode 100644 index 000000000..ef63bb0eb --- /dev/null +++ b/nxcomp/ClientProxy.cpp @@ -0,0 +1,538 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Socket.h" +#include "Agent.h" + +#include "ClientProxy.h" + +#include "ClientChannel.h" +#include "GenericChannel.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Log the operations related to sending +// and receiving the control tokens. +// + +#undef TOKEN + +ClientProxy::ClientProxy(int proxyFd) : Proxy(proxyFd) +{ + fontServerPort_ = NULL; + + #ifdef DEBUG + *logofs << "ClientProxy: Created new object at " << this + << ".\n" << logofs_flush; + #endif +} + +ClientProxy::~ClientProxy() +{ + delete [] fontServerPort_; + + #ifdef DEBUG + *logofs << "ClientProxy: Deleted object at " << this + << ".\n" << logofs_flush; + #endif +} + +void ClientProxy::handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily, + sockaddr * xServerAddr, unsigned int xServerAddrLength) +{ + #ifdef DEBUG + *logofs << "ClientProxy: No display configuration to set.\n" + << logofs_flush; + #endif +} + +void ClientProxy::handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort, + int httpServerPort, const char *fontServerPort) +{ + delete [] fontServerPort_; + + fontServerPort_ = new char[strlen(fontServerPort) + 1]; + + strcpy(fontServerPort_, fontServerPort); + + #ifdef DEBUG + *logofs << "ClientProxy: Set port configuration to font '" + << fontServerPort_ << "'.\n" + << logofs_flush; + #endif +} + +int ClientProxy::handleNewConnection(T_channel_type type, int clientFd) +{ + switch (type) + { + case channel_x11: + { + return handleNewXConnection(clientFd); + } + case channel_cups: + { + return handleNewGenericConnection(clientFd, channel_cups, "CUPS"); + } + case channel_smb: + { + return handleNewGenericConnection(clientFd, channel_smb, "SMB"); + } + case channel_media: + { + return handleNewGenericConnection(clientFd, channel_media, "media"); + } + case channel_http: + { + return handleNewGenericConnection(clientFd, channel_http, "HTTP"); + } + case channel_slave: + { + return handleNewSlaveConnection(clientFd); + } + default: + { + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Unsupported channel with type '" + << getTypeName(type) << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unsupported channel with type '" + << getTypeName(type) << "'.\n"; + + return -1; + } + } +} + +int ClientProxy::handleNewConnectionFromProxy(T_channel_type type, int channelId) +{ + switch (type) + { + case channel_font: + { + int port = atoi(fontServerPort_); + + if (port > 0) + { + // + // Connect on the TCP port number. + // + + return handleNewGenericConnectionFromProxy(channelId, channel_font, "localhost", + port, "font"); + } + else + { + // + // Connect to the Unix path. + // + + return handleNewGenericConnectionFromProxy(channelId, channel_font, "localhost", + fontServerPort_, "font"); + } + } + case channel_slave: + { + return handleNewSlaveConnectionFromProxy(channelId); + } + default: + { + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Unsupported channel with type '" + << getTypeName(type) << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unsupported channel with type '" + << getTypeName(type) << "'.\n"; + + return -1; + } + } +} + +int ClientProxy::handleNewAgentConnection(Agent *agent) +{ + int clientFd = agent -> getLocalFd(); + + int channelId = allocateChannelMap(clientFd); + + if (channelId == -1) + { + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Maximum mumber of available " + << "channels exceeded.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Maximum mumber of available " + << "channels exceeded.\n"; + + return -1; + } + + transports_[channelId] = agent -> getTransport(); + + agent_ = channelId; + + return handleNewXConnection(clientFd); +} + +int ClientProxy::handleNewXConnection(int clientFd) +{ + int channelId = getChannel(clientFd); + + // + // Check if the channel has been + // already mapped. + // + + if (channelId == -1) + { + channelId = allocateChannelMap(clientFd); + + if (channelId == -1) + { + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Maximum mumber of available " + << "channels exceeded.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Maximum mumber of available " + << "channels exceeded.\n"; + + return -1; + } + } + + #ifdef TEST + *logofs << "ClientProxy: X client descriptor FD#" << clientFd + << " mapped to channel ID#" << channelId << ".\n" + << logofs_flush; + #endif + + // + // Turn queuing off for path proxy-to-X-client. + // + + if (control -> OptionClientNoDelay == 1) + { + SetNoDelay(clientFd, control -> OptionClientNoDelay); + } + + // + // If requested, set the size of the TCP send + // and receive buffers. + // + + if (control -> OptionClientSendBuffer != -1) + { + SetSendBuffer(clientFd, control -> OptionClientSendBuffer); + } + + if (control -> OptionClientReceiveBuffer != -1) + { + SetReceiveBuffer(clientFd, control -> OptionClientReceiveBuffer); + } + + if (allocateTransport(clientFd, channelId) < 0) + { + return -1; + } + + // + // Starting from protocol level 3 client and server + // caches are created in proxy and shared between all + // channels. If remote proxy has older protocol level + // pointers are NULL and channels must create their + // own instances. + // + + channels_[channelId] = new ClientChannel(transports_[channelId], compressor_); + + if (channels_[channelId] == NULL) + { + deallocateTransport(channelId); + + return -1; + } + + increaseChannels(channelId); + + // + // Propagate channel stores and caches to the new + // channel. + // + + channels_[channelId] -> setOpcodes(opcodeStore_); + + channels_[channelId] -> setStores(clientStore_, serverStore_); + + channels_[channelId] -> setCaches(clientCache_, serverCache_); + + int port = atoi(fontServerPort_); + + if (port > 0 || *fontServerPort_ != '\0') + { + channels_[channelId] -> setPorts(1); + } + + if (handleControl(code_new_x_connection, channelId) < 0) + { + return -1; + } + + // + // Let channel configure itself according + // to control parameters. + // + + channels_[channelId] -> handleConfiguration(); + + return 1; +} + +int ClientProxy::handleNewXConnectionFromProxy(int channelId) +{ + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Can't create a new X channel " + << "with ID#" << channelId << " at this side.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't create a new X channel " + << "with ID#" << channelId << " at this side.\n"; + + return -1; +} + +int ClientProxy::handleLoad(T_load_type type) +{ + int channelCount = getChannels(channel_x11); + + if ((channelCount == 0 && type == load_if_first) || + (channelCount > 0 && type == load_if_any)) + { + #ifdef TEST + *logofs << "ClientProxy: Going to load content of client store.\n" + << logofs_flush; + #endif + + int result = handleLoadStores(); + + if (result == 1) + { + if (handleControl(code_load_request) < 0) + { + return -1; + } + + priority_ = 1; + } + else if (result < 0) + { + #ifdef WARNING + *logofs << "ClientProxy: WARNING! Failed to load content " + << "of persistent cache.\n" << logofs_flush; + #endif + + // + // Don't abort the proxy connection in the case + // of a corrupted cache. By not sending the load + // message to the remote peer, both sides will + // start encoding messages using empty stores. + // This behaviour is compatible with old proxy + // versions. + // + + if (channelCount == 0 && type == load_if_first) + { + if (handleResetStores() < 0) + { + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Failed to reset message stores.\n" + << logofs_flush; + #endif + + return -1; + } + } + else + { + return -1; + } + } + } + else + { + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Can't load the stores with " + << channelCount << " remaining channels.\n" + << logofs_flush; + #endif + + return -1; + } + + return 1; +} + +int ClientProxy::handleSave() +{ + // + // If no more X channels are remaining + // then save content of message stores. + // + + int channelCount = getChannels(channel_x11); + + if (channelCount == 0) + { + int result = handleSaveStores(); + + if (result == 1) + { + if (handleControl(code_save_request) < 0) + { + return -1; + } + + priority_ = 1; + + return 1; + } + else if (result < 0) + { + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Failed to save stores " + << "to persistent cache.\n" << logofs_flush; + #endif + + return -1; + } + } + else + { + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Can't save the stores with " + << channelCount << " remaining channels.\n" + << logofs_flush; + #endif + + return -1; + } + + return 1; +} + +int ClientProxy::handleAsyncEvents() +{ + if (canRead() == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: WARNING! Reading while writing " + << "with data available on the proxy link.\n" + << logofs_flush; + #endif + + if (handleRead() < 0) + { + return -1; + } + + return 1; + } + + return 0; +} + +int ClientProxy::handleLoadFromProxy() +{ + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Invalid load control message " + << "received in proxy.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid load control message " + << "received in proxy.\n"; + + return -1; +} + +int ClientProxy::handleSaveFromProxy() +{ + #ifdef PANIC + *logofs << "ClientProxy: PANIC! Invalid save control message " + << "received in proxy.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid save control message " + << "received in proxy.\n"; + + return -1; +} + +int ClientProxy::handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient) const +{ + if (clientStore_ -> saveRequestStores(cachefs, md5StateStream, md5StateClient, + use_checksum, discard_data) < 0) + { + return -1; + } + else if (serverStore_ -> saveReplyStores(cachefs, md5StateStream, md5StateClient, + discard_checksum, use_data) < 0) + { + return -1; + } + else if (serverStore_ -> saveEventStores(cachefs, md5StateStream, md5StateClient, + discard_checksum, use_data) < 0) + { + return -1; + } + + return 1; +} + +int ClientProxy::handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const +{ + if (clientStore_ -> loadRequestStores(cachefs, md5StateStream, + use_checksum, discard_data) < 0) + { + return -1; + } + else if (serverStore_ -> loadReplyStores(cachefs, md5StateStream, + discard_checksum, use_data) < 0) + { + return -1; + } + else if (serverStore_ -> loadEventStores(cachefs, md5StateStream, + discard_checksum, use_data) < 0) + { + return -1; + } + + return 1; +} + diff --git a/nxcomp/ClientProxy.h b/nxcomp/ClientProxy.h new file mode 100644 index 000000000..2b669ba2d --- /dev/null +++ b/nxcomp/ClientProxy.h @@ -0,0 +1,108 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ClientProxy_H +#define ClientProxy_H + +#include "Proxy.h" + +// +// Set the verbosity level. +// + +#undef TEST +#undef DEBUG + +class ClientProxy : public Proxy +{ + public: + + ClientProxy(int proxyFD); + + virtual ~ClientProxy(); + + virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily, + sockaddr *xServerAddr, unsigned int xServerAddrLength); + + virtual void handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort, + int httpServerPort, const char *fontServerPort); + + protected: + + // + // Create a new channel. + // + + virtual int handleNewConnection(T_channel_type type, int clientFd); + + virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId); + + virtual int handleNewAgentConnection(Agent *agent); + + virtual int handleNewXConnection(int clientFd); + + virtual int handleNewXConnectionFromProxy(int channelId); + + // + // Implement persistence according + // to our proxy mode. + // + + virtual int handleLoad(T_load_type type); + virtual int handleSave(); + + virtual int handleAsyncEvents(); + + virtual int handleLoadFromProxy(); + virtual int handleSaveFromProxy(); + + virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient) const; + + virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const; + + // + // Utility function used to realize + // a new connection. + // + + protected: + + virtual int checkLocalChannelMap(int channelId) + { + if (control -> isProtoStep7() == 1) + { + return ((channelId & control -> ChannelMask) != 0); + } + else + { + return 1; + } + } + + // + // Ports where to forward extended services' + // TCP connections. + // + + private: + + char *fontServerPort_; +}; + + +#endif /* ClientProxy_H */ diff --git a/nxcomp/ClientReadBuffer.cpp b/nxcomp/ClientReadBuffer.cpp new file mode 100644 index 000000000..b32033b17 --- /dev/null +++ b/nxcomp/ClientReadBuffer.cpp @@ -0,0 +1,166 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ClientReadBuffer.h" + +#include "ClientChannel.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +unsigned int ClientReadBuffer::suggestedLength(unsigned int pendingLength) +{ + // + // Even if the pending data is not + // enough to make a complete message, + // resize the buffer to accomodate + // it all. + // + + unsigned int readLength = pendingLength; + + if (pendingLength < remaining_) + { + readLength = remaining_; + } + + return readLength; +} + +int ClientReadBuffer::locateMessage(const unsigned char *start, + const unsigned char *end, + unsigned int &controlLength, + unsigned int &dataLength, + unsigned int &trailerLength) +{ + unsigned int size = end - start; + + #ifdef TEST + *logofs << "ClientReadBuffer: Locating message for FD#" + << transport_ -> fd() << " with " << size + << " bytes.\n" << logofs_flush; + #endif + + if (firstMessage_) + { + if (size < 12) + { + remaining_ = 12 - size; + + #ifdef TEST + *logofs << "ClientReadBuffer: No message was located " + << "with remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 0; + } + + if (*start == 0x42) + { + bigEndian_ = 1; + } + else + { + bigEndian_ = 0; + } + + channel_ -> setBigEndian(bigEndian_); + + dataLength = 12 + RoundUp4(GetUINT(start + 6, bigEndian_)) + + RoundUp4(GetUINT(start + 8, bigEndian_)); + + // + // Send the data immediately if this is unlikely + // to be a X connection attempt. + // + + if (dataLength > 4096) + { + #ifdef WARNING + *logofs << "ClientReadBuffer: WARNING! Flushing suspicious X " + << "connection with first request of " << dataLength + << " bytes.\n" << logofs_flush; + #endif + + dataLength = size; + } + } + else + { + if (size < 4) + { + remaining_ = 4 - size; + + #ifdef TEST + *logofs << "ClientReadBuffer: No message was located " + << "with remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 0; + } + + dataLength = (GetUINT(start + 2, bigEndian_) << 2); + + if (dataLength < 4) + { + #ifdef TEST + *logofs << "ClientReadBuffer: WARNING! Assuming length 4 " + << "for suspicious message of length " << dataLength + << ".\n" << logofs_flush; + #endif + + dataLength = 4; + } + } + + #ifdef TEST + *logofs << "ClientReadBuffer: Length of the next message is " + << dataLength << ".\n" << logofs_flush; + #endif + + if (size < dataLength) + { + remaining_ = dataLength - size; + + #ifdef TEST + *logofs << "ClientReadBuffer: No message was located " + << "with remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 0; + } + + firstMessage_ = 0; + + controlLength = 0; + trailerLength = 0; + + remaining_ = 0; + + #ifdef TEST + *logofs << "ClientReadBuffer: Located message with " + << "remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 1; +} diff --git a/nxcomp/ClientReadBuffer.h b/nxcomp/ClientReadBuffer.h new file mode 100644 index 000000000..6dee630ac --- /dev/null +++ b/nxcomp/ClientReadBuffer.h @@ -0,0 +1,57 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ClientReadBuffer_H +#define ClientReadBuffer_H + +#include "Control.h" +#include "ReadBuffer.h" + +class ClientChannel; + +class ClientReadBuffer : public ReadBuffer +{ + public: + + ClientReadBuffer(Transport *transport, ClientChannel *channel) + + : ReadBuffer(transport), firstMessage_(1), channel_(channel) + { + } + + virtual ~ClientReadBuffer() + { + } + + protected: + + virtual unsigned int suggestedLength(unsigned int pendingLength); + + virtual int locateMessage(const unsigned char *start, + const unsigned char *end, + unsigned int &controlLength, + unsigned int &dataLength, + unsigned int &trailerLength); + + int bigEndian_; + + int firstMessage_; + + ClientChannel *channel_; +}; + +#endif /* ClientReadBuffer_H */ diff --git a/nxcomp/ClientStore.cpp b/nxcomp/ClientStore.cpp new file mode 100644 index 000000000..be0e892b4 --- /dev/null +++ b/nxcomp/ClientStore.cpp @@ -0,0 +1,228 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ClientStore.h" + +// +// Cached request classes. +// + +#include "ChangeProperty.h" +#include "SendEvent.h" +#include "CreateGC.h" +#include "ChangeGC.h" +#include "CreatePixmap.h" +#include "SetClipRectangles.h" +#include "CopyArea.h" +#include "PolyLine.h" +#include "PolySegment.h" +#include "PolyFillRectangle.h" +#include "PutImage.h" +#include "TranslateCoords.h" +#include "GetImage.h" +#include "ClearArea.h" +#include "ConfigureWindow.h" +#include "ShapeExtension.h" +#include "RenderExtension.h" +#include "PolyText8.h" +#include "PolyText16.h" +#include "ImageText8.h" +#include "ImageText16.h" +#include "PolyPoint.h" +#include "PolyFillArc.h" +#include "PolyArc.h" +#include "FillPoly.h" +#include "InternAtom.h" +#include "GetProperty.h" +#include "SetUnpackGeometry.h" +#include "SetUnpackColormap.h" +#include "SetUnpackAlpha.h" +#include "PutPackedImage.h" +#include "GenericRequest.h" + +#include "ChangeGCCompat.h" +#include "CreatePixmapCompat.h" +#include "SetUnpackColormapCompat.h" +#include "SetUnpackAlphaCompat.h" + +// +// Set the verbosity level. +// + +#define WARNING +#define PANIC +#undef TEST + +ClientStore::ClientStore(StaticCompressor *compressor) + + : compressor_(compressor) +{ + if (logofs == NULL) + { + logofs = &cout; + } + + for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) + { + requests_[i] = NULL; + } + + requests_[X_ChangeProperty] = new ChangePropertyStore(); + requests_[X_SendEvent] = new SendEventStore(); + requests_[X_CreateGC] = new CreateGCStore(); + requests_[X_SetClipRectangles] = new SetClipRectanglesStore(); + requests_[X_CopyArea] = new CopyAreaStore(); + requests_[X_PolyLine] = new PolyLineStore(); + requests_[X_PolySegment] = new PolySegmentStore(); + requests_[X_PolyFillRectangle] = new PolyFillRectangleStore(); + requests_[X_PutImage] = new PutImageStore(compressor); + requests_[X_TranslateCoords] = new TranslateCoordsStore(); + requests_[X_GetImage] = new GetImageStore(); + requests_[X_ClearArea] = new ClearAreaStore(); + requests_[X_ConfigureWindow] = new ConfigureWindowStore(); + requests_[X_PolyText8] = new PolyText8Store(); + requests_[X_PolyText16] = new PolyText16Store(); + requests_[X_ImageText8] = new ImageText8Store(); + requests_[X_ImageText16] = new ImageText16Store(); + requests_[X_PolyPoint] = new PolyPointStore(); + requests_[X_PolyFillArc] = new PolyFillArcStore(); + requests_[X_PolyArc] = new PolyArcStore(); + requests_[X_FillPoly] = new FillPolyStore(); + requests_[X_InternAtom] = new InternAtomStore(); + requests_[X_GetProperty] = new GetPropertyStore(); + + requests_[X_NXInternalShapeExtension] = new ShapeExtensionStore(compressor); + requests_[X_NXInternalGenericRequest] = new GenericRequestStore(compressor); + requests_[X_NXInternalRenderExtension] = new RenderExtensionStore(compressor); + requests_[X_NXSetUnpackGeometry] = new SetUnpackGeometryStore(compressor); + requests_[X_NXPutPackedImage] = new PutPackedImageStore(compressor); + + if (control -> isProtoStep7() == 1) + { + requests_[X_ChangeGC] = new ChangeGCStore(); + requests_[X_CreatePixmap] = new CreatePixmapStore(); + requests_[X_NXSetUnpackColormap] = new SetUnpackColormapStore(compressor); + requests_[X_NXSetUnpackAlpha] = new SetUnpackAlphaStore(compressor); + } + else + { + requests_[X_ChangeGC] = new ChangeGCCompatStore(); + requests_[X_CreatePixmap] = new CreatePixmapCompatStore(); + requests_[X_NXSetUnpackColormap] = new SetUnpackColormapCompatStore(compressor); + requests_[X_NXSetUnpackAlpha] = new SetUnpackAlphaCompatStore(compressor); + } + + for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++) + { + splits_[i] = NULL; + } + + commits_ = new CommitStore(compressor); +} + +ClientStore::~ClientStore() +{ + if (logofs == NULL) + { + logofs = &cout; + } + + for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) + { + delete requests_[i]; + } + + for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++) + { + delete splits_[i]; + } + + delete commits_; +} + +int ClientStore::saveRequestStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient, T_checksum_action checksumAction, + T_data_action dataAction) const +{ + for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) + { + if (requests_[i] != NULL && + requests_[i] -> saveStore(cachefs, md5StateStream, md5StateClient, + checksumAction, dataAction, + storeBigEndian()) < 0) + { + #ifdef WARNING + *logofs << "ClientStore: WARNING! Error saving request store " + << "for OPCODE#" << (unsigned int) i << ".\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Error saving request store " + << "for opcode '" << (unsigned int) i << "'.\n"; + + return -1; + } + } + + return 1; +} + +int ClientStore::loadRequestStores(istream *cachefs, md5_state_t *md5StateStream, + T_checksum_action checksumAction, T_data_action dataAction) const +{ + for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) + { + if (requests_[i] != NULL && + requests_[i] -> loadStore(cachefs, md5StateStream, + checksumAction, dataAction, + storeBigEndian()) < 0) + { + #ifdef WARNING + *logofs << "ClientStore: WARNING! Error loading request store " + << "for OPCODE#" << (unsigned int) i << ".\n" + << logofs_flush; + #endif + + return -1; + } + } + + return 1; +} + +void ClientStore::dumpSplitStores() const +{ + for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++) + { + if (splits_[i] != NULL) + { + splits_[i] -> dump(); + } + } + + if ((getSplitTotalSize() != 0 && getSplitTotalStorageSize() == 0) || + (getSplitTotalSize() == 0 && getSplitTotalStorageSize() != 0)) + { + #ifdef PANIC + *logofs << "ClientStore: PANIC! Inconsistency detected " + << "while handling the split stores.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } +} diff --git a/nxcomp/ClientStore.h b/nxcomp/ClientStore.h new file mode 100644 index 000000000..54a68a309 --- /dev/null +++ b/nxcomp/ClientStore.h @@ -0,0 +1,135 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ClientStore_H +#define ClientStore_H + +#include "Message.h" +#include "Split.h" + +#include "ChannelStore.h" + +class StaticCompressor; + +class ClientStore : public ChannelStore +{ + public: + + ClientStore(StaticCompressor *compressor); + + virtual ~ClientStore(); + + // + // Get the store based on the index. + // + + MessageStore *getRequestStore(unsigned char opcode) const + { + return requests_[opcode]; + } + + SplitStore *getSplitStore(int resource) const + { + return splits_[resource]; + } + + int getSplitTotalSize() const + { + return SplitStore::getTotalSize(); + } + + int getSplitTotalStorageSize() const + { + return SplitStore::getTotalStorageSize(); + } + + CommitStore *getCommitStore() const + { + return commits_; + } + + int getCommitSize() const + { + return commits_ -> getSize(); + } + + void dumpSplitStore(int resource) const + { + splits_[resource] -> dump(); + } + + void dumpCommitStore() const + { + commits_ -> dump(); + } + + void dumpSplitStores() const; + + SplitStore *createSplitStore(int resource) + { + splits_[resource] = new SplitStore(compressor_, commits_, resource); + + return splits_[resource]; + } + + void destroySplitStore(int resource) + { + delete splits_[resource]; + + splits_[resource] = NULL; + } + + // + // Actually save the message store + // to disk according to proxy mode. + // + + int saveRequestStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient, T_checksum_action checksumAction, + T_data_action dataAction) const; + + int loadRequestStores(istream *cachefs, md5_state_t *md5StateStream, + T_checksum_action checksumAction, T_data_action dataAction) const; + + private: + + // + // A client store contains requests. + // + + MessageStore *requests_[CHANNEL_STORE_OPCODE_LIMIT]; + + // + // Client messages being split. + // + + SplitStore *splits_[CHANNEL_STORE_RESOURCE_LIMIT]; + + // + // Messages having been recomposed. + // + + CommitStore *commits_; + + // + // Passed forward to the other stores. + // + + StaticCompressor *compressor_; +}; + +#endif /* ClientStore_H */ diff --git a/nxcomp/Colormap.cpp b/nxcomp/Colormap.cpp new file mode 100644 index 000000000..5702beca9 --- /dev/null +++ b/nxcomp/Colormap.cpp @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Misc.h" +#include "Unpack.h" +#include "Colormap.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +int UnpackColormap(unsigned char method, unsigned char *src_data, int src_size, + unsigned char *dst_data, int dst_size) +{ + if (*src_data == 0) + { + if (dst_size != src_size - 1) + { + #ifdef TEST + *logofs << "UnpackColormap: PANIC! Invalid destination size " + << dst_size << " with source " << src_size + << ".\n" << logofs_flush; + #endif + + return -1; + } + + #ifdef TEST + *logofs << "UnpackColormap: Expanding " << src_size - 1 + << " bytes of plain colormap data.\n" << logofs_flush; + #endif + + memcpy(dst_data, src_data + 1, src_size - 1); + + return 1; + } + + unsigned int check_size = dst_size; + + int result = ZDecompress(&unpackStream, dst_data, &check_size, + src_data + 1, src_size - 1); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "UnpackColormap: PANIC! Failure decompressing colormap data. " + << "Error is '" << zError(result) << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decompressing colormap data. " + << "Error is '" << zError(result) << "'.\n"; + + return -1; + } + else if (check_size != (unsigned int) dst_size) + { + #ifdef PANIC + *logofs << "UnpackColormap: PANIC! Size mismatch in colormap data. " + << "Resulting size is " << check_size << " with " + << "expected size " << dst_size << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Size mismatch in colormap data. " + << "Resulting size is " << check_size << " with " + << "expected size " << dst_size << ".\n"; + + return -1; + } + + #ifdef TEST + *logofs << "UnpackColormap: Decompressed " << src_size - 1 + << " bytes to " << dst_size << " bytes of colormap data.\n" + << logofs_flush; + #endif + + return 1; +} diff --git a/nxcomp/Colormap.h b/nxcomp/Colormap.h new file mode 100644 index 000000000..e0056f86c --- /dev/null +++ b/nxcomp/Colormap.h @@ -0,0 +1,24 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Colormap_H +#define Colormap_H + +int UnpackColormap(unsigned char method, unsigned char *src_data, int src_size, + unsigned char *dst_data, int dst_size); + +#endif /* Colormap_H */ diff --git a/nxcomp/ConfigureWindow.cpp b/nxcomp/ConfigureWindow.cpp new file mode 100644 index 000000000..995ab1831 --- /dev/null +++ b/nxcomp/ConfigureWindow.cpp @@ -0,0 +1,130 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ConfigureWindow.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int ConfigureWindowStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message; + + // + // Here is the fingerprint. + // + + configureWindow -> window = GetULONG(buffer + 4, bigEndian); + + configureWindow -> value_mask = GetUINT(buffer + 8, bigEndian); + + // + // To increase effectiveness of the caching algorithm + // we remove the unused bytes carried in the data part. + // + + if ((int) size > dataOffset) + { + #ifdef DEBUG + *logofs << name() << ": Removing unused bytes from the data payload.\n" << logofs_flush; + #endif + + configureWindow -> value_mask &= (1 << 7) - 1; + + unsigned int mask = 0x1; + unsigned char *source = (unsigned char *) buffer + CONFIGUREWINDOW_DATA_OFFSET; + unsigned long value = 0; + + for (unsigned int i = 0; i < 7; i++) + { + if (configureWindow -> value_mask & mask) + { + value = GetULONG(source, bigEndian); + + value &= (1 << CONFIGUREWINDOW_FIELD_WIDTH[i]) - 1; + + PutULONG(value, source, bigEndian); + + source += 4; + } + mask <<= 1; + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ConfigureWindowStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(configureWindow -> window, buffer + 4, bigEndian); + + PutUINT(configureWindow -> value_mask, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void ConfigureWindowStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message; + + *logofs << "ConfigureWindow: window " << configureWindow -> window + << ", value_mask " << configureWindow -> value_mask + << ", size " << configureWindow -> size_ << ".\n"; + + #endif +} + +void ConfigureWindowStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 4, 4); + md5_append(md5_state_, buffer + 8, 2); +} diff --git a/nxcomp/ConfigureWindow.h b/nxcomp/ConfigureWindow.h new file mode 100644 index 000000000..bb511b0d4 --- /dev/null +++ b/nxcomp/ConfigureWindow.h @@ -0,0 +1,170 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ConfigureWindow_H +#define ConfigureWindow_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define CONFIGUREWINDOW_ENABLE_CACHE 1 +#define CONFIGUREWINDOW_ENABLE_DATA 0 +#define CONFIGUREWINDOW_ENABLE_SPLIT 0 +#define CONFIGUREWINDOW_ENABLE_COMPRESS 0 + +#define CONFIGUREWINDOW_DATA_LIMIT 32 +#define CONFIGUREWINDOW_DATA_OFFSET 12 + +#define CONFIGUREWINDOW_CACHE_SLOTS 3000 +#define CONFIGUREWINDOW_CACHE_THRESHOLD 5 +#define CONFIGUREWINDOW_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class ConfigureWindowMessage : public Message +{ + friend class ConfigureWindowStore; + + public: + + ConfigureWindowMessage() + { + } + + ~ConfigureWindowMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int window; + unsigned short value_mask; +}; + +class ConfigureWindowStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + ConfigureWindowStore() : MessageStore() + { + enableCache = CONFIGUREWINDOW_ENABLE_CACHE; + enableData = CONFIGUREWINDOW_ENABLE_DATA; + enableSplit = CONFIGUREWINDOW_ENABLE_SPLIT; + enableCompress = CONFIGUREWINDOW_ENABLE_COMPRESS; + + dataLimit = CONFIGUREWINDOW_DATA_LIMIT; + dataOffset = CONFIGUREWINDOW_DATA_OFFSET; + + cacheSlots = CONFIGUREWINDOW_CACHE_SLOTS; + cacheThreshold = CONFIGUREWINDOW_CACHE_THRESHOLD; + cacheLowerThreshold = CONFIGUREWINDOW_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~ConfigureWindowStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "ConfigureWindow"; + } + + virtual unsigned char opcode() const + { + return X_ConfigureWindow; + } + + virtual unsigned int storage() const + { + return sizeof(ConfigureWindowMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new ConfigureWindowMessage(); + } + + virtual Message *create(const Message &message) const + { + return new ConfigureWindowMessage((const ConfigureWindowMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (ConfigureWindowMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* ConfigureWindow_H */ diff --git a/nxcomp/Control.cpp b/nxcomp/Control.cpp new file mode 100644 index 000000000..ce99567d7 --- /dev/null +++ b/nxcomp/Control.cpp @@ -0,0 +1,896 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "NXpack.h" + +#include "Control.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Flush immediately on prioritized messages. +// + +#define FLUSH_PRIORITY 0 + +// +// Maximum number of bytes sent for each token. +// + +#define TOKEN_SIZE 1536 + +// +// Maximum number of tokens that can be spent +// by the client proxy before having to block +// waiting for a token reply. +// + +#define TOKEN_LIMIT 24 + +// +// By default assume the proxy is running as a +// standalone program. +// + +#define LINK_ENCRYPTED 0 + +// +// Maximum number of pids the proxy will record +// and kill at shutdown. +// + +#define KILL_LIMIT 16 + +// +// Allocate on the NX client side channels whose +// ids are a multiple of 8 (starting from 0). All +// the other ids can be used to allocate channels +// at the NX server side (X client side). +// + +#define CHANNEL_MASK 0x07 + +// +// Kill session if control parameters cannot be +// negotiated before this timeout. +// + +#define INIT_TIMEOUT 60000 + +// +// Enter the congestion state if the remote does +// not reply to a ping within the given amount +// of time. +// + +#define PING_TIMEOUT 5000 + +// +// Only send one motion event any N milliseconds. +// + +#define MOTION_TIMEOUT 0 + + +// +// Force an update of the congestion counter if +// the proxy is idle for this time. +// + +#define IDLE_TIMEOUT 50 + +// +// Close X connection if can't write before this +// timeout. +// + +#define CHANNEL_TIMEOUT 10000 + +// +// Warn user (or close proxy connection) if don't +// receive any data before this timeout. +// + +#define PROXY_TIMEOUT 120000 + +// +// How many milliseconds to wait for the shared +// memory completion event to become available. +// + +#define SHMEM_TIMEOUT 200 +// +// Before closing down the proxy, wait for the +// given amount of miliseconds to let all the +// running applications to close down their +// connections. +// +// A null timeout will cause the proxy to wait +// indefinitely, until the watchdog process is +// killed. This is usually the way the proxy is +// started by the NX server. If on the other +// hand a timeout is given and there no channel +// is remaining, the proxy will be closed down +// using a small timeout, presently of 500 ms. +// + +#define CLEANUP_TIMEOUT 3000 + +// +// Wait this amount of milliseconds after any +// iteration of the house-keeping process. +// + +#define KEEPER_TIMEOUT 60000 + +// +// In case of timeout, select can return control +// to program earlier or later of this amount of +// ms. Consider this when calculating if timeout +// is elapsed. +// + +#define LATENCY_TIMEOUT 1 + +// +// Control memory allocation in transport +// and other classes. +// + +#define TRANSPORT_X_BUFFER_SIZE 131072 +#define TRANSPORT_PROXY_BUFFER_SIZE 65536 +#define TRANSPORT_GENERIC_BUFFER_SIZE 16384 + +#define TRANSPORT_X_BUFFER_THRESHOLD 262144 +#define TRANSPORT_PROXY_BUFFER_THRESHOLD 131072 +#define TRANSPORT_GENERIC_BUFFER_THRESHOLD 32768 + +// +// Never allow buffers to exceed this limit. +// + +#define TRANSPORT_MAXIMUM_BUFFER_SIZE 393216 + +// +// Immediately flush the accumulated data to +// the X server if the write buffer exceeds +// this size. +// + +#define TRANSPORT_FLUSH_BUFFER_SIZE 16384 + +// +// Defaults used for socket options. +// + +#define OPTION_PROXY_KEEP_ALIVE 0 +#define OPTION_PROXY_LOW_DELAY 1 +#define OPTION_PROXY_CLIENT_NO_DELAY 1 +#define OPTION_PROXY_SERVER_NO_DELAY 1 +#define OPTION_CLIENT_NO_DELAY 1 +#define OPTION_SERVER_NO_DELAY 1 + +#define OPTION_PROXY_RECEIVE_BUFFER -1 +#define OPTION_CLIENT_RECEIVE_BUFFER -1 +#define OPTION_SERVER_RECEIVE_BUFFER -1 + +#define OPTION_PROXY_SEND_BUFFER -1 +#define OPTION_CLIENT_SEND_BUFFER -1 +#define OPTION_SERVER_SEND_BUFFER -1 + +#define OPTION_PROXY_RETRY_CONNECT 30 +#define OPTION_PROXY_RETRY_ACCEPT 3 +#define OPTION_SERVER_RETRY_CONNECT 3 + +// +// Defaults used for cache persistence. +// + +#define PERSISTENT_CACHE_THRESHOLD 102400 + +#define PERSISTENT_CACHE_ENABLE_LOAD 1 +#define PERSISTENT_CACHE_ENABLE_SAVE 1 + +#define PERSISTENT_CACHE_CHECK_ON_SHUTDOWN 0 + +#define PERSISTENT_CACHE_LOAD_PACKED 1 +#define PERSISTENT_CACHE_LOAD_RENDER 1 + +#define PERSISTENT_CACHE_DISK_LIMIT 33554432 + +// +// Defaults used for image cache. +// + +#define IMAGE_CACHE_ENABLE_LOAD 0 +#define IMAGE_CACHE_ENABLE_SAVE 0 + +#define IMAGE_CACHE_DISK_LIMIT 33554432 + +// +// Suggested defaults for read length parameters +// used by read buffer classes. +// + +#define CLIENT_INITIAL_READ_SIZE 8192 +#define CLIENT_MAXIMUM_BUFFER_SIZE 262144 + +#define SERVER_INITIAL_READ_SIZE 8192 +#define SERVER_MAXIMUM_BUFFER_SIZE 65536 + +#define PROXY_INITIAL_READ_SIZE 65536 +#define PROXY_MAXIMUM_BUFFER_SIZE 262144 + 1024 + +#define GENERIC_INITIAL_READ_SIZE 8192 +#define GENERIC_MAXIMUM_BUFFER_SIZE 8192 + +// +// Calculate bitrate in given time frames. +// Values are in milliseconds. +// + +#define SHORT_BITRATE_TIME_FRAME 5000 +#define LONG_BITRATE_TIME_FRAME 30000 + +// +// Bandwidth control. A value of 0 means no +// limit. Values are stored internally in +// bytes per second. +// + +#define CLIENT_BITRATE_LIMIT 0 +#define SERVER_BITRATE_LIMIT 0 + +// +// Default values for cache control. We limit +// the maximum size of a request to 262144 but +// we need to consider the replies, whose size +// may be up to 4MB. +// + +#define MINIMUM_MESSAGE_SIZE 4 +#define MAXIMUM_MESSAGE_SIZE 4194304 +#define MAXIMUM_REQUEST_SIZE 262144 + +#define CLIENT_TOTAL_STORAGE_SIZE 8388608 +#define SERVER_TOTAL_STORAGE_SIZE 8388608 + +#define STORE_TIME_LIMIT 3600 + +#define STORE_HITS_LOAD_BONUS 10 +#define STORE_HITS_ADD_BONUS 20 +#define STORE_HITS_LIMIT 100 + +#define STORE_HITS_TOUCH 1 +#define STORE_HITS_UNTOUCH 2 + +// +// Default parameters for message splitting. +// + +#define SPLIT_MODE 1 +#define SPLIT_TIMEOUT 50 +#define SPLIT_TOTAL_SIZE 128 +#define SPLIT_TOTAL_STORAGE_SIZE 1048576 +#define SPLIT_DATA_THRESHOLD 65536 +#define SPLIT_DATA_PACKET_LIMIT 24576 + +// +// Agent related parameters. +// + +#define PACK_METHOD 63 +#define PACK_QUALITY 9 +#define HIDE_RENDER 0 +#define TAINT_REPLIES 1 +#define TAINT_THRESHOLD 8 + +// +// In current version only X server support is +// implemented. Note that use of shared memory +// is negotiated according to options provided +// by the user. +// + +#define SHMEM_CLIENT 0 +#define SHMEM_SERVER 1 + +// +// Default size of shared memory segments used +// in MIT-SHM support. +// + +#define SHMEM_CLIENT_SIZE 0 +#define SHMEM_SERVER_SIZE 2097152 + +// +// What do we do at the end of session? If this +// flag is set, we launch a new client letting +// the user run a new NX session. +// + +#define ENABLE_RESTART_ON_SHUTDOWN 0 + +// +// Do we produce a core dump on fatal errors? +// + +#define ENABLE_CORE_DUMP_ON_ABORT 0 + +// +// Reopen the log file if it exceeds this size. +// + +#define FILE_SIZE_LIMIT 60000000 + +// +// Check periodically if we need to truncate the +// log file. By default check every minute. +// + +#define FILE_SIZE_CHECK_TIMEOUT 60000 + +// +// Set defaults for control. They should be what +// you get in case of 'local' connection. +// + +Control::Control() +{ + ProxyMode = proxy_undefined; + ProxyStage = stage_undefined; + SessionMode = session_undefined; + FlushPolicy = policy_undefined; + LinkMode = link_undefined; + + LinkEncrypted = LINK_ENCRYPTED; + FlushPriority = FLUSH_PRIORITY; + + TokenSize = TOKEN_SIZE; + TokenLimit = TOKEN_LIMIT; + + ChannelMask = CHANNEL_MASK; + + InitTimeout = INIT_TIMEOUT; + PingTimeout = PING_TIMEOUT; + MotionTimeout = MOTION_TIMEOUT; + IdleTimeout = IDLE_TIMEOUT; + + ChannelTimeout = CHANNEL_TIMEOUT; + ProxyTimeout = PROXY_TIMEOUT; + ShmemTimeout = SHMEM_TIMEOUT; + + CleanupTimeout = CLEANUP_TIMEOUT; + KeeperTimeout = KEEPER_TIMEOUT; + LatencyTimeout = LATENCY_TIMEOUT; + + FileSizeLimit = FILE_SIZE_LIMIT; + FileSizeCheckTimeout = FILE_SIZE_CHECK_TIMEOUT; + + EnableRestartOnShutdown = ENABLE_RESTART_ON_SHUTDOWN; + + KillDaemonOnShutdownLimit = KILL_LIMIT; + + KillDaemonOnShutdown = new int[KillDaemonOnShutdownLimit]; + + for (int i = 0; i < KILL_LIMIT; i++) + { + KillDaemonOnShutdown[i] = -1; + } + + KillDaemonOnShutdownNumber = 0; + + EnableCoreDumpOnAbort = ENABLE_CORE_DUMP_ON_ABORT; + + // + // Collect statistics by default. + // + + EnableStatistics = 1; + + // + // Memory restrictions if any. + // + + LocalMemoryLevel = -1; + + // + // Compression must be negotiated between proxies. + // + + LocalDeltaCompression = -1; + RemoteDeltaCompression = -1; + + LocalDataCompression = -1; + LocalStreamCompression = -1; + + RemoteDataCompression = -1; + RemoteStreamCompression = -1; + + LocalDataCompressionLevel = -1; + LocalDataCompressionThreshold = -1; + LocalStreamCompressionLevel = -1; + + RemoteDataCompressionLevel = -1; + RemoteStreamCompressionLevel = -1; + + // + // Transport buffers' allocation parameters. + // + + TransportXBufferSize = TRANSPORT_X_BUFFER_SIZE; + TransportProxyBufferSize = TRANSPORT_PROXY_BUFFER_SIZE; + TransportGenericBufferSize = TRANSPORT_GENERIC_BUFFER_SIZE; + + TransportXBufferThreshold = TRANSPORT_X_BUFFER_THRESHOLD; + TransportProxyBufferThreshold = TRANSPORT_PROXY_BUFFER_THRESHOLD; + TransportGenericBufferThreshold = TRANSPORT_GENERIC_BUFFER_THRESHOLD; + + TransportMaximumBufferSize = TRANSPORT_MAXIMUM_BUFFER_SIZE; + + // + // Flush the write buffer if it exceeds + // this size. + // + + TransportFlushBufferSize = TRANSPORT_FLUSH_BUFFER_SIZE; + + // + // Socket options. + // + + OptionProxyKeepAlive = OPTION_PROXY_KEEP_ALIVE; + OptionProxyLowDelay = OPTION_PROXY_LOW_DELAY; + OptionProxyClientNoDelay = OPTION_PROXY_CLIENT_NO_DELAY; + OptionProxyServerNoDelay = OPTION_PROXY_SERVER_NO_DELAY; + OptionClientNoDelay = OPTION_CLIENT_NO_DELAY; + OptionServerNoDelay = OPTION_SERVER_NO_DELAY; + + OptionProxyReceiveBuffer = OPTION_PROXY_RECEIVE_BUFFER; + OptionClientReceiveBuffer = OPTION_CLIENT_RECEIVE_BUFFER; + OptionServerReceiveBuffer = OPTION_SERVER_RECEIVE_BUFFER; + + OptionProxySendBuffer = OPTION_PROXY_SEND_BUFFER; + OptionClientSendBuffer = OPTION_CLIENT_SEND_BUFFER; + OptionServerSendBuffer = OPTION_SERVER_SEND_BUFFER; + + OptionProxyRetryAccept = OPTION_PROXY_RETRY_ACCEPT; + OptionProxyRetryConnect = OPTION_PROXY_RETRY_CONNECT; + OptionServerRetryConnect = OPTION_SERVER_RETRY_CONNECT; + + // + // Base NX directories. + // + + HomePath = NULL; + RootPath = NULL; + SystemPath = NULL; + TempPath = NULL; + ClientPath = NULL; + + // + // Set defaults for handling persistent cache. + // + + PersistentCachePath = NULL; + PersistentCacheName = NULL; + + PersistentCacheThreshold = PERSISTENT_CACHE_THRESHOLD; + + PersistentCacheEnableLoad = PERSISTENT_CACHE_ENABLE_LOAD; + PersistentCacheEnableSave = PERSISTENT_CACHE_ENABLE_SAVE; + + PersistentCacheCheckOnShutdown = PERSISTENT_CACHE_CHECK_ON_SHUTDOWN; + + PersistentCacheLoadPacked = PERSISTENT_CACHE_LOAD_PACKED; + PersistentCacheLoadRender = PERSISTENT_CACHE_LOAD_RENDER; + + PersistentCacheDiskLimit = PERSISTENT_CACHE_DISK_LIMIT; + + // + // Set defaults for image cache. + // + + ImageCachePath = NULL; + + ImageCacheEnableLoad = IMAGE_CACHE_ENABLE_LOAD; + ImageCacheEnableSave = IMAGE_CACHE_ENABLE_SAVE; + + ImageCacheDiskLimit = IMAGE_CACHE_DISK_LIMIT; + + // + // Set defaults for the read buffers. + // + + ClientInitialReadSize = CLIENT_INITIAL_READ_SIZE; + ClientMaximumBufferSize = CLIENT_MAXIMUM_BUFFER_SIZE; + + ServerInitialReadSize = SERVER_INITIAL_READ_SIZE; + ServerMaximumBufferSize = SERVER_MAXIMUM_BUFFER_SIZE; + + ProxyInitialReadSize = PROXY_INITIAL_READ_SIZE; + ProxyMaximumBufferSize = PROXY_MAXIMUM_BUFFER_SIZE; + + GenericInitialReadSize = GENERIC_INITIAL_READ_SIZE; + GenericMaximumBufferSize = GENERIC_MAXIMUM_BUFFER_SIZE; + + ShortBitrateTimeFrame = SHORT_BITRATE_TIME_FRAME; + LongBitrateTimeFrame = LONG_BITRATE_TIME_FRAME; + + // + // Bandwidth control. + // + + LocalBitrateLimit = -1; + + ClientBitrateLimit = CLIENT_BITRATE_LIMIT; + ServerBitrateLimit = SERVER_BITRATE_LIMIT; + + // + // Default parameters for message handling. + // + + ClientTotalStorageSize = CLIENT_TOTAL_STORAGE_SIZE; + ServerTotalStorageSize = SERVER_TOTAL_STORAGE_SIZE; + + LocalTotalStorageSize = -1; + RemoteTotalStorageSize = -1; + + StoreTimeLimit = STORE_TIME_LIMIT; + + StoreHitsLoadBonus = STORE_HITS_LOAD_BONUS; + StoreHitsAddBonus = STORE_HITS_ADD_BONUS; + StoreHitsLimit = STORE_HITS_LIMIT; + + StoreHitsTouch = STORE_HITS_TOUCH; + StoreHitsUntouch = STORE_HITS_UNTOUCH; + + MinimumMessageSize = MINIMUM_MESSAGE_SIZE; + MaximumMessageSize = MAXIMUM_MESSAGE_SIZE; + MaximumRequestSize = MAXIMUM_REQUEST_SIZE; + + SplitMode = SPLIT_MODE; + SplitTimeout = SPLIT_TIMEOUT; + SplitTotalSize = SPLIT_TOTAL_SIZE; + SplitTotalStorageSize = SPLIT_TOTAL_STORAGE_SIZE; + SplitDataThreshold = SPLIT_DATA_THRESHOLD; + SplitDataPacketLimit = SPLIT_DATA_PACKET_LIMIT; + + PackMethod = PACK_METHOD; + PackQuality = PACK_QUALITY; + HideRender = HIDE_RENDER; + TaintReplies = TAINT_REPLIES; + TaintThreshold = TAINT_THRESHOLD; + + ShmemClient = SHMEM_CLIENT; + ShmemServer = SHMEM_SERVER; + + ShmemClientSize = SHMEM_CLIENT_SIZE; + ShmemServerSize = SHMEM_SERVER_SIZE; + + // + // Get local version number from compile time + // settings. Version of remote proxy will be + // checked at connection time. + // + + RemoteVersionMajor = -1; + RemoteVersionMinor = -1; + RemoteVersionPatch = -1; + + CompatVersionMajor = -1; + CompatVersionMinor = -1; + CompatVersionPatch = -1; + + char version[32]; + + strcpy(version, VERSION); + + char *value; + + value = strtok(version, "."); + + for (int i = 0; value != NULL && i < 3; i++) + { + switch (i) + { + case 0: + + LocalVersionMajor = atoi(value); + + break; + + case 1: + + LocalVersionMinor = atoi(value); + + break; + + case 2: + + LocalVersionPatch = atoi(value); + + break; + } + + value = strtok(NULL, "."); + } + + #ifdef TEST + *logofs << "Control: Major version is " << LocalVersionMajor + << " minor is " << LocalVersionMinor << " patch is " + << LocalVersionPatch << ".\n" << logofs_flush; + #endif + + // + // Initialize local implemented methods later + // and negotiate remote methods at connection + // time. + // + + LocalUnpackMethods = NULL; + RemoteUnpackMethods = NULL; + + // + // Set to 1 those methods which are implemented. + // + + setLocalUnpackMethods(); + + // + // Set the protocol version at the + // time the session is negotiated. + // + + protoStep6_ = 0; + protoStep7_ = 0; + protoStep8_ = 0; + protoStep9_ = 0; + protoStep10_ = 0; +} + +Control::~Control() +{ + if (KillDaemonOnShutdown != NULL) + { + delete [] KillDaemonOnShutdown; + } + + if (HomePath != NULL) + { + delete [] HomePath; + } + + if (RootPath != NULL) + { + delete [] RootPath; + } + + if (SystemPath != NULL) + { + delete [] SystemPath; + } + + if (TempPath != NULL) + { + delete [] TempPath; + } + + if (ClientPath != NULL) + { + delete [] ClientPath; + } + + if (PersistentCachePath != NULL) + { + delete [] PersistentCachePath; + } + + if (PersistentCacheName != NULL) + { + delete [] PersistentCacheName; + } + + if (LocalUnpackMethods != NULL) + { + delete [] LocalUnpackMethods; + } + + if (RemoteUnpackMethods != NULL) + { + delete [] RemoteUnpackMethods; + } + + if (ImageCachePath != NULL) + { + delete [] ImageCachePath; + } +} + +// +// Set the protocol step based on the +// remote version. +// + +void Control::setProtoStep(int step) +{ + switch (step) + { + case 6: + { + protoStep6_ = 1; + protoStep7_ = 0; + protoStep8_ = 0; + protoStep9_ = 0; + protoStep10_ = 0; + + break; + } + case 7: + { + protoStep6_ = 1; + protoStep7_ = 1; + protoStep8_ = 0; + protoStep9_ = 0; + protoStep10_ = 0; + + break; + } + case 8: + { + protoStep6_ = 1; + protoStep7_ = 1; + protoStep8_ = 1; + protoStep9_ = 0; + protoStep10_ = 0; + + break; + } + case 9: + { + protoStep6_ = 1; + protoStep7_ = 1; + protoStep8_ = 1; + protoStep9_ = 1; + protoStep10_ = 0; + + break; + } + case 10: + { + protoStep6_ = 1; + protoStep7_ = 1; + protoStep8_ = 1; + protoStep9_ = 1; + protoStep10_ = 1; + + break; + } + default: + { + #ifdef PANIC + *logofs << "Control: PANIC! Invalid protocol step " + << "with value " << step << ".\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + } +} + +int Control::getProtoStep() +{ + if (protoStep10_ == 1) + { + return 10; + } + else if (protoStep9_ == 1) + { + return 9; + } + else if (protoStep8_ == 1) + { + return 8; + } + else if (protoStep7_ == 1) + { + return 7; + } + else if (protoStep6_ == 1) + { + return 6; + } + else + { + #ifdef PANIC + *logofs << "Control: PANIC! Can't identify the " + << "protocol step.\n" << logofs_flush; + #endif + + HandleCleanup(); + } +} + +// +// Set here the pack/unpack methods that are +// implemented by this NX proxy. +// + +void Control::setLocalUnpackMethods() +{ + LocalUnpackMethods = new unsigned char[PACK_METHOD_LIMIT]; + RemoteUnpackMethods = new unsigned char[PACK_METHOD_LIMIT]; + + for (int i = 0; i < PACK_METHOD_LIMIT; i++) + { + LocalUnpackMethods[i] = 0; + RemoteUnpackMethods[i] = 0; + } + + LocalUnpackMethods[NO_PACK] = 1; + + LocalUnpackMethods[PACK_MASKED_8_COLORS] = 1; + LocalUnpackMethods[PACK_MASKED_64_COLORS] = 1; + LocalUnpackMethods[PACK_MASKED_256_COLORS] = 1; + LocalUnpackMethods[PACK_MASKED_512_COLORS] = 1; + LocalUnpackMethods[PACK_MASKED_4K_COLORS] = 1; + LocalUnpackMethods[PACK_MASKED_32K_COLORS] = 1; + LocalUnpackMethods[PACK_MASKED_64K_COLORS] = 1; + LocalUnpackMethods[PACK_MASKED_256K_COLORS] = 1; + LocalUnpackMethods[PACK_MASKED_2M_COLORS] = 1; + LocalUnpackMethods[PACK_MASKED_16M_COLORS] = 1; + + LocalUnpackMethods[PACK_RAW_8_BITS] = 1; + LocalUnpackMethods[PACK_RAW_16_BITS] = 1; + LocalUnpackMethods[PACK_RAW_24_BITS] = 1; + + LocalUnpackMethods[PACK_COLORMAP_256_COLORS] = 1; + + LocalUnpackMethods[PACK_JPEG_8_COLORS] = 1; + LocalUnpackMethods[PACK_JPEG_64_COLORS] = 1; + LocalUnpackMethods[PACK_JPEG_256_COLORS] = 1; + LocalUnpackMethods[PACK_JPEG_512_COLORS] = 1; + LocalUnpackMethods[PACK_JPEG_4K_COLORS] = 1; + LocalUnpackMethods[PACK_JPEG_32K_COLORS] = 1; + LocalUnpackMethods[PACK_JPEG_64K_COLORS] = 1; + LocalUnpackMethods[PACK_JPEG_256K_COLORS] = 1; + LocalUnpackMethods[PACK_JPEG_2M_COLORS] = 1; + LocalUnpackMethods[PACK_JPEG_16M_COLORS] = 1; + + LocalUnpackMethods[PACK_PNG_8_COLORS] = 1; + LocalUnpackMethods[PACK_PNG_64_COLORS] = 1; + LocalUnpackMethods[PACK_PNG_256_COLORS] = 1; + LocalUnpackMethods[PACK_PNG_512_COLORS] = 1; + LocalUnpackMethods[PACK_PNG_4K_COLORS] = 1; + LocalUnpackMethods[PACK_PNG_32K_COLORS] = 1; + LocalUnpackMethods[PACK_PNG_64K_COLORS] = 1; + LocalUnpackMethods[PACK_PNG_256K_COLORS] = 1; + LocalUnpackMethods[PACK_PNG_2M_COLORS] = 1; + LocalUnpackMethods[PACK_PNG_16M_COLORS] = 1; + + LocalUnpackMethods[PACK_RGB_16M_COLORS] = 1; + LocalUnpackMethods[PACK_RLE_16M_COLORS] = 1; + + LocalUnpackMethods[PACK_ALPHA] = 1; + LocalUnpackMethods[PACK_COLORMAP] = 1; + + LocalUnpackMethods[PACK_BITMAP_16M_COLORS] = 1; +} diff --git a/nxcomp/Control.h b/nxcomp/Control.h new file mode 100644 index 000000000..c21477544 --- /dev/null +++ b/nxcomp/Control.h @@ -0,0 +1,747 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Control_H +#define Control_H + +#include "NXpack.h" + +#include "Misc.h" +#include "Types.h" +#include "Timestamp.h" +#include "Statistics.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// This is the mode proxy is running. +// + +typedef enum +{ + proxy_undefined = -1, + proxy_client, + proxy_server, + proxy_last_tag +} +T_proxy_mode; + +// +// Handle advances in the connection +// procedure. +// + +typedef enum +{ + stage_undefined, + stage_initializing, + stage_connecting, + stage_connected, + stage_waiting_forwarder_version, + stage_waiting_forwarder_options, + stage_sending_forwarder_options, + stage_waiting_proxy_version, + stage_waiting_proxy_options, + stage_sending_proxy_options, + stage_waiting_proxy_caches, + stage_sending_proxy_caches, + stage_operational, + stage_terminating, + stage_terminated +} +T_proxy_stage; + +// +// Hint about whether or not the proxy is +// connected to a NX agen. +// + +typedef enum +{ + session_undefined = -1, + session_agent, + session_shadow, + session_proxy, + session_last_tag +} +T_session_mode; + +// +// Set how data will be written to the peer +// socket. +// + +typedef enum +{ + policy_undefined = -1, + policy_immediate, + policy_deferred +} +T_flush_policy; + +// +// Link mode, after negotiation, will be set to +// any of the values defined in the NXproto.h. +// + +#define link_undefined -1; + +// +// This class collects functioning parameters, +// to be configurable at run-time. They are for +// the most part regarding timeouts, transport +// and message stores handling. +// + +class Control +{ + public: + + // + // Does proxy run in client mode or server mode? + // As soon as we'll have gone through parsing of + // the command line options the current mode will + // be propagated to the control class. + // + + T_proxy_mode ProxyMode; + + // + // Goes from initializing to operational. + // + + T_proxy_stage ProxyStage; + + // + // Hint about type of session currently running. + // + + T_session_mode SessionMode; + + // + // Either immediate or defferred flushes. + // + + T_flush_policy FlushPolicy; + + // + // If set, the channels will try to flush the + // encoded data whenever there is a prioritized + // message. Depending on the flush policy, this + // may determine an immediate flush or an event + // being generated telling to the agent that it + // should flush the proxy link. + // + + int FlushPriority; + + // + // Id corresponding to link speed negotiated + // between proxies. + // + + int LinkMode; + + // + // Set if the proxy is connected to a program + // providing the encryption of the point to + // point communication. + // + + int LinkEncrypted; + + // + // Maximum number of bytes sent for each token. + // + + int TokenSize; + + // + // Maximum number of tokens that can be spent + // by the client proxy before having to block + // waiting for a reply. + // + + int TokenLimit; + + // + // Bitmask used to determine the distribution + // of channel ids between the client and server + // proxies. + // + + int ChannelMask; + + // + // Kill session if control parameters cannot + // be negotiated before this timeout. + // + + int InitTimeout; + + // + // Enter the congestion state if the remote does + // not reply to the ping within the given amount + // of time. + // + + int PingTimeout; + + // + // Enqueue motion notify events in server channel. + // + + int MotionTimeout; + + // + // Force an update of the congestion counter if + // the proxy is idle for this time. + // + + int IdleTimeout; + + // + // Close the connection if can't write before + // this timeout. + // + + int ChannelTimeout; + + // + // Close connection if can't write before + // this timeout. + // + + int ProxyTimeout; + + // + // How many milliseconds to wait for the shared + // memory completion event to become available. + // + + int ShmemTimeout; + + // + // Wait for applications to complete at the time + // proxy is shut down. + // + + int CleanupTimeout; + + // + // Wait this amount of milliseconds before any + // iteration of the house-keeping process. + // + + int KeeperTimeout; + + // + // Adjust timeout calculations. + // + + int LatencyTimeout; + + // + // Maximum allowed size of log files. + // + + int FileSizeLimit; + int FileSizeCheckTimeout; + + // + // What do we do at the end of session? If + // this flag is set we launch a new client + // letting the user run a new NX session. + // + + int EnableRestartOnShutdown; + + // + // The client can request the proxy to kill + // a number of processes before exiting. + // + + int *KillDaemonOnShutdown; + int KillDaemonOnShutdownNumber; + int KillDaemonOnShutdownLimit; + + // + // Do we generate a core dump and exit in + // case of program errors? + // + + int EnableCoreDumpOnAbort; + + // + // Is statistic output enabled? + // + + int EnableStatistics; + + // + // Version number of local and remote proxy. + // + + int LocalVersionMajor; + int LocalVersionMinor; + int LocalVersionPatch; + + int RemoteVersionMajor; + int RemoteVersionMinor; + int RemoteVersionPatch; + + int CompatVersionMajor; + int CompatVersionMinor; + int CompatVersionPatch; + + // + // Which unpack methods are implemented in proxy? + // + + unsigned char *LocalUnpackMethods; + unsigned char *RemoteUnpackMethods; + + // + // Memory restriction imposed by user. + // + + int LocalMemoryLevel; + + // + // Use or not differential compression + // and caching of X protocol messages. + // + + int LocalDeltaCompression; + int RemoteDeltaCompression; + + // + // Compression of images and replies. + // + + int LocalDataCompression; + int LocalDataCompressionLevel; + + int RemoteDataCompression; + int RemoteDataCompressionLevel; + + // + // Minimum packet size to be compressed. + // + + int LocalDataCompressionThreshold; + + // + // Compress or not data flowing through the proxy + // link. Level should be one of the ZLIB level as + // Z_DEFAULT_COMPRESSION or Z_BEST_COMPRESSION. + // + + int LocalStreamCompression; + int LocalStreamCompressionLevel; + + int RemoteStreamCompression; + int RemoteStreamCompressionLevel; + + // + // Size of read operations in read buffer classes. + // + + int ClientInitialReadSize; + int ClientMaximumBufferSize; + + int ServerInitialReadSize; + int ServerMaximumBufferSize; + + int ProxyInitialReadSize; + int ProxyMaximumBufferSize; + + int GenericInitialReadSize; + int GenericMaximumBufferSize; + + // + // Set initial size and resize policy of + // transport buffers. If maximum size is + // exceeded, print a warning. + // + + int TransportXBufferSize; + int TransportProxyBufferSize; + int TransportGenericBufferSize; + + int TransportXBufferThreshold; + int TransportProxyBufferThreshold; + int TransportGenericBufferThreshold; + + int TransportMaximumBufferSize; + + // + // Flush the data produced for the channel + // connection if it exceeds this size. + // + + int TransportFlushBufferSize; + + // + // Socket options. + // + + int OptionProxyKeepAlive; + int OptionProxyLowDelay; + int OptionProxyClientNoDelay; + int OptionProxyServerNoDelay; + int OptionClientNoDelay; + int OptionServerNoDelay; + + int OptionProxyReceiveBuffer; + int OptionClientReceiveBuffer; + int OptionServerReceiveBuffer; + + int OptionProxySendBuffer; + int OptionClientSendBuffer; + int OptionServerSendBuffer; + + int OptionProxyRetryAccept; + int OptionProxyRetryConnect; + int OptionServerRetryConnect; + + // + // Calculate current bitrate on proxy link + // using these observation periods. Value + // is in milliseconds. + // + + int ShortBitrateTimeFrame; + int LongBitrateTimeFrame; + + // + // Limit the bandwidth usage of the proxy + // link. + // + + int LocalBitrateLimit; + + int ClientBitrateLimit; + int ServerBitrateLimit; + + // + // This is the limit imposed by user on + // total cache size. + // + + int ClientTotalStorageSize; + int ServerTotalStorageSize; + + int LocalTotalStorageSize; + int RemoteTotalStorageSize; + + // + // Discard messages in store older than + // this amount of seconds. + // + + int StoreTimeLimit; + + // + // Any new message in store starts with + // this amount of hits. + // + + int StoreHitsAddBonus; + + // + // Unless it is loaded from persistent + // cache. + // + + int StoreHitsLoadBonus; + + // + // Stop increasing hits at this threshold. + // + + int StoreHitsLimit; + + // + // Give a special weight to messages put or + // taken from cache during startup time. + // + + int StoreHitsStartup; + + // + // Weight of touch and untoch operations. + // + + int StoreHitsTouch; + int StoreHitsUntouch; + + // + // Directives on size of messages to cache. + // + + int MinimumMessageSize; + int MaximumMessageSize; + + // + // Maximum size of a single X request. + // + + int MaximumRequestSize; + + // + // Currently selected streaming mode. + // + + int SplitMode; + + // + // Send new split data any given amount of + // milliseconds. + // + + int SplitTimeout; + + // + // Maximum number of distinct messages and + // maximum size in bytes of the temporary + // storage. + // + + int SplitTotalSize; + int SplitTotalStorageSize; + + // + // Don't split messages smaller that this + // threshold and send no more than the + // given amount of bytes in a single data + // shot when streaming the split messages. + // + + int SplitDataThreshold; + int SplitDataPacketLimit; + + // + // Agent related parameters. These values apply + // to the agent which, at startup, must query + // the user's settings. + // + + int PackMethod; + int PackQuality; + int HideRender; + int TaintReplies; + int TaintThreshold; + + // + // Do we allow shared memory image support in + // client and or server? + // + + int ShmemClient; + int ShmemServer; + + // + // Default size of shared memory segments used + // in MIT-SHM support. + // + + int ShmemClientSize; + int ShmemServerSize; + + // + // The user's home directory. + // + + char *HomePath; + + // + // The ".nx" directory, usually in + // the user's home. + // + + char *RootPath; + + // + // Usually the /usr/NX" directory. + // + + char *SystemPath; + + // + // Usually the "/tmp" directory. + // + + char *TempPath; + + // + // The complete path to the client. + // + + char *ClientPath; + + // + // String containing path of cache + // file selected for load or save. + // + + char *PersistentCachePath; + + // + // Name of selected cache file. + // + + char *PersistentCacheName; + + // + // Minimum size of cache in memory + // to proceed to its storage on disk. + // + + int PersistentCacheThreshold; + + // + // Is persistent cache enabled? + // + + int PersistentCacheEnableLoad; + int PersistentCacheEnableSave; + + // + // This is used just for test because + // it requires that client and server + // reside on the same machine. + // + + int PersistentCacheCheckOnShutdown; + + // + // Load packed image and render extension + // message stores. This currently depends + // on the type of session. + // + + int PersistentCacheLoadPacked; + int PersistentCacheLoadRender; + + // + // Maximum disk consumption of message + // caches on disk. + // + + int PersistentCacheDiskLimit; + + // + // String containing the base path + // of image cache files. + // + + char *ImageCachePath; + + // + // Is image cache enabled? + // + + int ImageCacheEnableLoad; + int ImageCacheEnableSave; + + // + // Maximum disk consumption of image + // caches on disk. + // + + int ImageCacheDiskLimit; + + // + // Only constructor, destructor + // and a few utility functions. + // + + Control(); + + ~Control(); + + // + // Should not leverage control to find channel + // stores' size limits. As most of values in + // control, this info must be moved elsewhere. + // + + int getUpperStorageSize() const + { + return (ClientTotalStorageSize > + ServerTotalStorageSize ? + ClientTotalStorageSize : + ServerTotalStorageSize); + } + + int getLowerStorageSize() const + { + return (ClientTotalStorageSize < + ServerTotalStorageSize ? + ClientTotalStorageSize : + ServerTotalStorageSize); + } + + void setProtoStep(int step); + + int getProtoStep(); + + int isProtoStep7() + { + return protoStep7_; + } + + int isProtoStep8() + { + return protoStep8_; + } + + int isProtoStep9() + { + return protoStep9_; + } + + int isProtoStep10() + { + return protoStep10_; + } + + private: + + // + // Look in Control.cpp. + // + + void setLocalUnpackMethods(); + + // + // Manage the encoding according + // to the protocol version. + // + + int protoStep6_; + int protoStep7_; + int protoStep8_; + int protoStep9_; + int protoStep10_; +}; + +#endif /* Control_H */ diff --git a/nxcomp/CopyArea.cpp b/nxcomp/CopyArea.cpp new file mode 100644 index 000000000..e384ce13c --- /dev/null +++ b/nxcomp/CopyArea.cpp @@ -0,0 +1,187 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "CopyArea.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +int CopyAreaStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + CopyAreaMessage *copyArea = (CopyAreaMessage *) message; + + // + // Here is the fingerprint. + // + + copyArea -> src_drawable = GetULONG(buffer + 4, bigEndian); + copyArea -> dst_drawable = GetULONG(buffer + 8, bigEndian); + copyArea -> gcontext = GetULONG(buffer + 12, bigEndian); + + copyArea -> src_x = GetUINT(buffer + 16, bigEndian); + copyArea -> src_y = GetUINT(buffer + 18, bigEndian); + copyArea -> dst_x = GetUINT(buffer + 20, bigEndian); + copyArea -> dst_y = GetUINT(buffer + 22, bigEndian); + + copyArea -> width = GetUINT(buffer + 24, bigEndian); + copyArea -> height = GetUINT(buffer + 26, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int CopyAreaStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + CopyAreaMessage *copyArea = (CopyAreaMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(copyArea -> src_drawable, buffer + 4, bigEndian); + PutULONG(copyArea -> dst_drawable, buffer + 8, bigEndian); + PutULONG(copyArea -> gcontext, buffer + 12, bigEndian); + + PutUINT(copyArea -> src_x, buffer + 16, bigEndian); + PutUINT(copyArea -> src_y, buffer + 18, bigEndian); + PutUINT(copyArea -> dst_x, buffer + 20, bigEndian); + PutUINT(copyArea -> dst_y, buffer + 22, bigEndian); + + PutUINT(copyArea -> width, buffer + 24, bigEndian); + PutUINT(copyArea -> height, buffer + 26, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void CopyAreaStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + CopyAreaMessage *copyArea = (CopyAreaMessage *) message; + + *logofs << name() << ": Identity src_drawable " << copyArea -> src_drawable + << ", dst_drawable " << copyArea -> dst_drawable << ", gcontext " << copyArea -> gcontext + << ", src_x " << copyArea -> src_x << ", src_y " << copyArea -> src_y + << ", dst_x " << copyArea -> dst_x << ", dst_y " << copyArea -> dst_y + << ", width " << copyArea -> width << ", height " << copyArea -> height + << ", size " << copyArea -> size_ << ".\n"; + + #endif +} + +void CopyAreaStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 16, 12); +} + +void CopyAreaStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + CopyAreaMessage *copyArea = (CopyAreaMessage *) message; + CopyAreaMessage *cachedCopyArea = (CopyAreaMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << copyArea -> src_drawable + << " as " << "src_drawable" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(copyArea -> src_drawable, clientCache -> drawableCache); + + cachedCopyArea -> src_drawable = copyArea -> src_drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << copyArea -> dst_drawable + << " as " << "dst_drawable" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(copyArea -> dst_drawable, clientCache -> drawableCache); + + cachedCopyArea -> dst_drawable = copyArea -> dst_drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << copyArea -> gcontext + << " as " << "gcontext" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(copyArea -> gcontext, clientCache -> gcCache); + + cachedCopyArea -> gcontext = copyArea -> gcontext; +} + +void CopyAreaStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + CopyAreaMessage *copyArea = (CopyAreaMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + copyArea -> src_drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << copyArea -> src_drawable + << " as " << "src_drawable" << " field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + copyArea -> dst_drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << copyArea -> dst_drawable + << " as " << "dst_drawable" << " field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + copyArea -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << copyArea -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif +} + + diff --git a/nxcomp/CopyArea.h b/nxcomp/CopyArea.h new file mode 100644 index 000000000..a811f3801 --- /dev/null +++ b/nxcomp/CopyArea.h @@ -0,0 +1,184 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef CopyArea_H +#define CopyArea_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define COPYAREA_ENABLE_CACHE 1 +#define COPYAREA_ENABLE_DATA 0 +#define COPYAREA_ENABLE_SPLIT 0 +#define COPYAREA_ENABLE_COMPRESS 0 + +#define COPYAREA_DATA_LIMIT 0 +#define COPYAREA_DATA_OFFSET 28 + +#define COPYAREA_CACHE_SLOTS 3000 +#define COPYAREA_CACHE_THRESHOLD 5 +#define COPYAREA_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class CopyAreaMessage : public Message +{ + friend class CopyAreaStore; + + public: + + CopyAreaMessage() + { + } + + ~CopyAreaMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int src_drawable; + unsigned int dst_drawable; + unsigned int gcontext; + unsigned short src_x; + unsigned short src_y; + unsigned short dst_x; + unsigned short dst_y; + unsigned short width; + unsigned short height; +}; + +class CopyAreaStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + CopyAreaStore() : MessageStore() + { + enableCache = COPYAREA_ENABLE_CACHE; + enableData = COPYAREA_ENABLE_DATA; + enableSplit = COPYAREA_ENABLE_SPLIT; + enableCompress = COPYAREA_ENABLE_COMPRESS; + + dataLimit = COPYAREA_DATA_LIMIT; + dataOffset = COPYAREA_DATA_OFFSET; + + cacheSlots = COPYAREA_CACHE_SLOTS; + cacheThreshold = COPYAREA_CACHE_THRESHOLD; + cacheLowerThreshold = COPYAREA_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~CopyAreaStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "CopyArea"; + } + + virtual unsigned char opcode() const + { + return X_CopyArea; + } + + virtual unsigned int storage() const + { + return sizeof(CopyAreaMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new CopyAreaMessage(); + } + + virtual Message *create(const Message &message) const + { + return new CopyAreaMessage((const CopyAreaMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (CopyAreaMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* CopyArea_H */ diff --git a/nxcomp/CreateGC.cpp b/nxcomp/CreateGC.cpp new file mode 100644 index 000000000..f1c10e69b --- /dev/null +++ b/nxcomp/CreateGC.cpp @@ -0,0 +1,226 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "CreateGC.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int CreateGCStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + CreateGCMessage *createGC = (CreateGCMessage *) message; + + // + // Here is the fingerprint. + // + + createGC -> gcontext = GetULONG(buffer + 4, bigEndian); + createGC -> drawable = GetULONG(buffer + 8, bigEndian); + createGC -> value_mask = GetULONG(buffer + 12, bigEndian); + + // + // Clear the unused bytes carried in the + // payload to increase the effectiveness + // of the caching algorithm. + // + + if ((int) size > dataOffset) + { + #ifdef DEBUG + *logofs << name() << ": Removing unused bytes from the " + << "data payload.\n" << logofs_flush; + #endif + + createGC -> value_mask &= (1 << 23) - 1; + + unsigned int mask = 0x1; + unsigned char *source = (unsigned char *) buffer + CREATEGC_DATA_OFFSET; + unsigned long value = 0; + + for (unsigned int i = 0; i < 23; i++) + { + if (createGC -> value_mask & mask) + { + value = GetULONG(source, bigEndian); + + value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i])); + + PutULONG(value, source, bigEndian); + + source += 4; + } + + mask <<= 1; + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int CreateGCStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + CreateGCMessage *createGC = (CreateGCMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(createGC -> gcontext, buffer + 4, bigEndian); + PutULONG(createGC -> drawable, buffer + 8, bigEndian); + PutULONG(createGC -> value_mask, buffer + 12, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void CreateGCStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + CreateGCMessage *createGC = (CreateGCMessage *) message; + + *logofs << name() << ": Identity gcontext " << createGC -> gcontext << ", drawable " + << createGC -> drawable << ", value_mask " << createGC -> value_mask + << ", size " << createGC -> size_ << ".\n" << logofs_flush; + #endif +} + +void CreateGCStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // This didn't include the drawable + // in previous versions. + // + + md5_append(md5_state_, buffer + 8, 8); +} + +void CreateGCStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + CreateGCMessage *createGC = (CreateGCMessage *) message; + CreateGCMessage *cachedCreateGC = (CreateGCMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + if (control -> isProtoStep7() == 1) + { + #ifdef TEST + *logofs << name() << ": Encoding value " << createGC -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeNewXidValue(createGC -> gcontext, clientCache -> lastId, + clientCache -> lastIdCache, clientCache -> gcCache, + clientCache -> freeGCCache); + + cachedCreateGC -> gcontext = createGC -> gcontext; + } + else + { + #ifdef TEST + *logofs << name() << ": Encoding value " << createGC -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(createGC -> drawable, clientCache -> drawableCache); + + cachedCreateGC -> drawable = createGC -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << createGC -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(createGC -> gcontext, clientCache -> gcCache); + + cachedCreateGC -> gcontext = createGC -> gcontext; + } +} + +void CreateGCStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + CreateGCMessage *createGC = (CreateGCMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeNewXidValue(value, clientCache -> lastId, + clientCache -> lastIdCache, clientCache -> gcCache, + clientCache -> freeGCCache); + + createGC -> gcontext = value; + + #ifdef TEST + *logofs << name() << ": Decoded value " << createGC -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + } + else + { + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + createGC -> drawable = value; + + #ifdef TEST + *logofs << name() << ": Decoded value " << createGC -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + createGC -> gcontext = value; + + #ifdef TEST + *logofs << name() << ": Decoded value " << createGC -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + } +} diff --git a/nxcomp/CreateGC.h b/nxcomp/CreateGC.h new file mode 100644 index 000000000..b77f13c47 --- /dev/null +++ b/nxcomp/CreateGC.h @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef CreateGC_H +#define CreateGC_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define CREATEGC_ENABLE_CACHE 1 +#define CREATEGC_ENABLE_DATA 0 +#define CREATEGC_ENABLE_SPLIT 0 +#define CREATEGC_ENABLE_COMPRESS 0 + +#define CREATEGC_DATA_LIMIT 144 +#define CREATEGC_DATA_OFFSET 16 + +#define CREATEGC_CACHE_SLOTS 2000 +#define CREATEGC_CACHE_THRESHOLD 2 +#define CREATEGC_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class CreateGCMessage : public Message +{ + friend class CreateGCStore; + + public: + + CreateGCMessage() + { + } + + ~CreateGCMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int gcontext; + unsigned int drawable; + unsigned int value_mask; +}; + +class CreateGCStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + CreateGCStore() : MessageStore() + { + enableCache = CREATEGC_ENABLE_CACHE; + enableData = CREATEGC_ENABLE_DATA; + enableSplit = CREATEGC_ENABLE_SPLIT; + enableCompress = CREATEGC_ENABLE_COMPRESS; + + dataLimit = CREATEGC_DATA_LIMIT; + dataOffset = CREATEGC_DATA_OFFSET; + + cacheSlots = CREATEGC_CACHE_SLOTS; + cacheThreshold = CREATEGC_CACHE_THRESHOLD; + cacheLowerThreshold = CREATEGC_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~CreateGCStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "CreateGC"; + } + + virtual unsigned char opcode() const + { + return X_CreateGC; + } + + virtual unsigned int storage() const + { + return sizeof(CreateGCMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new CreateGCMessage(); + } + + virtual Message *create(const Message &message) const + { + return new CreateGCMessage((const CreateGCMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (CreateGCMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* CreateGC_H */ diff --git a/nxcomp/CreatePixmap.cpp b/nxcomp/CreatePixmap.cpp new file mode 100644 index 000000000..403786747 --- /dev/null +++ b/nxcomp/CreatePixmap.cpp @@ -0,0 +1,268 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "CreatePixmap.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Constructors and destructors. +// + +CreatePixmapStore::CreatePixmapStore() + + : MessageStore() +{ + enableCache = CREATEPIXMAP_ENABLE_CACHE; + enableData = CREATEPIXMAP_ENABLE_DATA; + enableSplit = CREATEPIXMAP_ENABLE_SPLIT; + enableCompress = CREATEPIXMAP_ENABLE_COMPRESS; + + dataLimit = CREATEPIXMAP_DATA_LIMIT; + dataOffset = CREATEPIXMAP_DATA_OFFSET; + + cacheSlots = CREATEPIXMAP_CACHE_SLOTS; + cacheThreshold = CREATEPIXMAP_CACHE_THRESHOLD; + cacheLowerThreshold = CREATEPIXMAP_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +CreatePixmapStore::~CreatePixmapStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int CreatePixmapStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + clientCache -> depthCache); + + encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> drawableCache, + clientCache -> freeDrawableCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> windowCache); + + encodeBuffer.encodeCachedValue(GetUINT(buffer + 12, bigEndian), 16, + clientCache -> createPixmapXCache, 8); + + encodeBuffer.encodeCachedValue(GetUINT(buffer + 14, bigEndian), 16, + clientCache -> createPixmapYCache, 8); + + #ifdef TEST + *logofs << name() << ": Encoded message. Size is " + << size << ".\n" << logofs_flush; + #endif + + return 1; +} + +int CreatePixmapStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned char cValue; + unsigned int value; + + size = 16; + + buffer = writeBuffer -> addMessage(size); + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + + *(buffer + 1) = cValue; + + decodeBuffer.decodeNewXidValue(value, + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> drawableCache, + clientCache -> freeDrawableCache); + + PutULONG(value, buffer + 4, bigEndian); + + decodeBuffer.decodeXidValue(value, + clientCache -> windowCache); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> createPixmapXCache, 8); + + PutUINT(value, buffer + 12, bigEndian); + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> createPixmapYCache, 8); + + PutUINT(value, buffer + 14, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Size is " + << size << ".\n" << logofs_flush; + #endif + + return 1; +} + +int CreatePixmapStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message; + + createPixmap -> depth = *(buffer + 1); + + createPixmap -> id = GetULONG(buffer + 4, bigEndian); + createPixmap -> drawable = GetULONG(buffer + 8, bigEndian); + + createPixmap -> width = GetUINT(buffer + 12, bigEndian); + createPixmap -> height = GetUINT(buffer + 14, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Size is " + << createPixmap -> size_ << " identity is " + << createPixmap -> i_size_ << ".\n" + << logofs_flush; + #endif + + return 1; +} + +int CreatePixmapStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message; + + *(buffer + 1) = createPixmap -> depth; + + PutULONG(createPixmap -> id, buffer + 4, bigEndian); + PutULONG(createPixmap -> drawable, buffer + 8, bigEndian); + + PutUINT(createPixmap -> width, buffer + 12, bigEndian); + PutUINT(createPixmap -> height, buffer + 14, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Size is " + << createPixmap -> size_ << " identity is " + << createPixmap -> i_size_ << ".\n" + << logofs_flush; + #endif + + return 1; +} + +void CreatePixmapStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + #ifdef WARNING + *logofs << name() << ": WARNING! Dump of identity not implemented.\n" + << logofs_flush; + #endif + + #endif +} + +void CreatePixmapStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 1, 1); + md5_append(md5_state_, buffer + 8, 8); +} + +void CreatePixmapStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message; + CreatePixmapMessage *cachedCreatePixmap = (CreatePixmapMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeNewXidValue(createPixmap -> id, + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> drawableCache, + clientCache -> freeDrawableCache); + + cachedCreatePixmap -> id = createPixmap -> id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Size is " + << createPixmap -> size_ << " identity is " + << createPixmap -> i_size_ << ".\n" + << logofs_flush; + #endif +} + +void CreatePixmapStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeNewXidValue(createPixmap -> id, + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> drawableCache, + clientCache -> freeDrawableCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Size is " + << createPixmap -> size_ << " identity is " + << createPixmap -> i_size_ << ".\n" + << logofs_flush; + #endif +} diff --git a/nxcomp/CreatePixmap.h b/nxcomp/CreatePixmap.h new file mode 100644 index 000000000..1d742e452 --- /dev/null +++ b/nxcomp/CreatePixmap.h @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef CreatePixmap_H +#define CreatePixmap_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define CREATEPIXMAP_ENABLE_CACHE 1 +#define CREATEPIXMAP_ENABLE_DATA 0 +#define CREATEPIXMAP_ENABLE_SPLIT 0 +#define CREATEPIXMAP_ENABLE_COMPRESS 0 + +#define CREATEPIXMAP_DATA_LIMIT 16 +#define CREATEPIXMAP_DATA_OFFSET 16 + +#define CREATEPIXMAP_CACHE_SLOTS 1000 +#define CREATEPIXMAP_CACHE_THRESHOLD 2 +#define CREATEPIXMAP_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class CreatePixmapMessage : public Message +{ + friend class CreatePixmapStore; + + public: + + CreatePixmapMessage() + { + } + + ~CreatePixmapMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char depth; + + unsigned int id; + unsigned int drawable; + + unsigned short width; + unsigned short height; +}; + +class CreatePixmapStore : public MessageStore +{ + public: + + CreatePixmapStore(); + + virtual ~CreatePixmapStore(); + + virtual const char *name() const + { + return "CreatePixmap"; + } + + virtual unsigned char opcode() const + { + return X_CreatePixmap; + } + + virtual unsigned int storage() const + { + return sizeof(CreatePixmapMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new CreatePixmapMessage(); + } + + virtual Message *create(const Message &message) const + { + return new CreatePixmapMessage((const CreatePixmapMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (CreatePixmapMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* CreatePixmap_H */ diff --git a/nxcomp/CreatePixmapCompat.cpp b/nxcomp/CreatePixmapCompat.cpp new file mode 100644 index 000000000..6ea346ee1 --- /dev/null +++ b/nxcomp/CreatePixmapCompat.cpp @@ -0,0 +1,272 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "CreatePixmapCompat.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Constructors and destructors. +// + +CreatePixmapCompatStore::CreatePixmapCompatStore() + + : MessageStore() +{ + enableCache = CREATEPIXMAP_ENABLE_CACHE; + enableData = CREATEPIXMAP_ENABLE_DATA; + enableSplit = CREATEPIXMAP_ENABLE_SPLIT; + enableCompress = CREATEPIXMAP_ENABLE_COMPRESS; + + dataLimit = CREATEPIXMAP_DATA_LIMIT; + dataOffset = CREATEPIXMAP_DATA_OFFSET; + + cacheSlots = CREATEPIXMAP_CACHE_SLOTS; + cacheThreshold = CREATEPIXMAP_CACHE_THRESHOLD; + cacheLowerThreshold = CREATEPIXMAP_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +CreatePixmapCompatStore::~CreatePixmapCompatStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int CreatePixmapCompatStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + clientCache -> depthCache); + + encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian), + clientCache -> createPixmapLastId, 29, + clientCache -> createPixmapIdCache, 4); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> drawableCache); + + encodeBuffer.encodeCachedValue(GetUINT(buffer + 12, bigEndian), 16, + clientCache -> createPixmapXCache, 8); + + encodeBuffer.encodeCachedValue(GetUINT(buffer + 14, bigEndian), 16, + clientCache -> createPixmapYCache, 8); + + #ifdef TEST + *logofs << name() << ": Encoded message. Size is " + << size << ".\n" << logofs_flush; + #endif + + return 1; +} + +int CreatePixmapCompatStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned char cValue; + unsigned int value; + + size = 16; + + buffer = writeBuffer -> addMessage(size); + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + + *(buffer + 1) = cValue; + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> createPixmapLastId, 29, + clientCache -> createPixmapIdCache, 4); + + PutULONG(value, buffer + 4, bigEndian); + + decodeBuffer.decodeXidValue(value, + clientCache -> drawableCache); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> createPixmapXCache, 8); + + PutUINT(value, buffer + 12, bigEndian); + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> createPixmapYCache, 8); + + PutUINT(value, buffer + 14, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Size is " + << size << ".\n" << logofs_flush; + #endif + + return 1; +} + +int CreatePixmapCompatStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message; + + createPixmap -> depth = *(buffer + 1); + + createPixmap -> id = GetULONG(buffer + 4, bigEndian); + createPixmap -> drawable = GetULONG(buffer + 8, bigEndian); + + createPixmap -> width = GetUINT(buffer + 12, bigEndian); + createPixmap -> height = GetUINT(buffer + 14, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Size is " + << createPixmap -> size_ << " identity is " + << createPixmap -> i_size_ << ".\n" + << logofs_flush; + #endif + + return 1; +} + +int CreatePixmapCompatStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message; + + *(buffer + 1) = createPixmap -> depth; + + PutULONG(createPixmap -> id, buffer + 4, bigEndian); + PutULONG(createPixmap -> drawable, buffer + 8, bigEndian); + + PutUINT(createPixmap -> width, buffer + 12, bigEndian); + PutUINT(createPixmap -> height, buffer + 14, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Size is " + << createPixmap -> size_ << " identity is " + << createPixmap -> i_size_ << ".\n" + << logofs_flush; + #endif + + return 1; +} + +void CreatePixmapCompatStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + #ifdef WARNING + *logofs << name() << ": WARNING! Dump of identity not implemented.\n" + << logofs_flush; + #endif + + #endif +} + +void CreatePixmapCompatStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 1, 1); + md5_append(md5_state_, buffer + 8, 8); +} + +void CreatePixmapCompatStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message; + CreatePixmapCompatMessage *cachedCreatePixmap = (CreatePixmapCompatMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeDiffCachedValue(createPixmap -> id, + clientCache -> createPixmapLastId, 29, + clientCache -> createPixmapIdCache, 4); + + cachedCreatePixmap -> id = createPixmap -> id; + + encodeBuffer.encodeXidValue(createPixmap -> drawable, + clientCache -> drawableCache); + + cachedCreatePixmap -> drawable = createPixmap -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoded update. Size is " + << createPixmap -> size_ << " identity is " + << createPixmap -> i_size_ << ".\n" + << logofs_flush; + #endif +} + +void CreatePixmapCompatStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + CreatePixmapCompatMessage *createPixmap = (CreatePixmapCompatMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeDiffCachedValue(createPixmap -> id, + clientCache -> createPixmapLastId, 29, + clientCache -> createPixmapIdCache, 4); + + decodeBuffer.decodeXidValue(createPixmap -> drawable, + clientCache -> drawableCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Size is " + << createPixmap -> size_ << " identity is " + << createPixmap -> i_size_ << ".\n" + << logofs_flush; + #endif +} diff --git a/nxcomp/CreatePixmapCompat.h b/nxcomp/CreatePixmapCompat.h new file mode 100644 index 000000000..e8cf8d99f --- /dev/null +++ b/nxcomp/CreatePixmapCompat.h @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef CreatePixmapCompat_H +#define CreatePixmapCompat_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define CREATEPIXMAP_ENABLE_CACHE 1 +#define CREATEPIXMAP_ENABLE_DATA 0 +#define CREATEPIXMAP_ENABLE_SPLIT 0 +#define CREATEPIXMAP_ENABLE_COMPRESS 0 + +#define CREATEPIXMAP_DATA_LIMIT 16 +#define CREATEPIXMAP_DATA_OFFSET 16 + +#define CREATEPIXMAP_CACHE_SLOTS 1000 +#define CREATEPIXMAP_CACHE_THRESHOLD 2 +#define CREATEPIXMAP_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class CreatePixmapCompatMessage : public Message +{ + friend class CreatePixmapCompatStore; + + public: + + CreatePixmapCompatMessage() + { + } + + ~CreatePixmapCompatMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char depth; + + unsigned int id; + unsigned int drawable; + + unsigned short width; + unsigned short height; +}; + +class CreatePixmapCompatStore : public MessageStore +{ + public: + + CreatePixmapCompatStore(); + + virtual ~CreatePixmapCompatStore(); + + virtual const char *name() const + { + return "CreatePixmapCompat"; + } + + virtual unsigned char opcode() const + { + return X_CreatePixmap; + } + + virtual unsigned int storage() const + { + return sizeof(CreatePixmapCompatMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new CreatePixmapCompatMessage(); + } + + virtual Message *create(const Message &message) const + { + return new CreatePixmapCompatMessage((const CreatePixmapCompatMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (CreatePixmapCompatMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* CreatePixmapCompat_H */ diff --git a/nxcomp/DecodeBuffer.cpp b/nxcomp/DecodeBuffer.cpp new file mode 100644 index 000000000..077bfdfc0 --- /dev/null +++ b/nxcomp/DecodeBuffer.cpp @@ -0,0 +1,692 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Misc.h" +#include "Control.h" + +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +DecodeBuffer::DecodeBuffer(const unsigned char *data, unsigned int length) + + : buffer_(data), end_(buffer_ + length), nextSrc_(buffer_), srcMask_(0x80) +{ + if (control -> isProtoStep7() == 1) + { + end_ = buffer_ + length - DECODE_BUFFER_POSTFIX_SIZE; + } +} + +int DecodeBuffer::decodeValue(unsigned int &value, unsigned int numBits, + unsigned int blockSize, int endOkay) +{ + #ifdef DUMP + *logofs << "DecodeBuffer: Decoding " << numBits + << " bits value with block " << blockSize + << " and " << (nextSrc_ - buffer_) + << " bytes in buffer.\n" << logofs_flush; + #endif + + unsigned int result = 0; + unsigned int destMask = 0x1; + unsigned int bitsRead = 0; + + if (blockSize == 0) + blockSize = numBits; + + unsigned char nextSrcChar = *nextSrc_; + unsigned int numBlocks = 1; + + do + { + if (numBlocks == 4) + { + blockSize = numBits; + } + + unsigned int bitsToRead = (blockSize > numBits - bitsRead ? + numBits - bitsRead : blockSize); + unsigned int count = 0; + unsigned char lastBit; + + do + { + if (nextSrc_ >= end_) + { + if (!endOkay) + { + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [A] " + << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) + << " end_ = " << (end_ - buffer_) << ".\n" + << logofs_flush; + #endif + + // + // Label "context" is just used to identify + // the routine which detected the problem in + // present source file. + // + + cerr << "Error" << ": Failure decoding data in context [A].\n"; + + HandleAbort(); + } + + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [B] " + << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) + << " end_ = " << (end_ - buffer_) << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [B].\n"; + + HandleAbort(); + } + + lastBit = (nextSrcChar & srcMask_); + + if (lastBit) + result |= destMask; + + srcMask_ >>= 1; + + if (srcMask_ == 0) + { + srcMask_ = 0x80; + nextSrc_++; + nextSrcChar = *nextSrc_; + } + + destMask <<= 1; + } + while (bitsToRead > ++count); + + bitsRead += bitsToRead; + + if (bitsRead < numBits) + { + if (nextSrc_ >= end_) + { + if (!endOkay) + { + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [C] " + << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) + << " end_ = " << (end_ - buffer_) << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [C].\n"; + + HandleAbort(); + } + + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [D] " + << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) + << " end_ = " << (end_ - buffer_) << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [D].\n"; + + HandleAbort(); + } + + unsigned char moreData = (nextSrcChar & srcMask_); + + srcMask_ >>= 1; + + if (srcMask_ == 0) + { + srcMask_ = 0x80; + nextSrc_++; + nextSrcChar = *nextSrc_; + } + + if (!moreData) + { + if (lastBit) + { + do + { + result |= destMask; + destMask <<= 1; + } + while (numBits > ++bitsRead); + } + else + bitsRead = numBits; + } + } + + blockSize >>= 1; + + if (blockSize < 2) + blockSize = 2; + + numBlocks++; + } + while (numBits > bitsRead); + + value = result; + + return 1; +} + +int DecodeBuffer::decodeCachedValue(unsigned int &value, unsigned int numBits, + IntCache &cache, unsigned int blockSize, + int endOkay) +{ + #ifdef DUMP + *logofs << "DecodeBuffer: Decoding " << numBits + << " bits cached value with block " << blockSize + << " and " << (nextSrc_ - buffer_) + << " bytes in buffer.\n" << logofs_flush; + #endif + + if (nextSrc_ >= end_) + { + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [E] " + << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) + << " end_ = " << (end_ - buffer_) << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [E].\n"; + + HandleAbort(); + } + + unsigned int index = 0; + unsigned char nextSrcChar = *nextSrc_; + + while (!(nextSrcChar & srcMask_)) + { + index++; + srcMask_ >>= 1; + if (srcMask_ == 0) + { + srcMask_ = 0x80; + nextSrc_++; + if (nextSrc_ >= end_) + { + if (!endOkay) + { + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [F] " + << "in decodeCachedValue() nextSrc_ = " + << (nextSrc_ - buffer_) << " end_ = " + << (end_ - buffer_) << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [F].\n"; + + HandleAbort(); + } + + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [G] " + << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_) + << " end_ = " << (end_ - buffer_) << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [G].\n"; + + HandleAbort(); + } + + nextSrcChar = *nextSrc_; + } + } + + srcMask_ >>= 1; + + if (srcMask_ == 0) + { + srcMask_ = 0x80; + nextSrc_++; + } + + if (index == 2) + { + if (control -> isProtoStep8() == 1) + { + blockSize = cache.getBlockSize(blockSize); + + if (decodeValue(value, numBits, blockSize, endOkay)) + { + cache.insert(value, IntMask[numBits]); + + return 1; + } + + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [H] " + << "in decodeCacheValue() with no value found.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [H].\n"; + + HandleAbort(); + } + else + { + unsigned int sameDiff; + + decodeBoolValue(sameDiff); + + if (sameDiff) + { + value = cache.getLastDiff(IntMask[numBits]); + + cache.insert(value, IntMask[numBits]); + + return 1; + } + else + { + blockSize = cache.getBlockSize(blockSize); + + if (decodeValue(value, numBits, blockSize, endOkay)) + { + cache.insert(value, IntMask[numBits]); + + return 1; + } + + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [H] " + << "in decodeCacheValue() with no value found.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [H].\n"; + + HandleAbort(); + } + } + } + else + { + if (index > 2) + { + index--; + } + + if (index > cache.getSize()) + { + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [I] " + << "in decodeCachedValue() index = " << index + << " cache size = " << cache.getSize() << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [I].\n"; + + HandleAbort(); + } + + value = cache.get(index); + + return 1; + } +} + +int DecodeBuffer::decodeCachedValue(unsigned char &value, unsigned int numBits, + CharCache &cache, unsigned int blockSize, + int endOkay) +{ + #ifdef DUMP + *logofs << "DecodeBuffer: Decoding " << numBits + << " bits char cached value with block " << blockSize + << " and " << nextSrc_ - buffer_ << " bytes read out of " + << end_ - buffer_ << ".\n" << logofs_flush; + #endif + + if (nextSrc_ >= end_) + { + #ifdef TEST + *logofs << "DecodeBuffer: End of buffer reached in context [J] with " + << nextSrc_ - buffer_ << " bytes read out of " + << end_ - buffer_ << ".\n" << logofs_flush; + #endif + + return 0; + } + + unsigned int index = 0; + unsigned char nextSrcChar = *nextSrc_; + + while (!(nextSrcChar & srcMask_)) + { + index++; + srcMask_ >>= 1; + + if (srcMask_ == 0) + { + srcMask_ = 0x80; + nextSrc_++; + + if (nextSrc_ >= end_) + { + if (!endOkay) + { + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [K] " + << "in decodeCachedValue() nextSrc_ " + << (nextSrc_ - buffer_) << " end_ " << (end_ - buffer_) + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [K].\n"; + + HandleAbort(); + } + + #ifdef TEST + *logofs << "DecodeBuffer: End of buffer reached in context [L] with " + << nextSrc_ - buffer_ << " bytes read out of " + << end_ - buffer_ << ".\n" << logofs_flush; + #endif + + return 0; + } + + nextSrcChar = *nextSrc_; + } + } + + srcMask_ >>= 1; + + if (srcMask_ == 0) + { + srcMask_ = 0x80; + nextSrc_++; + } + + if (index == 2) + { + unsigned int temp; + + if (decodeValue(temp, numBits, blockSize, endOkay)) + { + value = (unsigned char) temp; + + cache.insert(value); + } + else + { + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [M] " + << "in decodeValue() with index = 2.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [M].\n"; + + HandleAbort(); + } + } + else + { + if (index > 2) + { + index--; + } + + if (index > cache.getSize()) + { + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [N] " + << "in decodeCachedValue() " << "index = " << index + << " cache size = " << cache.getSize() << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [N].\n"; + + HandleAbort(); + } + + value = cache.get(index); + } + + return 1; +} + +// +// Simply returns a pointer to the correct spot in +// the internal buffer. If the caller needs this +// data to last beyond the lifetime of the internal +// buffer, it must copy the data in its own memory. +// + +const unsigned char *DecodeBuffer::decodeMemory(unsigned int numBytes) +{ + #ifdef DUMP + *logofs << "DecodeBuffer: Decoding " << numBytes + << " bytes of memory with " << (nextSrc_ - buffer_) + << " bytes in buffer.\n" << logofs_flush; + #endif + + const unsigned char *result; + + // + // Force ourselves to a byte boundary. + // Is up to application to ensure data + // is word alligned when needed. + // + + if (srcMask_ != 0x80) + { + srcMask_ = 0x80; + nextSrc_++; + } + + result = nextSrc_; + + if (numBytes > DECODE_BUFFER_OVERFLOW_SIZE) + { + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Can't decode a buffer of " + << numBytes << " bytes with limit set to " + << DECODE_BUFFER_OVERFLOW_SIZE << ".\n" + << logofs_flush; + + *logofs << "DecodeBuffer: PANIC! Assuming failure decoding " + << "data in context [O].\n" << logofs_flush; + #endif + + cerr << "Error" << ": Should never decode buffer of size " + << "greater than " << DECODE_BUFFER_OVERFLOW_SIZE + << " bytes.\n"; + + cerr << "Error" << ": Assuming failure decoding data in " + << "context [O].\n"; + + HandleAbort(); + } + else if (end_ - nextSrc_ < (int) numBytes) + { + #ifdef PANIC + *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [P] " + << "in decodeMemory() " << "with length " << numBytes + << " and " << (end_ - nextSrc_) + << " bytes remaining.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Failure decoding data in context [P].\n"; + + HandleAbort(); + } + + nextSrc_ += numBytes; + + return result; +} + +void DecodeBuffer::decodeActionValue(unsigned char &value, unsigned short &position, + ActionCache &cache) +{ + unsigned int t; + + decodeCachedValue(t, 15, *(cache.base_[cache.slot_])); + + cache.last_ += t; + cache.last_ &= 0x7fff; + + value = cache.last_ >> 13; + + position = cache.last_ & 0x1fff; + + #ifdef DEBUG + *logofs << "DecodeBuffer: Decoded value " + << (unsigned) value << " and position " + << position << " with base " << cache.slot_ + << ".\n" << logofs_flush; + #endif + + #ifdef DEBUG + *logofs << "DecodeBuffer: Action block prediction is " + << (*(cache.base_[cache.slot_])).getBlockSize(15) + << ".\n" << logofs_flush; + #endif + + cache.slot_ = (cache.last_ & 0xff); +} + +void DecodeBuffer::decodeNewXidValue(unsigned int &value, unsigned int &lastId, + IntCache &lastIdCache, IntCache &cache, + FreeCache &freeCache) +{ + decodeCachedValue(value, 29, lastIdCache); + + lastId += (value + 1); + lastId &= 0x1fffffff; + + value = lastId; + + cache.push(value, 0x1fffffff); + + freeCache.push(value, 0x1fffffff); +} + +void DecodeBuffer::decodeNewXidValue(unsigned int &value, unsigned int &lastId, + IntCache &lastIdCache, XidCache &cache, + FreeCache &freeCache) +{ + decodeCachedValue(value, 29, lastIdCache); + + #ifdef DEBUG + *logofs << "DecodeBuffer: Decoded new Xid difference " + << value << ".\n" << logofs_flush; + #endif + + lastId += (value + 1); + lastId &= 0x1fffffff; + + value = lastId; + + unsigned int t = (value - cache.last_); + + cache.last_ = value; + + #ifdef DEBUG + *logofs << "DecodeBuffer: Decoded new Xid " << value + << " with base " << cache.slot_ << ".\n" + << logofs_flush; + #endif + + cache.slot_ = (value & 0xff); + + cache.base_[cache.slot_] -> push(t, 0x1fffffff); + + freeCache.push(value, 0x1fffffff); +} + +void DecodeBuffer::decodeXidValue(unsigned int &value, XidCache &cache) +{ + unsigned int t; + + decodeCachedValue(t, 29, *(cache.base_[cache.slot_])); + + cache.last_ += t; + cache.last_ &= 0x1fffffff; + + value = cache.last_; + + #ifdef DEBUG + *logofs << "DecodeBuffer: Decoded Xid " << value + << " with base " << cache.slot_ << ".\n" + << logofs_flush; + #endif + + cache.slot_ = (value & 0xff); + + #ifdef DEBUG + *logofs << "DecodeBuffer: Xid block prediction is " + << (*(cache.base_[cache.slot_])).getBlockSize(29) + << ".\n" << logofs_flush; + #endif +} + +void DecodeBuffer::decodeFreeXidValue(unsigned int &value, FreeCache &cache) +{ + decodeCachedValue(value, 29, cache); +} + +void DecodeBuffer::decodePositionValueCompat(short int &value, PositionCacheCompat &cache) +{ + unsigned int t; + + decodeCachedValue(t, 13, *(cache.base_[cache.slot_])); + + cache.last_ += t; + cache.last_ &= 0x1fff; + + value = cache.last_; + + #ifdef DEBUG + *logofs << "DecodeBuffer: Decoded position " + << value << " with base " << cache.slot_ + << ".\n" << logofs_flush; + #endif + + #ifdef DEBUG + *logofs << "DecodeBuffer: Position block prediction is " + << (*(cache.base_[cache.slot_])).getBlockSize(13) + << ".\n" << logofs_flush; + #endif + + cache.slot_ = (value & 0x1f); +} diff --git a/nxcomp/DecodeBuffer.h b/nxcomp/DecodeBuffer.h new file mode 100644 index 000000000..9345f4e23 --- /dev/null +++ b/nxcomp/DecodeBuffer.h @@ -0,0 +1,142 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef DecodeBuffer_H +#define DecodeBuffer_H + +#include <string.h> + +#include "IntCache.h" +#include "CharCache.h" +#include "XidCache.h" +#include "FreeCache.h" +#include "OpcodeCache.h" +#include "ActionCache.h" + +#include "ActionCacheCompat.h" +#include "PositionCacheCompat.h" + +#define DECODE_BUFFER_OVERFLOW_SIZE 4194304 + +#define DECODE_BUFFER_POSTFIX_SIZE 1 + +class DecodeBuffer +{ + public: + + DecodeBuffer(const unsigned char *data, unsigned int length); + + ~DecodeBuffer() + { + } + + int decodeValue(unsigned int &value, unsigned int numBits, + unsigned int blockSize = 0, int endOkay = 0); + + int decodeCachedValue(unsigned int &value, unsigned int numBits, + IntCache &cache, unsigned int blockSize = 0, + int endOkay = 0); + + int decodeCachedValue(unsigned char &value, unsigned int numBits, + CharCache &cache, unsigned int blockSize = 0, + int endOkay = 0); + + void decodeDiffCachedValue(unsigned int &value, unsigned int &previous, + unsigned int numBits, IntCache &cache, + unsigned int blockSize = 0) + { + decodeCachedValue(value, numBits, cache, blockSize); + + previous += (value + 1); + previous &= (0xffffffff >> (32 - numBits)); + + value = previous; + } + + void decodeBoolValue(unsigned int &value) + { + decodeValue(value, 1); + } + + int decodeOpcodeValue(unsigned char &value, OpcodeCache &cache, int endOkay = 0) + { + int result = decodeCachedValue(value, 8, cache.base_[cache.slot_], 8, endOkay); + + if (result == 1) + { + cache.slot_ = value; + } + + return result; + } + + void decodeActionValue(unsigned char &value, unsigned short &position, + ActionCache &cache); + + void decodeNewXidValue(unsigned int &value, unsigned int &lastId, + IntCache &lastIdCache, IntCache &cache, + FreeCache &freeCache); + + void decodeNewXidValue(unsigned int &value, unsigned int &lastId, + IntCache &lastIdCache, XidCache &cache, + FreeCache &freeCache); + + void decodeXidValue(unsigned int &value, XidCache &cache); + + void decodeFreeXidValue(unsigned int &value, FreeCache &cache); + + void decodeActionValueCompat(unsigned char &value, ActionCacheCompat &cache) + { + decodeCachedValue(value, 2, cache.base_[cache.slot_]); + + cache.slot_ = value; + } + + void decodePositionValueCompat(short int &value, PositionCacheCompat &cache); + + void decodeTextData(unsigned char *buffer, unsigned int numBytes) + { + decodeMemory(buffer, numBytes); + } + + void decodeIntData(unsigned char *buffer, unsigned int numBytes) + { + decodeMemory(buffer, numBytes); + } + + void decodeLongData(unsigned char *buffer, unsigned int numBytes) + { + decodeMemory(buffer, numBytes); + } + + const unsigned char *decodeMemory(unsigned int numBytes); + + void decodeMemory(unsigned char *buffer, unsigned int numBytes) + { + memcpy(buffer, decodeMemory(numBytes), numBytes); + } + + private: + + const unsigned char *buffer_; + const unsigned char *end_; + const unsigned char *nextSrc_; + + unsigned char srcMask_; +}; + +#endif /* DecodeBuffer_H */ diff --git a/nxcomp/EncodeBuffer.cpp b/nxcomp/EncodeBuffer.cpp new file mode 100644 index 000000000..466a1d7a0 --- /dev/null +++ b/nxcomp/EncodeBuffer.cpp @@ -0,0 +1,660 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Misc.h" +#include "Control.h" + +#include "EncodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +#define ADVANCE_DEST \ +\ +if (destShift_ == 0) \ +{ \ + destShift_ = 7; nextDest_++; *nextDest_ = 0; \ +} \ +else \ +{ \ + destShift_--; \ +} + +EncodeBuffer::EncodeBuffer() +{ + size_ = ENCODE_BUFFER_DEFAULT_SIZE; + + buffer_ = new unsigned char[size_ + ENCODE_BUFFER_PREFIX_SIZE + + ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE; + end_ = buffer_ + size_; + + nextDest_ = buffer_; + *nextDest_ = 0; + destShift_ = 7; + + lastBits_ = 0; + + initialSize_ = ENCODE_BUFFER_DEFAULT_SIZE; + thresholdSize_ = ENCODE_BUFFER_DEFAULT_SIZE << 1; + maximumSize_ = ENCODE_BUFFER_DEFAULT_SIZE << 4; +} + +EncodeBuffer::~EncodeBuffer() +{ + delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE); +} + +void EncodeBuffer::setSize(unsigned int initialSize, unsigned int thresholdSize, + unsigned int maximumSize) +{ + initialSize_ = initialSize; + thresholdSize_ = thresholdSize; + maximumSize_ = maximumSize; + + #ifdef TEST + *logofs << "EncodeBuffer: Set buffer sizes to " + << initialSize_ << "/" << thresholdSize_ + << "/" << maximumSize_ << ".\n" + << logofs_flush; + #endif +} + +void EncodeBuffer::fullReset() +{ + if (size_ > initialSize_) + { + delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE); + + size_ = initialSize_; + + buffer_ = new unsigned char[size_ + ENCODE_BUFFER_PREFIX_SIZE + + ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE; + } + + end_ = buffer_ + size_; + + nextDest_ = buffer_; + *nextDest_ = 0; + destShift_ = 7; + + lastBits_ = 0; +} + +void EncodeBuffer::encodeValue(unsigned int value, unsigned int numBits, + unsigned int blockSize) +{ + #ifdef DUMP + *logofs << "EncodeBuffer: Encoding " << numBits + << " bits value with block " << blockSize + << " and " << (nextDest_ - buffer_) + << " bytes in buffer.\n" << logofs_flush; + #endif + + value &= IntMask[numBits]; + + unsigned int srcMask = 0x1; + unsigned int bitsWritten = 0; + + if (blockSize == 0) + blockSize = numBits; + + if (end_ - nextDest_ < 8) + { + growBuffer(); + } + + unsigned int numBlocks = 1; + + do + { + if (numBlocks == 4) + blockSize = numBits; + + unsigned int bitsToWrite = (blockSize > numBits - bitsWritten ? + numBits - bitsWritten : blockSize); + unsigned int count = 0; + unsigned int lastBit; + + do + { + lastBit = (value & srcMask); + if (lastBit) + *nextDest_ |= (1 << destShift_); + ADVANCE_DEST; + srcMask <<= 1; + } + while (bitsToWrite > ++count); + + bitsWritten += bitsToWrite; + + if (bitsWritten < numBits) + { + unsigned int tmpMask = srcMask; + unsigned int i = bitsWritten; + + if (lastBit) + { + do + { + unsigned int nextBit = (value & tmpMask); + + if (!nextBit) + break; + + tmpMask <<= 1; + } + while (numBits > ++i); + } + else + { + do + { + unsigned int nextBit = (value & tmpMask); + + if (nextBit) + break; + + tmpMask <<= 1; + } + while (numBits > ++i); + } + + if (i < numBits) + *nextDest_ |= (1 << destShift_); + else + bitsWritten = numBits; + + ADVANCE_DEST; + } + blockSize >>= 1; + + if (blockSize < 2) + blockSize = 2; + + numBlocks++; + } + while (numBits > bitsWritten); +} + +void EncodeBuffer::encodeCachedValue(unsigned int value, unsigned int numBits, + IntCache &cache, unsigned int blockSize) +{ + #ifdef DUMP + *logofs << "EncodeBuffer: Encoding " << numBits + << " bits cached value with block " << blockSize + << " and " << (nextDest_ - buffer_) + << " bytes in buffer.\n" << logofs_flush; + #endif + + value &= IntMask[numBits]; + + if (end_ - nextDest_ < 8) + { + growBuffer(); + } + + blockSize = cache.getBlockSize(blockSize); + + unsigned int index; + unsigned int sameDiff; + + #ifdef DUMP + + diffBits(); + + #endif + + if (cache.lookup(value, index, IntMask[numBits], sameDiff)) + { + if (index > 1) + index++; + + while (destShift_ < index) + { + index -= destShift_; + index--; + destShift_ = 7; + nextDest_++; + *nextDest_ = 0; + } + + destShift_ -= index; + *nextDest_ |= (1 << destShift_); + ADVANCE_DEST; + + #ifdef DUMP + *logofs << "EncodeBuffer: Encoded cached int using " + << diffBits() << " bits out of " << numBits + << ".\n" << logofs_flush; + #endif + } + else + { + ADVANCE_DEST; + ADVANCE_DEST; + *nextDest_ |= (1 << destShift_); + ADVANCE_DEST; + + // + // The attempt is very seldom successful. + // Avoid to encode the additional bool. + // + + if (control -> isProtoStep8() == 1) + { + #ifdef DUMP + *logofs << "EncodeBuffer: Encoded missed int using " + << diffBits() << " bits out of " << numBits + << ".\n" << logofs_flush; + #endif + + encodeValue(value, numBits, blockSize); + } + else + { + if (sameDiff) + { + #ifdef DUMP + *logofs << "EncodeBuffer: Matched difference with block size " + << cache.getBlockSize(blockSize) << ".\n" + << logofs_flush; + #endif + + encodeBoolValue(1); + } + else + { + #ifdef DUMP + *logofs << "EncodeBuffer: Missed difference with block size " + << cache.getBlockSize(blockSize) << ".\n" + << logofs_flush; + #endif + + encodeBoolValue(0); + + encodeValue(value, numBits, blockSize); + } + + #ifdef DUMP + *logofs << "EncodeBuffer: Encoded missed int using " + << diffBits() << " bits out of " << numBits + << ".\n" << logofs_flush; + #endif + } + } +} + +void EncodeBuffer::encodeCachedValue(unsigned char value, unsigned int numBits, + CharCache &cache, unsigned int blockSize) +{ + #ifdef DUMP + *logofs << "EncodeBuffer: Encoding " << numBits + << " bits char cached value with block " << blockSize + << " and " << (nextDest_ - buffer_) + << " bytes in buffer.\n" << logofs_flush; + #endif + + value &= IntMask[numBits]; + + if (end_ - nextDest_ < 8) + { + growBuffer(); + } + + unsigned int index; + + #ifdef DUMP + + diffBits(); + + #endif + + if (cache.lookup(value, index)) + { + if (index > 1) + index++; + + while (destShift_ < index) + { + index -= destShift_; + index--; + destShift_ = 7; + nextDest_++; + *nextDest_ = 0; + } + + destShift_ -= index; + *nextDest_ |= (1 << destShift_); + ADVANCE_DEST; + + #ifdef DUMP + *logofs << "EncodeBuffer: Encoded cached char using " + << diffBits() << " bits out of " << numBits + << ".\n" << logofs_flush; + #endif + } + else + { + ADVANCE_DEST; + ADVANCE_DEST; + *nextDest_ |= (1 << destShift_); + ADVANCE_DEST; + + encodeValue(value, numBits, blockSize); + + #ifdef DUMP + *logofs << "EncodeBuffer: Encoded missed char using " + << diffBits() << " bits out of " << numBits + << ".\n" << logofs_flush; + #endif + } +} + +void EncodeBuffer::encodeMemory(const unsigned char *buffer, unsigned int numBytes) +{ + #ifdef DUMP + *logofs << "EncodeBuffer: Encoding " << numBytes + << " bytes of memory with " << (nextDest_ - buffer_) + << " bytes in buffer.\n" << logofs_flush; + #endif + + if (numBytes > ENCODE_BUFFER_OVERFLOW_SIZE) + { + #ifdef PANIC + *logofs << "EncodeBuffer: PANIC! Should never encode buffer " + << "of size greater than " << ENCODE_BUFFER_OVERFLOW_SIZE + << " bytes.\n" << logofs_flush; + + *logofs << "EncodeBuffer: PANIC! Assuming failure encoding data " + << "in context [A].\n" << logofs_flush; + #endif + + // + // Label "context" is just used to identify + // the routine which detected the problem in + // present source file. + // + + cerr << "Error" << ": Should never encode buffer of size " + << "greater than " << ENCODE_BUFFER_OVERFLOW_SIZE + << " bytes.\n"; + + cerr << "Error" << ": Assuming failure encoding data " + << "in context [A].\n" ; + + HandleAbort(); + } + + alignBuffer(); + + if (end_ - nextDest_ < (int) numBytes) + { + growBuffer(numBytes); + } + + memcpy(nextDest_, buffer, numBytes); + + nextDest_ += numBytes; + + if (nextDest_ == end_) + { + growBuffer(); + } + else if (nextDest_ > end_) + { + #ifdef PANIC + *logofs << "EncodeBuffer: PANIC! Assertion failed. Error [B] " + << "in encodeMemory() nextDest_ " << (nextDest_ - buffer) + << " end_ " << (end_ - buffer) << ".\n" + << logofs_flush; + #endif + + // + // Label "context" is just used to identify + // the routine which detected the problem in + // present source file. + // + + cerr << "Error" << ": Failure encoding raw data " + << "in context [B].\n" ; + + HandleAbort(); + } + + *nextDest_ = 0; +} + +unsigned int EncodeBuffer::getLength() const +{ + unsigned int length = nextDest_ - buffer_; + + if (destShift_ != 7) + { + length++; + } + + if (length > 0 && control -> isProtoStep7() == 1) + { + return length + ENCODE_BUFFER_POSTFIX_SIZE; + } + + return length; +} + +unsigned int EncodeBuffer::diffBits() +{ + unsigned int bits = ((nextDest_ - buffer_) << 3); + + bits += (7 - destShift_); + + unsigned int diff = bits - lastBits_; + + lastBits_ = bits; + + return diff; +} + +void EncodeBuffer::growBuffer(unsigned int numBytes) +{ + if (numBytes == 0) + { + numBytes = initialSize_; + } + + unsigned int bytesInBuffer = nextDest_ - buffer_; + + unsigned int newSize = thresholdSize_; + + while (newSize < bytesInBuffer + numBytes) + { + newSize <<= 1; + + if (newSize > maximumSize_) + { + newSize = bytesInBuffer + numBytes + initialSize_; + } + } + + unsigned char *newBuffer; + + newBuffer = new unsigned char[newSize + ENCODE_BUFFER_PREFIX_SIZE + + ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE; + + if (newBuffer == NULL) + { + #ifdef PANIC + *logofs << "EncodeBuffer: PANIC! Error in context [C] " + << "growing buffer to accomodate " << numBytes + << " bytes .\n" << logofs_flush; + #endif + + cerr << "Error" << ": Error in context [C] " + << "growing encode buffer to accomodate " + << numBytes << " bytes.\n"; + + HandleAbort(); + } + + #ifdef TEST + if (newSize >= maximumSize_) + { + *logofs << "EncodeBuffer: WARNING! Buffer grown to reach " + << "size of " << newSize << " bytes.\n" + << logofs_flush; + } + #endif + + // + // Prefix should not contain any valid data. + // It is proxy that will fill it with control + // messages and data length at the time a new + // frame is written to socket. + // + + memcpy(newBuffer, buffer_, bytesInBuffer + 1); + + newBuffer[bytesInBuffer + 1] = 0; + + delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE); + + buffer_ = newBuffer; + size_ = newSize; + end_ = buffer_ + size_; + + nextDest_ = buffer_ + bytesInBuffer; +} + +void EncodeBuffer::alignBuffer() +{ + if (destShift_ != 7) + { + destShift_ = 7; + nextDest_++; + + if (nextDest_ >= end_) + { + growBuffer(); + } + + *nextDest_ = 0; + } +} + +void EncodeBuffer::encodeActionValue(unsigned char value, unsigned short position, + ActionCache &cache) +{ + unsigned int v = (value << 13) | position; + + unsigned int t = (v - cache.last_); + + encodeCachedValue(t, 15, *(cache.base_[cache.slot_])); + + cache.last_ = v; + + #ifdef DEBUG + *logofs << "EncodeBuffer: Encoded value " + << (unsigned) value << " and position " + << position << " with base " << cache.slot_ + << ".\n" << logofs_flush; + #endif + + cache.slot_ = (cache.last_ & 0xff); +} + +void EncodeBuffer::encodeNewXidValue(unsigned int value, unsigned int &lastId, + IntCache &lastIdCache, IntCache &cache, + FreeCache &freeCache) +{ + encodeCachedValue((value - 1) - lastId, 29, lastIdCache); + + lastId = value; + + cache.push(value, 0x1fffffff); + + freeCache.push(value, 0x1fffffff); +} + +void EncodeBuffer::encodeNewXidValue(unsigned int value, unsigned int &lastId, + IntCache &lastIdCache, XidCache &cache, + FreeCache &freeCache) +{ + encodeCachedValue((value - 1) - lastId, 29, lastIdCache); + + lastId = value; + + unsigned int t = (value - cache.last_); + + cache.last_ = value; + + #ifdef DEBUG + *logofs << "EncodeBuffer: Encoded new Xid " << value + << " with base " << cache.slot_ << ".\n" + << logofs_flush; + #endif + + cache.slot_ = (value & 0xff); + + cache.base_[cache.slot_] -> push(t, 0x1fffffff); + + freeCache.push(value, IntMask[29]); +} + +void EncodeBuffer::encodeXidValue(unsigned int value, XidCache &cache) +{ + unsigned int t = (value - cache.last_); + + encodeCachedValue(t, 29, *(cache.base_[cache.slot_])); + + cache.last_ = value; + + #ifdef DEBUG + *logofs << "EncodeBuffer: Encoded Xid " << value + << " with base " << cache.slot_ << ".\n" + << logofs_flush; + #endif + + cache.slot_ = (value & 0xff); +} + +void EncodeBuffer::encodeFreeXidValue(unsigned int value, FreeCache &cache) +{ + encodeCachedValue(value, 29, cache); +} + +void EncodeBuffer::encodePositionValueCompat(short int value, PositionCacheCompat &cache) +{ + unsigned int t = (value - cache.last_); + + encodeCachedValue(t, 13, *(cache.base_[cache.slot_])); + + cache.last_ = value; + + #ifdef DEBUG + *logofs << "EncodeBuffer: Encoded position " + << value << " with base " << cache.slot_ + << ".\n" << logofs_flush; + #endif + + cache.slot_ = (value & 0x1f); +} diff --git a/nxcomp/EncodeBuffer.h b/nxcomp/EncodeBuffer.h new file mode 100644 index 000000000..9f5ac5352 --- /dev/null +++ b/nxcomp/EncodeBuffer.h @@ -0,0 +1,187 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef EncodeBuffer_H +#define EncodeBuffer_H + +#include "IntCache.h" +#include "CharCache.h" +#include "XidCache.h" +#include "FreeCache.h" +#include "OpcodeCache.h" +#include "ActionCache.h" + +#include "ActionCacheCompat.h" +#include "PositionCacheCompat.h" + +#define ENCODE_BUFFER_DEFAULT_SIZE 16384 + +// +// This should match the maximum size of +// a single message added to write buffer +// (see WriteBuffer.h). +// + +#define ENCODE_BUFFER_OVERFLOW_SIZE 4194304 + +// +// Adjust for the control messages and the +// frame length added by the proxy. +// + +#define ENCODE_BUFFER_PREFIX_SIZE 64 + +// +// The encode routines may write one byte +// past the nominal end of the encode buffer. +// This additional byte is included in the +// payload. This is actually a harmless bug. +// + +#define ENCODE_BUFFER_POSTFIX_SIZE 1 + +class EncodeBuffer +{ + public: + + EncodeBuffer(); + + ~EncodeBuffer(); + + void setSize(unsigned int initialSize, unsigned int thresholdSize, + unsigned int maximumSize); + + void encodeValue(unsigned int value, unsigned int numBits, + unsigned int blockSize = 0); + + void encodeCachedValue(unsigned int value, unsigned int numBits, + IntCache &cache, unsigned int blockSize = 0); + + void encodeCachedValue(unsigned char value, unsigned int numBits, + CharCache &cache, unsigned int blockSize = 0); + + void encodeDiffCachedValue(const unsigned int value, unsigned int &previous, + unsigned int numBits, IntCache &cache, + unsigned int blockSize = 0) + { + encodeCachedValue((value - 1) - previous, numBits, cache, blockSize); + + previous = value; + } + + void encodeBoolValue(unsigned int value) + { + encodeValue(value, 1); + } + + void encodeOpcodeValue(unsigned char value, OpcodeCache &cache) + { + encodeCachedValue(value, 8, cache.base_[cache.slot_], 8); + + cache.slot_ = value; + } + + void encodeActionValue(unsigned char value, ActionCache &cache) + { + unsigned short position = 0; + + encodeActionValue(value, position, cache); + } + + void encodeActionValue(unsigned char value, unsigned short position, + ActionCache &cache); + + void encodeNewXidValue(unsigned int value, unsigned int &lastId, + IntCache &lastIdCache, IntCache &cache, + FreeCache &freeCache); + + void encodeNewXidValue(unsigned int value, unsigned int &lastId, + IntCache &lastIdCache, XidCache &cache, + FreeCache &freeCache); + + void encodeXidValue(unsigned int value, XidCache &cache); + + void encodeFreeXidValue(unsigned int value, FreeCache &cache); + + void encodeActionValueCompat(unsigned char value, ActionCacheCompat &cache) + { + encodeCachedValue(value, 2, cache.base_[cache.slot_]); + + cache.slot_ = value; + } + + void encodePositionValueCompat(short int value, PositionCacheCompat &cache); + + void encodeTextData(const unsigned char *buffer, unsigned int numBytes) + { + encodeMemory(buffer, numBytes); + } + + void encodeIntData(const unsigned char *buffer, unsigned int numBytes) + { + encodeMemory(buffer, numBytes); + } + + void encodeLongData(const unsigned char *buffer, unsigned int numBytes) + { + encodeMemory(buffer, numBytes); + } + + void encodeMemory(const unsigned char *buffer, unsigned int numBytes); + + unsigned char *getData() + { + return buffer_; + } + + unsigned int getLength() const; + + unsigned int getBits() const + { + return ((nextDest_ - buffer_) << 3) + (7 - destShift_); + } + + unsigned int diffBits(); + + void fullReset(); + + private: + + void growBuffer(unsigned int numBytes = 0); + + void alignBuffer(); + + unsigned int size_; + unsigned char *buffer_; + + // + // This points to the first byte + // just beyond end of the buffer. + // + + const unsigned char *end_; + + unsigned char *nextDest_; + unsigned int destShift_; + unsigned int lastBits_; + + unsigned int initialSize_; + unsigned int thresholdSize_; + unsigned int maximumSize_; +}; + +#endif /* EncodeBuffer_H */ diff --git a/nxcomp/FillPoly.cpp b/nxcomp/FillPoly.cpp new file mode 100644 index 000000000..37df3772b --- /dev/null +++ b/nxcomp/FillPoly.cpp @@ -0,0 +1,227 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "FillPoly.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int FillPolyStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + FillPolyMessage *fillPoly = (FillPolyMessage *) message; + + // + // Here is the fingerprint. + // + + fillPoly -> drawable = GetULONG(buffer + 4, bigEndian); + fillPoly -> gcontext = GetULONG(buffer + 8, bigEndian); + + fillPoly -> shape = *(buffer + 12); + fillPoly -> mode = *(buffer + 13); + + if (control -> isProtoStep8() == 1 && + size >= (unsigned int) dataOffset) + { + fillPoly -> x_origin = GetUINT(buffer + 16, bigEndian); + fillPoly -> y_origin = GetUINT(buffer + 18, bigEndian); + } + else + { + fillPoly -> x_origin = 0; + fillPoly -> y_origin = 0; + } + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int FillPolyStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + FillPolyMessage *fillPoly = (FillPolyMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(fillPoly -> drawable, buffer + 4, bigEndian); + PutULONG(fillPoly -> gcontext, buffer + 8, bigEndian); + + *(buffer + 12) = fillPoly -> shape; + *(buffer + 13) = fillPoly -> mode; + + if (control -> isProtoStep8() == 1 && + size >= (unsigned int) dataOffset) + { + PutUINT(fillPoly -> x_origin, buffer + 16, bigEndian); + PutUINT(fillPoly -> y_origin, buffer + 18, bigEndian); + } + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void FillPolyStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + FillPolyMessage *fillPoly = (FillPolyMessage *) message; + + *logofs << name() << ": Identity drawable " << fillPoly -> drawable + << ", gcontext " << fillPoly -> gcontext << ", shape " + << fillPoly -> shape << ", mode " << fillPoly -> mode + << fillPoly -> size_ << ", x_origin " << fillPoly -> x_origin + << ", y_origin " << fillPoly -> y_origin << ".\n" + << logofs_flush; + #endif +} + +void FillPolyStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // Fields shape, mode. + // + + md5_append(md5_state_, buffer + 12, 2); +} + +void FillPolyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + FillPolyMessage *fillPoly = (FillPolyMessage *) message; + FillPolyMessage *cachedFillPoly = (FillPolyMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << fillPoly -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(fillPoly -> drawable, clientCache -> drawableCache); + + cachedFillPoly -> drawable = fillPoly -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << fillPoly -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(fillPoly -> gcontext, clientCache -> gcCache); + + cachedFillPoly -> gcontext = fillPoly -> gcontext; + + if (control -> isProtoStep8() == 1 && + fillPoly -> size_ >= dataOffset) + { + #ifdef TEST + *logofs << name() << ": Encoding value " << fillPoly -> x_origin + << " as x_origin field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(fillPoly -> x_origin, 16, + *clientCache -> fillPolyXAbsCache[0], 8); + + cachedFillPoly -> x_origin = fillPoly -> x_origin; + + #ifdef TEST + *logofs << name() << ": Encoding value " << fillPoly -> y_origin + << " as y_origin field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(fillPoly -> y_origin, 16, + *clientCache -> fillPolyYAbsCache[0], 8); + + cachedFillPoly -> y_origin = fillPoly -> y_origin; + } +} + +void FillPolyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + FillPolyMessage *fillPoly = (FillPolyMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(fillPoly -> drawable, clientCache -> drawableCache); + + #ifdef TEST + *logofs << name() << ": Decoded value " << fillPoly -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(fillPoly -> gcontext, clientCache -> gcCache); + + #ifdef TEST + *logofs << name() << ": Decoded value " << fillPoly -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + if (control -> isProtoStep8() == 1 && + fillPoly -> size_ >= dataOffset) + { + unsigned int value; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache -> fillPolyXAbsCache[0], 8); + + fillPoly -> x_origin = value; + + #ifdef TEST + *logofs << name() << ": Decoded value " << fillPoly -> x_origin + << " as x_origin field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache -> fillPolyYAbsCache[0], 8); + + fillPoly -> y_origin = value; + + #ifdef TEST + *logofs << name() << ": Decoded value " << fillPoly -> y_origin + << " as y_origin field.\n" << logofs_flush; + #endif + } +} + + diff --git a/nxcomp/FillPoly.h b/nxcomp/FillPoly.h new file mode 100644 index 000000000..f33968494 --- /dev/null +++ b/nxcomp/FillPoly.h @@ -0,0 +1,198 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef FillPoly_H +#define FillPoly_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define FILLPOLY_ENABLE_CACHE 1 +#define FILLPOLY_ENABLE_DATA 0 +#define FILLPOLY_ENABLE_SPLIT 0 +#define FILLPOLY_ENABLE_COMPRESS 0 + +#define FILLPOLY_DATA_LIMIT 512 +#define FILLPOLY_DATA_OFFSET 16 + +#define FILLPOLY_CACHE_SLOTS 2000 +#define FILLPOLY_CACHE_THRESHOLD 3 +#define FILLPOLY_CACHE_LOWER_THRESHOLD 1 + +#define FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 20 + +// +// The message class. +// + +class FillPolyMessage : public Message +{ + friend class FillPolyStore; + + public: + + FillPolyMessage() + { + } + + ~FillPolyMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char shape; + unsigned char mode; + unsigned int drawable; + unsigned int gcontext; + + unsigned short x_origin; + unsigned short y_origin; +}; + +class FillPolyStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + FillPolyStore() : MessageStore() + { + enableCache = FILLPOLY_ENABLE_CACHE; + enableData = FILLPOLY_ENABLE_DATA; + enableSplit = FILLPOLY_ENABLE_SPLIT; + enableCompress = FILLPOLY_ENABLE_COMPRESS; + + dataLimit = FILLPOLY_DATA_LIMIT; + dataOffset = FILLPOLY_DATA_OFFSET; + + if (control -> isProtoStep8() == 1) + { + dataOffset = FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8; + } + + cacheSlots = FILLPOLY_CACHE_SLOTS; + cacheThreshold = FILLPOLY_CACHE_THRESHOLD; + cacheLowerThreshold = FILLPOLY_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~FillPolyStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "FillPoly"; + } + + virtual unsigned char opcode() const + { + return X_FillPoly; + } + + virtual unsigned int storage() const + { + return sizeof(FillPolyMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new FillPolyMessage(); + } + + virtual Message *create(const Message &message) const + { + return new FillPolyMessage((const FillPolyMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (FillPolyMessage *) message; + } + + virtual int identitySize(const unsigned char *buffer, unsigned int size) + { + unsigned int offset = (control -> isProtoStep8() == 1 ? + FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 : + FILLPOLY_DATA_OFFSET); + + return (size >= offset ? offset : size); + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* FillPoly_H */ diff --git a/nxcomp/Fork.cpp b/nxcomp/Fork.cpp new file mode 100644 index 000000000..48faa2992 --- /dev/null +++ b/nxcomp/Fork.cpp @@ -0,0 +1,98 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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/Fork.h b/nxcomp/Fork.h new file mode 100644 index 000000000..9df9f4041 --- /dev/null +++ b/nxcomp/Fork.h @@ -0,0 +1,23 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Try again if the fork() fails, as it can happen +// often on Cygwin. +// + +extern int Fork(); diff --git a/nxcomp/FreeCache.h b/nxcomp/FreeCache.h new file mode 100644 index 000000000..01fa42cd8 --- /dev/null +++ b/nxcomp/FreeCache.h @@ -0,0 +1,34 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef FreeCache_H +#define FreeCache_H + +#include "IntCache.h" + +class FreeCache : public IntCache +{ + public: + + FreeCache(unsigned int size) + + : IntCache(size) + { + } +}; + +#endif /* FreeCache_H */ diff --git a/nxcomp/GenericChannel.cpp b/nxcomp/GenericChannel.cpp new file mode 100644 index 000000000..641ad36d4 --- /dev/null +++ b/nxcomp/GenericChannel.cpp @@ -0,0 +1,483 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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 accomodate multiple reads in + // a single frame. + // + + if (priority_ > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: WARNING! Requesting flush " + << "because of " << priority_ << " prioritized " + << "messages for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncPriority() < 0) + { + return -1; + } + + // + // Reset the priority flag. + // + + priority_ = 0; + } + + // + // Flush if we produced enough data. + // + + if (proxy -> canAsyncFlush() == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: WARNING! Requesting flush " + << "because of enough data or timeout on the " + << "proxy link.\n" << logofs_flush; + #endif + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + } + + #if defined(TEST) || defined(INFO) + + if (transport_ -> pending() != 0 || + readBuffer_.checkMessage() != 0) + { + *logofs << "handleRead: PANIC! Buffer for X descriptor FD#" + << fd_ << " has " << transport_ -> pending() + << " bytes to read.\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // Reset the read buffer. + // + + readBuffer_.fullReset(); + + return 1; +} + +// +// End of handleRead(). +// + +// +// Beginning of handleWrite(). +// + +int GenericChannel::handleWrite(const unsigned char *message, unsigned int length) +{ + #ifdef TEST + *logofs << "handleWrite: Called for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + // + // Create the buffer from which to + // decode messages. + // + + DecodeBuffer decodeBuffer(message, length); + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "handleWrite: Decoding messages for FD#" << fd_ + << " with " << length << " bytes in the buffer.\n" + << logofs_flush; + #endif + + unsigned char *outputMessage; + unsigned int outputLength; + + // + // Tag message as generic data + // in decompression. + // + + unsigned char outputOpcode = X_NXInternalGenericData; + + for (;;) + { + decodeBuffer.decodeValue(outputLength, 32, 14); + + if (outputLength == 0) + { + break; + } + + if (isCompressed() == 1) + { + if (writeBuffer_.getAvailable() < outputLength || + (int) outputLength >= control -> TransportFlushBufferSize) + { + #ifdef DEBUG + *logofs << "handleWrite: Using scratch buffer for " + << "generic data with size " << outputLength << " and " + << writeBuffer_.getLength() << " bytes in buffer.\n" + << logofs_flush; + #endif + + outputMessage = writeBuffer_.addScratchMessage(outputLength); + } + else + { + outputMessage = writeBuffer_.addMessage(outputLength); + } + + const unsigned char *compressedData = NULL; + unsigned int compressedDataSize = 0; + + int decompressed = handleDecompress(decodeBuffer, outputOpcode, 0, + outputMessage, outputLength, compressedData, + compressedDataSize); + if (decompressed < 0) + { + return -1; + } + } + else + { + #ifdef DEBUG + *logofs << "handleWrite: Using scratch buffer for " + << "generic data with size " << outputLength << " and " + << writeBuffer_.getLength() << " bytes in buffer.\n" + << logofs_flush; + #endif + + writeBuffer_.addScratchMessage((unsigned char *) + decodeBuffer.decodeMemory(outputLength), outputLength); + } + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleWrite: Handled generic data for FD#" << fd_ + << ". " << outputLength << " bytes out.\n" + << logofs_flush; + #endif + + handleFlush(flush_if_needed); + } + + // + // Write any remaining data to socket. + // + + if (handleFlush(flush_if_any) < 0) + { + return -1; + } + + return 1; +} + +// +// End of handleWrite(). +// + +// +// Other members. +// + +int GenericChannel::handleCompletion(EncodeBuffer &encodeBuffer) +{ + // + // Add the bits telling to the remote + // that all data in the frame has been + // encoded. + // + + if (encodeBuffer.getLength() > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleCompletion: Writing completion bits with " + << encodeBuffer.getLength() << " bytes encoded " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeValue(0, 32, 14); + + return 1; + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "handleCompletion: PANIC! No completion to write " + << "for FD#" << fd_ << ".\n" << logofs_flush; + + HandleCleanup(); + } + #endif + + return 0; +} + +int GenericChannel::handleConfiguration() +{ + #ifdef TEST + *logofs << "GenericChannel: Setting new buffer parameters.\n" + << logofs_flush; + #endif + + readBuffer_.setSize(control -> GenericInitialReadSize, + control -> GenericMaximumBufferSize); + + writeBuffer_.setSize(control -> TransportGenericBufferSize, + control -> TransportGenericBufferThreshold, + control -> TransportMaximumBufferSize); + + transport_ -> setSize(control -> TransportGenericBufferSize, + control -> TransportGenericBufferThreshold, + control -> TransportMaximumBufferSize); + + return 1; +} + +int GenericChannel::handleFinish() +{ + #ifdef TEST + *logofs << "GenericChannel: Finishing channel for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + congestion_ = 0; + priority_ = 0; + + finish_ = 1; + + transport_ -> fullReset(); + + return 1; +} + +int GenericChannel::setReferences() +{ + #ifdef TEST + *logofs << "GenericChannel: Initializing the static " + << "members for the generic channels.\n" + << logofs_flush; + #endif + + #ifdef REFERENCES + + references_ = 0; + + #endif + + return 1; +} diff --git a/nxcomp/GenericChannel.h b/nxcomp/GenericChannel.h new file mode 100644 index 000000000..ba4f1e7e8 --- /dev/null +++ b/nxcomp/GenericChannel.h @@ -0,0 +1,448 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef GenericChannel_H +#define GenericChannel_H + +#include "Channel.h" + +#include "Statistics.h" + +#include "GenericReadBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#undef TEST +#undef DEBUG + +// +// Define this to log a line when a channel +// is created or destroyed. +// + +#undef REFERENCES + +// +// This class implements the client +// side compression of X protocol. +// + +class GenericChannel : public Channel +{ + public: + + GenericChannel(Transport *transport, StaticCompressor *compressor); + + virtual ~GenericChannel(); + + virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, + unsigned int length); + + virtual int handleWrite(const unsigned char *message, unsigned int length); + + + virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store, + T_store_action action, int position, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) + { + return 0; + } + + virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, + T_store_action action, int position, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) + { + return 0; + } + + + virtual int handleSplit(EncodeBuffer &encodeBuffer) + { + return 0; + } + + virtual int handleSplit(DecodeBuffer &decodeBuffer) + { + return 0; + } + + virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split) + { + return 0; + } + + virtual int handleSplitEvent(DecodeBuffer &decodeBuffer) + { + return 0; + } + + virtual int handleMotion(EncodeBuffer &encodeBuffer) + { + return 0; + } + + virtual int handleCompletion(EncodeBuffer &encodeBuffer); + + virtual int handleConfiguration(); + + virtual int handleFinish(); + + virtual int handleAsyncEvents() + { + return 0; + } + + virtual int needSplit() const + { + return 0; + } + + virtual int needMotion() const + { + return 0; + } + + virtual T_channel_type getType() const = 0; + + // + // Initialize the static members. + // + + static int setReferences(); + + protected: + + // + // Generic channels are considered to be + // in congestion state as soon as the + // socket is blocked for write. + // + + virtual int isCongested() + { + return (transport_ -> blocked() == 1); + } + + virtual int isReliable() + { + return 0; + } + + // + // Model generic channels' encoding and + // decoding policy. + // + + virtual int isCompressed() = 0; + + // + // Return true if the channel contains + // time sensitive data. + // + + virtual int isPrioritized() = 0; + + // + // Record the protocol bits for the + // specific service. + // + + virtual void addProtocolBits(unsigned int bitsIn, unsigned int bitsOut) = 0; + + // + // Channel's own read buffer. + // + + GenericReadBuffer readBuffer_; + + private: + + // + // Keep track of object's creation + // and deletion. + // + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +class CupsChannel : public GenericChannel +{ + public: + + CupsChannel(Transport *transport, StaticCompressor *compressor) + + : GenericChannel(transport, compressor) + { + } + + virtual ~CupsChannel() + { + } + + protected: + + virtual T_channel_type getType() const + { + return channel_cups; + } + + virtual int isCompressed() + { + if (control -> isProtoStep8() == 0) + { + return 1; + } + + return 0; + } + + virtual int isPrioritized() + { + return 0; + } + + virtual void addProtocolBits(unsigned int bitsIn, + unsigned int bitsOut) + { + statistics -> addCupsBits(bitsIn, bitsOut); + } +}; + +class SmbChannel : public GenericChannel +{ + public: + + SmbChannel(Transport *transport, StaticCompressor *compressor) + + : GenericChannel(transport, compressor) + { + } + + virtual ~SmbChannel() + { + } + + protected: + + virtual T_channel_type getType() const + { + return channel_smb; + } + + virtual int isCompressed() + { + if (control -> isProtoStep8() == 0) + { + return 1; + } + + return 0; + } + + virtual int isPrioritized() + { + return 0; + } + + virtual void addProtocolBits(unsigned int bitsIn, + unsigned int bitsOut) + { + statistics -> addSmbBits(bitsIn, bitsOut); + } +}; + +class MediaChannel : public GenericChannel +{ + public: + + MediaChannel(Transport *transport, StaticCompressor *compressor) + + : GenericChannel(transport, compressor) + { + } + + virtual ~MediaChannel() + { + } + + protected: + + virtual T_channel_type getType() const + { + return channel_media; + } + + // + // Don't try to compress the media data. + // + + virtual int isCompressed() + { + return 0; + } + + // + // Reduce the latency of media channels + // by setting them as prioritized, even + // if this will take away bandwidth from + // the X channels. + // + + virtual int isPrioritized() + { + return 1; + } + + virtual void addProtocolBits(unsigned int bitsIn, + unsigned int bitsOut) + { + statistics -> addMediaBits(bitsIn, bitsOut); + } +}; + +class HttpChannel : public GenericChannel +{ + public: + + HttpChannel(Transport *transport, StaticCompressor *compressor) + + : GenericChannel(transport, compressor) + { + } + + virtual ~HttpChannel() + { + } + + protected: + + virtual T_channel_type getType() const + { + return channel_http; + } + + virtual int isCompressed() + { + if (control -> isProtoStep8() == 0) + { + return 1; + } + + return 0; + } + + virtual int isPrioritized() + { + return 0; + } + + virtual void addProtocolBits(unsigned int bitsIn, + unsigned int bitsOut) + { + statistics -> addHttpBits(bitsIn, bitsOut); + } +}; + +class FontChannel : public GenericChannel +{ + public: + + FontChannel(Transport *transport, StaticCompressor *compressor) + + : GenericChannel(transport, compressor) + { + } + + virtual ~FontChannel() + { + } + + protected: + + virtual T_channel_type getType() const + { + return channel_font; + } + + virtual int isCompressed() + { + if (control -> isProtoStep8() == 0) + { + return 1; + } + + return 0; + } + + virtual int isPrioritized() + { + return 1; + } + + virtual void addProtocolBits(unsigned int bitsIn, + unsigned int bitsOut) + { + statistics -> addFontBits(bitsIn, bitsOut); + } +}; + +class SlaveChannel : public GenericChannel +{ + public: + + SlaveChannel(Transport *transport, StaticCompressor *compressor) + + : GenericChannel(transport, compressor) + { + } + + virtual ~SlaveChannel() + { + } + + protected: + + virtual T_channel_type getType() const + { + return channel_slave; + } + + virtual int isCompressed() + { + return 0; + } + + virtual int isPrioritized() + { + return 0; + } + + virtual void addProtocolBits(unsigned int bitsIn, + unsigned int bitsOut) + { + statistics -> addSlaveBits(bitsIn, bitsOut); + } +}; + +#endif /* GenericChannel_H */ diff --git a/nxcomp/GenericReadBuffer.cpp b/nxcomp/GenericReadBuffer.cpp new file mode 100644 index 000000000..b7b6d93f4 --- /dev/null +++ b/nxcomp/GenericReadBuffer.cpp @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "GenericReadBuffer.h" + +#include "GenericChannel.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +unsigned int GenericReadBuffer::suggestedLength(unsigned int pendingLength) +{ + // + // Always read the initial read size. + // + + return 0; +} + +int GenericReadBuffer::locateMessage(const unsigned char *start, + const unsigned char *end, + unsigned int &controlLength, + unsigned int &dataLength, + unsigned int &trailerLength) +{ + // + // We don't care about the endianess + // in generic channels. + // + + unsigned int size = end - start; + + #ifdef TEST + *logofs << "GenericReadBuffer: Locating message for FD#" + << transport_ -> fd() << " with " << size + << " bytes.\n" << logofs_flush; + #endif + + if (size == 0) + { + remaining_ = 1; + + return 0; + } + + dataLength = size; + + controlLength = 0; + trailerLength = 0; + + remaining_ = 0; + + return 1; +} diff --git a/nxcomp/GenericReadBuffer.h b/nxcomp/GenericReadBuffer.h new file mode 100644 index 000000000..6b1fdd1fa --- /dev/null +++ b/nxcomp/GenericReadBuffer.h @@ -0,0 +1,53 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef GenericReadBuffer_H +#define GenericReadBuffer_H + +#include "ReadBuffer.h" +#include "Control.h" + +class GenericChannel; + +class GenericReadBuffer : public ReadBuffer +{ + public: + + GenericReadBuffer(Transport *transport, GenericChannel *channel) + + : ReadBuffer(transport), channel_(channel) + { + } + + virtual ~GenericReadBuffer() + { + } + + protected: + + virtual unsigned int suggestedLength(unsigned int pendingLength); + + virtual int locateMessage(const unsigned char *start, + const unsigned char *end, + unsigned int &controlLength, + unsigned int &dataLength, + unsigned int &trailerLength); + + GenericChannel *channel_; +}; + +#endif /* GenericReadBuffer_H */ diff --git a/nxcomp/GenericReply.cpp b/nxcomp/GenericReply.cpp new file mode 100644 index 000000000..9daccc566 --- /dev/null +++ b/nxcomp/GenericReply.cpp @@ -0,0 +1,293 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "GenericReply.h" + +#include "ServerCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +GenericReplyStore::GenericReplyStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = GENERICREPLY_ENABLE_CACHE; + enableData = GENERICREPLY_ENABLE_DATA; + enableSplit = GENERICREPLY_ENABLE_SPLIT; + enableCompress = GENERICREPLY_ENABLE_COMPRESS; + + if (control -> isProtoStep7() == 1) + { + enableCompress = GENERICREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7; + } + + dataLimit = GENERICREPLY_DATA_LIMIT; + dataOffset = GENERICREPLY_DATA_OFFSET; + + cacheSlots = GENERICREPLY_CACHE_SLOTS; + cacheThreshold = GENERICREPLY_CACHE_THRESHOLD; + cacheLowerThreshold = GENERICREPLY_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +GenericReplyStore::~GenericReplyStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int GenericReplyStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ServerCache *serverCache = (ServerCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" + << logofs_flush; + #endif + + encodeBuffer.encodeValue(GetULONG(buffer + 4, bigEndian), 32, 15); + + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + serverCache -> genericReplyCharCache); + + for (unsigned int i = 0; i < 6; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(buffer + i * 4 + 8, bigEndian), 32, + *serverCache -> genericReplyIntCache[i]); + } + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" + << logofs_flush; + #endif + + return 1; +} + +int GenericReplyStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ServerCache *serverCache = (ServerCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" + << logofs_flush; + #endif + + decodeBuffer.decodeValue(size, 32, 15); + + size = 32 + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + decodeBuffer.decodeCachedValue(*(buffer + 1), 8, + serverCache -> genericReplyCharCache); + + unsigned int value; + + for (unsigned int i = 0; i < 6; i++) + { + decodeBuffer.decodeCachedValue(value, 32, + *serverCache -> genericReplyIntCache[i]); + + PutULONG(value, buffer + i * 4 + 8, bigEndian); + } + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" + << logofs_flush; + #endif + + return 1; +} + +int GenericReplyStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GenericReplyMessage *genericReply = (GenericReplyMessage *) message; + + genericReply -> byte_data = *(buffer + 1); + + for (int i = 0; i < 12; i++) + { + genericReply -> short_data[i] = GetUINT(buffer + i * 2 + 8, bigEndian); + } + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int GenericReplyStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GenericReplyMessage *genericReply = (GenericReplyMessage *) message; + + *(buffer + 1) = genericReply -> byte_data; + + for (int i = 0; i < 12; i++) + { + PutUINT(genericReply -> short_data[i], buffer + i * 2 + 8, bigEndian); + } + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void GenericReplyStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + GenericReplyMessage *genericReply = (GenericReplyMessage *) message; + + *logofs << name() << ": Identity byte_data " + << (unsigned) genericReply -> byte_data; + + for (int i = 0; i < 12; i++) + { + *logofs << ", short_data[" << i << "]" + << (unsigned) genericReply -> short_data[i]; + } + + *logofs << ", size " << genericReply -> size_ << ".\n"; + + #endif +} + +void GenericReplyStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ +} + +void GenericReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + // + // Encode the variant part. + // + + GenericReplyMessage *genericReply = (GenericReplyMessage *) message; + GenericReplyMessage *cachedGenericReply = (GenericReplyMessage *) cachedMessage; + + ServerCache *serverCache = (ServerCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " + << (unsigned int) genericReply -> byte_data + << " as byte_data field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(genericReply -> byte_data, 8, + serverCache -> genericReplyCharCache); + + cachedGenericReply -> byte_data = genericReply -> byte_data; + + for (unsigned int i = 0; i < 12; i++) + { + #ifdef TEST + *logofs << name() << ": Encoding value " << genericReply -> short_data[i] + << " as short_data[" << i << "] field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(genericReply -> short_data[i], 16, + *serverCache -> genericReplyIntCache[i]); + + cachedGenericReply -> short_data[i] = genericReply -> short_data[i]; + } +} + +void GenericReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + // + // Decode the variant part. + // + + GenericReplyMessage *genericReply = (GenericReplyMessage *) message; + + ServerCache *serverCache = (ServerCache *) channelCache; + + decodeBuffer.decodeCachedValue(genericReply -> byte_data, 8, + serverCache -> genericReplyCharCache); + + #ifdef TEST + *logofs << name() << ": Decoded value " + << (unsigned int) genericReply -> byte_data + << " as byte_data field.\n" << logofs_flush; + #endif + + unsigned int value; + + for (unsigned int i = 0; i < 12; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *serverCache -> genericReplyIntCache[i]); + + genericReply -> short_data[i] = (unsigned short) value; + + #ifdef TEST + *logofs << name() << ": Decoded value " << genericReply -> short_data[i] + << " as short_data[" << i << "] field.\n" << logofs_flush; + #endif + } +} diff --git a/nxcomp/GenericReply.h b/nxcomp/GenericReply.h new file mode 100644 index 000000000..de97b86d0 --- /dev/null +++ b/nxcomp/GenericReply.h @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef GenericReply_H +#define GenericReply_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define GENERICREPLY_ENABLE_CACHE 1 +#define GENERICREPLY_ENABLE_DATA 1 +#define GENERICREPLY_ENABLE_SPLIT 0 +#define GENERICREPLY_ENABLE_COMPRESS 1 + +#define GENERICREPLY_DATA_LIMIT 1048576 - 32 +#define GENERICREPLY_DATA_OFFSET 32 + +#define GENERICREPLY_CACHE_SLOTS 400 +#define GENERICREPLY_CACHE_THRESHOLD 5 +#define GENERICREPLY_CACHE_LOWER_THRESHOLD 1 + +#define GENERICREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 + +// +// The message class. +// + +class GenericReplyMessage : public Message +{ + friend class GenericReplyStore; + + public: + + GenericReplyMessage() + { + } + + ~GenericReplyMessage() + { + } + + // + // Put here the fields which constitute the + // 'identity' part of the message. Starting + // from protocol level 3 we use short data + // to increase cache efficiency. + // + + private: + + unsigned char byte_data; + unsigned int int_data[6]; + unsigned short short_data[12]; +}; + +class GenericReplyStore : public MessageStore +{ + public: + + GenericReplyStore(StaticCompressor *compressor); + + virtual ~GenericReplyStore(); + + virtual const char *name() const + { + return "GenericReply"; + } + + virtual unsigned char opcode() const + { + return X_NXInternalGenericReply; + } + + virtual unsigned int storage() const + { + return sizeof(GenericReplyMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new GenericReplyMessage(); + } + + virtual Message *create(const Message &message) const + { + return new GenericReplyMessage((const GenericReplyMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (GenericReplyMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* GenericReply_H */ diff --git a/nxcomp/GenericRequest.cpp b/nxcomp/GenericRequest.cpp new file mode 100644 index 000000000..40077291b --- /dev/null +++ b/nxcomp/GenericRequest.cpp @@ -0,0 +1,327 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "GenericRequest.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +GenericRequestStore::GenericRequestStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = GENERICREQUEST_ENABLE_CACHE; + enableData = GENERICREQUEST_ENABLE_DATA; + enableSplit = GENERICREQUEST_ENABLE_SPLIT; + enableCompress = GENERICREQUEST_ENABLE_COMPRESS; + + if (control -> isProtoStep7() == 1) + { + enableCompress = GENERICREQUEST_ENABLE_COMPRESS_IF_PROTO_STEP_7; + + enableCompress = 0; + } + + dataLimit = GENERICREQUEST_DATA_LIMIT; + dataOffset = GENERICREQUEST_DATA_OFFSET; + + cacheSlots = GENERICREQUEST_CACHE_SLOTS; + cacheThreshold = GENERICREQUEST_CACHE_THRESHOLD; + cacheLowerThreshold = GENERICREQUEST_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +GenericRequestStore::~GenericRequestStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int GenericRequestStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; + #endif + + encodeBuffer.encodeValue(size >> 2, 16, 10); + + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + clientCache -> genericRequestOpcodeCache); + + for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) + { + #ifdef DEBUG + *logofs << name() << ": Encoding data[" << i << "] " + << "at position " << i * 2 + 4 << " with value " + << GetUINT(buffer + (i * 2) + 4, bigEndian) + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(GetUINT(buffer + (i * 2) + 4, bigEndian), 16, + *clientCache -> genericRequestDataCache[i]); + } + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int GenericRequestStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; + #endif + + decodeBuffer.decodeValue(size, 16, 10); + + size <<= 2; + + buffer = writeBuffer -> addMessage(size); + + decodeBuffer.decodeCachedValue(*(buffer + 1), 8, + clientCache -> genericRequestOpcodeCache); + + unsigned int value; + + for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache -> genericRequestDataCache[i]); + + #ifdef DEBUG + *logofs << name() << ": Decoding data[" << i << "] " + << "at position " << i * 2 + 4 << " with value " + << value << ".\n" << logofs_flush; + #endif + + PutUINT(value, buffer + 4 + (i * 2), bigEndian); + } + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int GenericRequestStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GenericRequestMessage *genericRequest = (GenericRequestMessage *) message; + + genericRequest -> opcode = *(buffer + 1); + + for (unsigned int i = 0; i < 8; i++) + { + if ((i * 2 + 4) < size) + { + genericRequest -> data[i] = GetUINT(buffer + i * 2 + 4, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed data[" << i << "] " + << "with value " << genericRequest -> data[i] + << ".\n" << logofs_flush; + #endif + } + else + { + genericRequest -> data[i] = 0; + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int GenericRequestStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GenericRequestMessage *genericRequest = (GenericRequestMessage *) message; + + *(buffer + 1) = genericRequest -> opcode; + + for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) + { + #ifdef DEBUG + *logofs << name() << ": Unparsed data[" << i << "] " + << "with value " << genericRequest -> data[i] + << ".\n" << logofs_flush; + #endif + + PutUINT(genericRequest -> data[i], buffer + i * 2 + 4, bigEndian); + } + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void GenericRequestStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + GenericRequestMessage *genericRequest = (GenericRequestMessage *) message; + + *logofs << name() << ": Identity opcode " << (unsigned) genericRequest -> opcode; + + for (int i = 0; i < 8; i++) + { + *logofs << ", data[" << i << "] " << genericRequest -> data[i]; + } + + *logofs << ", size " << genericRequest -> size_ << ".\n" << logofs_flush; + + #endif +} + +void GenericRequestStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // As data offset can be beyond the real end of + // the message, we need to include the message's + // size or we will match any message whose size + // is less or equal to the data offset. + // + + md5_append(md5_state_, buffer + 2, 2); +} + +void GenericRequestStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + // + // Encode the variant part. + // + + GenericRequestMessage *genericRequest = (GenericRequestMessage *) message; + GenericRequestMessage *cachedGenericRequest = (GenericRequestMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Updating value " + << (unsigned) genericRequest -> opcode + << " as opcode field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue((unsigned int) genericRequest -> opcode, 8, + clientCache -> genericRequestOpcodeCache); + + cachedGenericRequest -> opcode = genericRequest -> opcode; + + for (int i = 0; i < 8 && (i * 2 + 4) < genericRequest -> size_; i++) + { + #ifdef TEST + *logofs << name() << ": Updating data[" << i << "] " + << "with value " << genericRequest -> data[i] + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue((unsigned int) genericRequest -> data[i], 16, + *clientCache -> genericRequestDataCache[i]); + + cachedGenericRequest -> data[i] = genericRequest -> data[i]; + } +} + +void GenericRequestStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + GenericRequestMessage *genericRequest = (GenericRequestMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(genericRequest -> opcode, 8, + clientCache -> genericRequestOpcodeCache); + + #ifdef TEST + *logofs << name() << ": Updated value " + << (unsigned) genericRequest -> opcode + << " as opcode field.\n" << logofs_flush; + #endif + + unsigned int value; + + for (int i = 0; i < 8 && (i * 2 + 4) < genericRequest -> size_; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache -> genericRequestDataCache[i]); + + genericRequest -> data[i] = (unsigned short) value; + + #ifdef TEST + *logofs << name() << ": Updated data[" << i << "] " + << "with value " << genericRequest -> data[i] + << ".\n" << logofs_flush; + #endif + } +} diff --git a/nxcomp/GenericRequest.h b/nxcomp/GenericRequest.h new file mode 100644 index 000000000..3175fc2ba --- /dev/null +++ b/nxcomp/GenericRequest.h @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef GenericRequest_H +#define GenericRequest_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define GENERICREQUEST_ENABLE_CACHE 1 +#define GENERICREQUEST_ENABLE_DATA 1 +#define GENERICREQUEST_ENABLE_SPLIT 0 +#define GENERICREQUEST_ENABLE_COMPRESS 1 + +#define GENERICREQUEST_DATA_LIMIT 262144 - 20 +#define GENERICREQUEST_DATA_OFFSET 20 + +#define GENERICREQUEST_CACHE_SLOTS 400 +#define GENERICREQUEST_CACHE_THRESHOLD 5 +#define GENERICREQUEST_CACHE_LOWER_THRESHOLD 1 + +#define GENERICREQUEST_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 + +// +// The message class. +// + +class GenericRequestMessage : public Message +{ + friend class GenericRequestStore; + + public: + + GenericRequestMessage() + { + } + + ~GenericRequestMessage() + { + } + + // + // Note that we consider for this message a data offset + // of 4 (or 20 starting from protocol 3). Bytes from 9 + // to 20, if present, are taken as part of identity and + // encoded through an array of int caches. + // + + private: + + unsigned char opcode; + unsigned short data[8]; +}; + +class GenericRequestStore : public MessageStore +{ + public: + + GenericRequestStore(StaticCompressor *compressor); + + virtual ~GenericRequestStore(); + + virtual const char *name() const + { + return "GenericRequest"; + } + + virtual unsigned char opcode() const + { + return X_NXInternalGenericRequest; + } + + virtual unsigned int storage() const + { + return sizeof(GenericRequestMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new GenericRequestMessage(); + } + + virtual Message *create(const Message &message) const + { + return new GenericRequestMessage((const GenericRequestMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (GenericRequestMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* GenericRequest_H */ diff --git a/nxcomp/GetImage.cpp b/nxcomp/GetImage.cpp new file mode 100644 index 000000000..6be574dbb --- /dev/null +++ b/nxcomp/GetImage.cpp @@ -0,0 +1,165 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "GetImage.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int GetImageStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GetImageMessage *getImage = (GetImageMessage *) message; + + // + // Here is the fingerprint. + // + + getImage -> format = *(buffer + 1); + + #ifdef TEST + if (getImage -> format != 1 && getImage -> format != 2) + { + *logofs << name() << ": WARNING! Dirty value " << getImage -> format + << " for field format.\n" << logofs_flush; + } + #endif + + getImage -> drawable = GetULONG(buffer + 4, bigEndian); + + getImage -> x = GetUINT(buffer + 8, bigEndian); + getImage -> y = GetUINT(buffer + 10, bigEndian); + getImage -> width = GetUINT(buffer + 12, bigEndian); + getImage -> height = GetUINT(buffer + 14, bigEndian); + + getImage -> plane_mask = GetULONG(buffer + 16, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int GetImageStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GetImageMessage *getImage = (GetImageMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = getImage -> format; + + PutULONG(getImage -> drawable, buffer + 4, bigEndian); + + PutUINT(getImage -> x, buffer + 8, bigEndian); + PutUINT(getImage -> y, buffer + 10, bigEndian); + PutUINT(getImage -> width, buffer + 12, bigEndian); + PutUINT(getImage -> height, buffer + 14, bigEndian); + + PutULONG(getImage -> plane_mask, buffer + 16, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void GetImageStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + GetImageMessage *getImage = (GetImageMessage *) message; + + *logofs << name() << ": Identity format " << (unsigned) getImage -> format + << ", drawable " << getImage -> drawable << ", x " << getImage -> x + << ", y " << getImage -> y << ", width " << getImage -> width + << ", height " << getImage -> height << ", plane_mask " + << getImage -> plane_mask << ", size " << getImage -> size_ + << ".\n" << logofs_flush; + + #endif +} + +void GetImageStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 1, 1); + md5_append(md5_state_, buffer + 8, 2); + md5_append(md5_state_, buffer + 10, 2); + md5_append(md5_state_, buffer + 12, 2); + md5_append(md5_state_, buffer + 14, 2); + md5_append(md5_state_, buffer + 16, 4); +} + +void GetImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + GetImageMessage *getImage = (GetImageMessage *) message; + GetImageMessage *cachedGetImage = (GetImageMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << getImage -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(getImage -> drawable, clientCache -> drawableCache); + + cachedGetImage -> drawable = getImage -> drawable; +} + +void GetImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + GetImageMessage *getImage = (GetImageMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + getImage -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << getImage -> drawable + << " as drawable field.\n" << logofs_flush; + #endif +} + diff --git a/nxcomp/GetImage.h b/nxcomp/GetImage.h new file mode 100644 index 000000000..f48fb9079 --- /dev/null +++ b/nxcomp/GetImage.h @@ -0,0 +1,182 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef GetImage_H +#define GetImage_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define GETIMAGE_ENABLE_CACHE 1 +#define GETIMAGE_ENABLE_DATA 0 +#define GETIMAGE_ENABLE_SPLIT 0 +#define GETIMAGE_ENABLE_COMPRESS 0 + +#define GETIMAGE_DATA_LIMIT 0 +#define GETIMAGE_DATA_OFFSET 20 + +#define GETIMAGE_CACHE_SLOTS 200 +#define GETIMAGE_CACHE_THRESHOLD 1 +#define GETIMAGE_CACHE_LOWER_THRESHOLD 0 + +// +// The message class. +// + +class GetImageMessage : public Message +{ + friend class GetImageStore; + + public: + + GetImageMessage() + { + } + + ~GetImageMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char format; + unsigned int drawable; + unsigned short int x; + unsigned short int y; + unsigned short int width; + unsigned short int height; + unsigned int plane_mask; +}; + +class GetImageStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + GetImageStore() : MessageStore() + { + enableCache = GETIMAGE_ENABLE_CACHE; + enableData = GETIMAGE_ENABLE_DATA; + enableSplit = GETIMAGE_ENABLE_SPLIT; + enableCompress = GETIMAGE_ENABLE_COMPRESS; + + dataLimit = GETIMAGE_DATA_LIMIT; + dataOffset = GETIMAGE_DATA_OFFSET; + + cacheSlots = GETIMAGE_CACHE_SLOTS; + cacheThreshold = GETIMAGE_CACHE_THRESHOLD; + cacheLowerThreshold = GETIMAGE_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~GetImageStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "GetImage"; + } + + virtual unsigned char opcode() const + { + return X_GetImage; + } + + virtual unsigned int storage() const + { + return sizeof(GetImageMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new GetImageMessage(); + } + + virtual Message *create(const Message &message) const + { + return new GetImageMessage((const GetImageMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (GetImageMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* GetImage_H */ diff --git a/nxcomp/GetImageReply.cpp b/nxcomp/GetImageReply.cpp new file mode 100644 index 000000000..d4bc21b5a --- /dev/null +++ b/nxcomp/GetImageReply.cpp @@ -0,0 +1,185 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "GetImageReply.h" + +#include "ServerCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +GetImageReplyStore::GetImageReplyStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = GETIMAGEREPLY_ENABLE_CACHE; + enableData = GETIMAGEREPLY_ENABLE_DATA; + enableSplit = GETIMAGEREPLY_ENABLE_SPLIT; + enableCompress = GETIMAGEREPLY_ENABLE_COMPRESS; + + if (control -> isProtoStep7() == 1) + { + enableCompress = GETIMAGEREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7; + } + + dataLimit = GETIMAGEREPLY_DATA_LIMIT; + dataOffset = GETIMAGEREPLY_DATA_OFFSET; + + cacheSlots = GETIMAGEREPLY_CACHE_SLOTS; + cacheThreshold = GETIMAGEREPLY_CACHE_THRESHOLD; + cacheLowerThreshold = GETIMAGEREPLY_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +GetImageReplyStore::~GetImageReplyStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int GetImageReplyStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message; + + // + // Here is the fingerprint. + // + + getImageReply -> depth = *(buffer + 1); + + getImageReply -> visual = GetULONG(buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int GetImageReplyStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = getImageReply -> depth; + + PutULONG(getImageReply -> visual, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void GetImageReplyStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message; + + *logofs << name() << ": Identity depth " << (unsigned) getImageReply -> depth + << ", visual " << getImageReply -> visual << ", size " + << getImageReply -> size_ << ".\n"; + + #endif +} + +void GetImageReplyStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // Field depth. + // + + md5_append(md5_state_, buffer + 1, 1); +} + +void GetImageReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + // + // Encode the variant part. + // + + GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message; + + ServerCache *serverCache = (ServerCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << getImageReply -> visual + << " as visual field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(getImageReply -> visual, 29, + serverCache -> visualCache); +} + +void GetImageReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + // + // Decode the variant part. + // + + GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message; + + ServerCache *serverCache = (ServerCache *) channelCache; + + decodeBuffer.decodeCachedValue(getImageReply -> visual, 29, + serverCache -> visualCache); +} diff --git a/nxcomp/GetImageReply.h b/nxcomp/GetImageReply.h new file mode 100644 index 000000000..ee7b0bfa3 --- /dev/null +++ b/nxcomp/GetImageReply.h @@ -0,0 +1,143 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef GetImageReply_H +#define GetImageReply_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define GETIMAGEREPLY_ENABLE_CACHE 1 +#define GETIMAGEREPLY_ENABLE_DATA 1 +#define GETIMAGEREPLY_ENABLE_SPLIT 0 +#define GETIMAGEREPLY_ENABLE_COMPRESS 1 + +#define GETIMAGEREPLY_DATA_LIMIT 1048576 - 32 +#define GETIMAGEREPLY_DATA_OFFSET 32 + +#define GETIMAGEREPLY_CACHE_SLOTS 1000 +#define GETIMAGEREPLY_CACHE_THRESHOLD 20 +#define GETIMAGEREPLY_CACHE_LOWER_THRESHOLD 2 + +#define GETIMAGEREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 + +// +// The message class. +// + +class GetImageReplyMessage : public Message +{ + friend class GetImageReplyStore; + + public: + + GetImageReplyMessage() + { + } + + ~GetImageReplyMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char depth; + unsigned int visual; +}; + +class GetImageReplyStore : public MessageStore +{ + public: + + GetImageReplyStore(StaticCompressor *compressor); + + virtual ~GetImageReplyStore(); + + virtual const char *name() const + { + return "GetImageReply"; + } + + virtual unsigned char opcode() const + { + return X_GetImage; + } + + virtual unsigned int storage() const + { + return sizeof(GetImageReplyMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new GetImageReplyMessage(); + } + + virtual Message *create(const Message &message) const + { + return new GetImageReplyMessage((const GetImageReplyMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (GetImageReplyMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* GetImageReply_H */ diff --git a/nxcomp/GetProperty.cpp b/nxcomp/GetProperty.cpp new file mode 100644 index 000000000..d358f8a6f --- /dev/null +++ b/nxcomp/GetProperty.cpp @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "GetProperty.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int GetPropertyStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GetPropertyMessage *getProperty = (GetPropertyMessage *) message; + + // + // Here is the fingerprint. + // + + getProperty -> property_delete = *(buffer + 1); + + getProperty -> window = GetULONG(buffer + 4, bigEndian); + getProperty -> property = GetULONG(buffer + 8, bigEndian); + getProperty -> type = GetULONG(buffer + 12, bigEndian); + getProperty -> long_offset = GetULONG(buffer + 16, bigEndian); + getProperty -> long_length = GetULONG(buffer + 20, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int GetPropertyStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + + GetPropertyMessage *getProperty = (GetPropertyMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = getProperty -> property_delete; + + PutULONG(getProperty -> window, buffer + 4, bigEndian); + PutULONG(getProperty -> property, buffer + 8, bigEndian); + PutULONG(getProperty -> type, buffer + 12, bigEndian); + PutULONG(getProperty -> long_offset, buffer + 16, bigEndian); + PutULONG(getProperty -> long_length, buffer + 20, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void GetPropertyStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + GetPropertyMessage *getProperty = (GetPropertyMessage *) message; + + *logofs << name() << ": Identity property_delete " << (unsigned int) getProperty -> property_delete + << ", window " << getProperty -> window << ", property " << getProperty -> property + << ", type " << getProperty -> type << ", long-offset " << getProperty -> long_offset + << ", long-length " << getProperty -> long_length << ".\n" << logofs_flush; + + #endif +} + +void GetPropertyStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 1, 1); + md5_append(md5_state_, buffer + 4, 20); +} diff --git a/nxcomp/GetProperty.h b/nxcomp/GetProperty.h new file mode 100644 index 000000000..a73425354 --- /dev/null +++ b/nxcomp/GetProperty.h @@ -0,0 +1,175 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef GetProperty_H +#define GetProperty_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define GETPROPERTY_ENABLE_CACHE 1 +#define GETPROPERTY_ENABLE_DATA 0 +#define GETPROPERTY_ENABLE_SPLIT 0 +#define GETPROPERTY_ENABLE_COMPRESS 0 + +#define GETPROPERTY_DATA_LIMIT 0 +#define GETPROPERTY_DATA_OFFSET 24 + +#define GETPROPERTY_CACHE_SLOTS 2000 +#define GETPROPERTY_CACHE_THRESHOLD 2 +#define GETPROPERTY_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class GetPropertyMessage : public Message +{ + friend class GetPropertyStore; + + public: + + GetPropertyMessage() + { + } + + ~GetPropertyMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char property_delete; + unsigned long window; + unsigned long property; + unsigned long type; + unsigned long long_offset; + unsigned long long_length; +}; + +class GetPropertyStore : public MessageStore +{ + + // + // Constructors and destructors. + // + + public: + + GetPropertyStore() : MessageStore() + { + enableCache = GETPROPERTY_ENABLE_CACHE; + enableData = GETPROPERTY_ENABLE_DATA; + enableSplit = GETPROPERTY_ENABLE_SPLIT; + enableCompress = GETPROPERTY_ENABLE_COMPRESS; + + dataLimit = GETPROPERTY_DATA_LIMIT; + dataOffset = GETPROPERTY_DATA_OFFSET; + + cacheSlots = GETPROPERTY_CACHE_SLOTS; + cacheThreshold = GETPROPERTY_CACHE_THRESHOLD; + cacheLowerThreshold = GETPROPERTY_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~GetPropertyStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "GetProperty"; + } + + virtual unsigned char opcode() const + { + return X_GetProperty; + } + + virtual unsigned int storage() const + { + return sizeof(GetPropertyMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new GetPropertyMessage(); + } + + virtual Message *create(const Message &message) const + { + return new GetPropertyMessage((const GetPropertyMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (GetPropertyMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* GetProperty_H */ diff --git a/nxcomp/GetPropertyReply.cpp b/nxcomp/GetPropertyReply.cpp new file mode 100644 index 000000000..223131803 --- /dev/null +++ b/nxcomp/GetPropertyReply.cpp @@ -0,0 +1,295 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "GetPropertyReply.h" + +#include "ServerCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +GetPropertyReplyStore::GetPropertyReplyStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = GETPROPERTYREPLY_ENABLE_CACHE; + enableData = GETPROPERTYREPLY_ENABLE_DATA; + enableSplit = GETPROPERTYREPLY_ENABLE_SPLIT; + enableCompress = GETPROPERTYREPLY_ENABLE_COMPRESS; + + if (control -> isProtoStep7() == 1) + { + enableCompress = GETPROPERTYREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7; + } + + dataLimit = GETPROPERTYREPLY_DATA_LIMIT; + dataOffset = GETPROPERTYREPLY_DATA_OFFSET; + + cacheSlots = GETPROPERTYREPLY_CACHE_SLOTS; + cacheThreshold = GETPROPERTYREPLY_CACHE_THRESHOLD; + cacheLowerThreshold = GETPROPERTYREPLY_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +GetPropertyReplyStore::~GetPropertyReplyStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int GetPropertyReplyStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ServerCache *serverCache = (ServerCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" + << logofs_flush; + #endif + + unsigned char format = (unsigned int) *(buffer + 1); + + encodeBuffer.encodeCachedValue(format, 8, + serverCache -> getPropertyFormatCache); + + unsigned int numBytes = GetULONG(buffer + 16, bigEndian); + + encodeBuffer.encodeValue(numBytes, 32, 9); + + if (format == 16) + { + numBytes <<= 1; + } + else if (format == 32) + { + numBytes <<= 2; + } + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 29, + serverCache -> getPropertyTypeCache, 9); + + encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9); + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" + << logofs_flush; + #endif + + return 1; +} + +int GetPropertyReplyStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ServerCache *serverCache = (ServerCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" + << logofs_flush; + #endif + + unsigned char format; + + decodeBuffer.decodeCachedValue(format, 8, + serverCache -> getPropertyFormatCache); + + unsigned int length; + + decodeBuffer.decodeValue(length, 32, 9); + + unsigned int numBytes = length; + + if (format == 16) + { + numBytes <<= 1; + } + else if (format == 32) + { + numBytes <<= 2; + } + + size = 32 + RoundUp4(numBytes); + + buffer = writeBuffer -> addMessage(size); + + *(buffer + 1) = format; + + PutULONG(length, buffer + 16, bigEndian); + + unsigned int value; + + decodeBuffer.decodeCachedValue(value, 29, + serverCache -> getPropertyTypeCache, 9); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeValue(value, 32, 9); + + PutULONG(value, buffer + 12, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" + << logofs_flush; + #endif + + return 1; +} + +int GetPropertyReplyStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message; + + getPropertyReply -> format = *(buffer + 1); + + getPropertyReply -> type = GetULONG(buffer + 8, bigEndian); + getPropertyReply -> after = GetULONG(buffer + 12, bigEndian); + getPropertyReply -> items = GetULONG(buffer + 16, bigEndian); + + // + // Cleanup the padding bytes. + // + + unsigned int uiLengthInBytes; + unsigned int uiFormat; + + if ((int) size > GETPROPERTYREPLY_DATA_OFFSET) + { + uiLengthInBytes = getPropertyReply -> items; + + uiFormat = *(buffer + 1); + + #ifdef DEBUG + *logofs << name() << ": length " << uiLengthInBytes + << ", format " << uiFormat << ", size " + << size << ".\n" << logofs_flush; + #endif + + if (uiFormat == 16) + { + uiLengthInBytes <<= 1; + } + else if (uiFormat == 32) + { + uiLengthInBytes <<= 2; + } + + unsigned char *end = ((unsigned char *) buffer) + size; + unsigned char *pad = ((unsigned char *) buffer) + GETPROPERTYREPLY_DATA_OFFSET + uiLengthInBytes; + + CleanData((unsigned char *) pad, end - pad); + } + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int GetPropertyReplyStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message; + + *(buffer + 1) = getPropertyReply -> format; + + PutULONG(getPropertyReply -> type, buffer + 8, bigEndian); + PutULONG(getPropertyReply -> after, buffer + 12, bigEndian); + PutULONG(getPropertyReply -> items, buffer + 16, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void GetPropertyReplyStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message; + + *logofs << name() << ": Identity format " + << (unsigned) getPropertyReply -> format << ", type " + << getPropertyReply -> type << ", after " << getPropertyReply -> after + << ", items " << getPropertyReply -> items << ", size " + << getPropertyReply -> size_ << ".\n"; + + #endif +} + +void GetPropertyReplyStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // Fields format, type, after, items. + // + + md5_append(md5_state_, buffer + 1, 1); + md5_append(md5_state_, buffer + 8, 12); +} + +void GetPropertyReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ +} + +void GetPropertyReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ +} diff --git a/nxcomp/GetPropertyReply.h b/nxcomp/GetPropertyReply.h new file mode 100644 index 000000000..0f6b19508 --- /dev/null +++ b/nxcomp/GetPropertyReply.h @@ -0,0 +1,153 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef GetPropertyReply_H +#define GetPropertyReply_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define GETPROPERTYREPLY_ENABLE_CACHE 1 +#define GETPROPERTYREPLY_ENABLE_DATA 1 +#define GETPROPERTYREPLY_ENABLE_SPLIT 0 +#define GETPROPERTYREPLY_ENABLE_COMPRESS 1 + +#define GETPROPERTYREPLY_DATA_LIMIT 1048576 - 32 +#define GETPROPERTYREPLY_DATA_OFFSET 32 + +#define GETPROPERTYREPLY_CACHE_SLOTS 400 +#define GETPROPERTYREPLY_CACHE_THRESHOLD 5 +#define GETPROPERTYREPLY_CACHE_LOWER_THRESHOLD 1 + +#define GETPROPERTYREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 + +// +// The message class. +// + +class GetPropertyReplyMessage : public Message +{ + friend class GetPropertyReplyStore; + + public: + + GetPropertyReplyMessage() + { + } + + ~GetPropertyReplyMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char format; + unsigned int type; + unsigned int after; + unsigned int items; +}; + +class GetPropertyReplyStore : public MessageStore +{ + public: + + GetPropertyReplyStore(StaticCompressor *compressor); + + virtual ~GetPropertyReplyStore(); + + virtual const char *name() const + { + return "GetPropertyReply"; + } + + virtual unsigned char opcode() const + { + return X_GetProperty; + } + + virtual unsigned int storage() const + { + return sizeof(GetPropertyReplyMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new GetPropertyReplyMessage(); + } + + virtual Message *create(const Message &message) const + { + return new GetPropertyReplyMessage((const GetPropertyReplyMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (GetPropertyReplyMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* GetPropertyReply_H */ diff --git a/nxcomp/ImageText16.cpp b/nxcomp/ImageText16.cpp new file mode 100644 index 000000000..569fdbe13 --- /dev/null +++ b/nxcomp/ImageText16.cpp @@ -0,0 +1,219 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ImageText16.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int ImageText16Store::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ImageText16Message *imageText16 = (ImageText16Message *) message; + + // + // Here is the fingerprint. + // + + imageText16 -> len = *(buffer + 1); + + imageText16 -> drawable = GetULONG(buffer + 4, bigEndian); + imageText16 -> gcontext = GetULONG(buffer + 8, bigEndian); + + imageText16 -> x = GetUINT(buffer + 12, bigEndian); + imageText16 -> y = GetUINT(buffer + 14, bigEndian); + + if ((int) size > dataOffset) + { + int pad = (size - dataOffset) - (imageText16 -> len * 2); + + if (pad > 0) + { + CleanData((unsigned char *) buffer + size - pad, pad); + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ImageText16Store::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ImageText16Message *imageText16 = (ImageText16Message *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = imageText16 -> len; + + PutULONG(imageText16 -> drawable, buffer + 4, bigEndian); + PutULONG(imageText16 -> gcontext, buffer + 8, bigEndian); + + PutUINT(imageText16 -> x, buffer + 12, bigEndian); + PutUINT(imageText16 -> y, buffer + 14, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void ImageText16Store::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + ImageText16Message *imageText16 = (ImageText16Message *) message; + + *logofs << name() << ": Identity len " << (unsigned int) imageText16 -> len + << " drawable " << imageText16 -> drawable << ", gcontext " + << imageText16 -> gcontext << ", x " << imageText16 -> x << ", y " + << imageText16 -> y << ", size " << imageText16 -> size_ << ".\n"; + + #endif +} + +void ImageText16Store::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 1, 1); +} + +void ImageText16Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + ImageText16Message *imageText16 = (ImageText16Message *) message; + ImageText16Message *cachedImageText16 = (ImageText16Message *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << imageText16 -> drawable + << " as " << "drawable" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(imageText16 -> drawable, clientCache -> drawableCache); + + cachedImageText16 -> drawable = imageText16 -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << imageText16 -> gcontext + << " as " << "gcontext" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(imageText16 -> gcontext, clientCache -> gcCache); + + cachedImageText16 -> gcontext = imageText16 -> gcontext; + + #ifdef TEST + *logofs << name() << ": Encoding value " << imageText16 -> x + << " as " << "x" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_x = imageText16 -> x - cachedImageText16 -> x; + + encodeBuffer.encodeCachedValue(diff_x, 16, + clientCache -> imageTextCacheX); + + cachedImageText16 -> x = imageText16 -> x; + + #ifdef TEST + *logofs << name() << ": Encoding value " << imageText16 -> y + << " as " << "y" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_y = imageText16 -> y - cachedImageText16 -> y; + + encodeBuffer.encodeCachedValue(diff_y, 16, + clientCache -> imageTextCacheY); + + cachedImageText16 -> y = imageText16 -> y; +} + +void ImageText16Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + ImageText16Message *imageText16 = (ImageText16Message *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + imageText16 -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << imageText16 -> drawable + << " as " << "drawable" << " field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + imageText16 -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << imageText16 -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> imageTextCacheX); + + imageText16 -> x += value; + imageText16 -> x &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << imageText16 -> x + << " as x field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> imageTextCacheY); + + imageText16 -> y += value; + imageText16 -> y &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << imageText16 -> y + << " as y field.\n" << logofs_flush; + #endif +} + + diff --git a/nxcomp/ImageText16.h b/nxcomp/ImageText16.h new file mode 100644 index 000000000..0e116a4fe --- /dev/null +++ b/nxcomp/ImageText16.h @@ -0,0 +1,182 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ImageText16_H +#define ImageText16_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define IMAGETEXT16_ENABLE_CACHE 1 +#define IMAGETEXT16_ENABLE_DATA 0 +#define IMAGETEXT16_ENABLE_SPLIT 0 +#define IMAGETEXT16_ENABLE_COMPRESS 0 + +#define IMAGETEXT16_DATA_LIMIT 512 +#define IMAGETEXT16_DATA_OFFSET 16 + +#define IMAGETEXT16_CACHE_SLOTS 3000 +#define IMAGETEXT16_CACHE_THRESHOLD 5 +#define IMAGETEXT16_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class ImageText16Message : public Message +{ + friend class ImageText16Store; + + public: + + ImageText16Message() + { + } + + ~ImageText16Message() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char len; + + unsigned int drawable; + unsigned int gcontext; + + unsigned short x; + unsigned short y; +}; + +class ImageText16Store : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + ImageText16Store() : MessageStore() + { + enableCache = IMAGETEXT16_ENABLE_CACHE; + enableData = IMAGETEXT16_ENABLE_DATA; + enableSplit = IMAGETEXT16_ENABLE_SPLIT; + enableCompress = IMAGETEXT16_ENABLE_COMPRESS; + + dataLimit = IMAGETEXT16_DATA_LIMIT; + dataOffset = IMAGETEXT16_DATA_OFFSET; + + cacheSlots = IMAGETEXT16_CACHE_SLOTS; + cacheThreshold = IMAGETEXT16_CACHE_THRESHOLD; + cacheLowerThreshold = IMAGETEXT16_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~ImageText16Store() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "ImageText16"; + } + + virtual unsigned char opcode() const + { + return X_ImageText16; + } + + virtual unsigned int storage() const + { + return sizeof(ImageText16Message); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new ImageText16Message(); + } + + virtual Message *create(const Message &message) const + { + return new ImageText16Message((const ImageText16Message &) message); + } + + virtual void destroy(Message *message) const + { + delete (ImageText16Message *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* ImageText16_H */ diff --git a/nxcomp/ImageText8.cpp b/nxcomp/ImageText8.cpp new file mode 100644 index 000000000..161977677 --- /dev/null +++ b/nxcomp/ImageText8.cpp @@ -0,0 +1,219 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ImageText8.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int ImageText8Store::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ImageText8Message *imageText8 = (ImageText8Message *) message; + + // + // Here is the fingerprint. + // + + imageText8 -> len = *(buffer + 1); + + imageText8 -> drawable = GetULONG(buffer + 4, bigEndian); + imageText8 -> gcontext = GetULONG(buffer + 8, bigEndian); + + imageText8 -> x = GetUINT(buffer + 12, bigEndian); + imageText8 -> y = GetUINT(buffer + 14, bigEndian); + + if ((int) size > dataOffset) + { + int pad = (size - dataOffset) - imageText8 -> len; + + if (pad > 0) + { + CleanData((unsigned char *) buffer + size - pad, pad); + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ImageText8Store::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ImageText8Message *imageText8 = (ImageText8Message *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = imageText8 -> len; + + PutULONG(imageText8 -> drawable, buffer + 4, bigEndian); + PutULONG(imageText8 -> gcontext, buffer + 8, bigEndian); + + PutUINT(imageText8 -> x, buffer + 12, bigEndian); + PutUINT(imageText8 -> y, buffer + 14, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void ImageText8Store::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + ImageText8Message *imageText8 = (ImageText8Message *) message; + + *logofs << name() << ": Identity len " << (unsigned int) imageText8 -> len + << " drawable " << imageText8 -> drawable << ", gcontext " + << imageText8 -> gcontext << ", x " << imageText8 -> x << ", y " + << imageText8 -> y << ", size " << imageText8 -> size_ << ".\n"; + + #endif +} + +void ImageText8Store::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 1, 1); +} + +void ImageText8Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + ImageText8Message *imageText8 = (ImageText8Message *) message; + ImageText8Message *cachedImageText8 = (ImageText8Message *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << imageText8 -> drawable + << " as " << "drawable" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(imageText8 -> drawable, clientCache -> drawableCache); + + cachedImageText8 -> drawable = imageText8 -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << imageText8 -> gcontext + << " as " << "gcontext" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(imageText8 -> gcontext, clientCache -> gcCache); + + cachedImageText8 -> gcontext = imageText8 -> gcontext; + + #ifdef TEST + *logofs << name() << ": Encoding value " << imageText8 -> x + << " as " << "x" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_x = imageText8 -> x - cachedImageText8 -> x; + + encodeBuffer.encodeCachedValue(diff_x, 16, + clientCache -> imageTextCacheX); + + cachedImageText8 -> x = imageText8 -> x; + + #ifdef TEST + *logofs << name() << ": Encoding value " << imageText8 -> y + << " as " << "y" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_y = imageText8 -> y - cachedImageText8 -> y; + + encodeBuffer.encodeCachedValue(diff_y, 16, + clientCache -> imageTextCacheY); + + cachedImageText8 -> y = imageText8 -> y; +} + +void ImageText8Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + ImageText8Message *imageText8 = (ImageText8Message *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + imageText8 -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << imageText8 -> drawable + << " as " << "drawable" << " field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + imageText8 -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << imageText8 -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> imageTextCacheX); + + imageText8 -> x += value; + imageText8 -> x &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << imageText8 -> x + << " as x field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> imageTextCacheY); + + imageText8 -> y += value; + imageText8 -> y &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << imageText8 -> y + << " as y field.\n" << logofs_flush; + #endif +} + + diff --git a/nxcomp/ImageText8.h b/nxcomp/ImageText8.h new file mode 100644 index 000000000..c56502f90 --- /dev/null +++ b/nxcomp/ImageText8.h @@ -0,0 +1,182 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ImageText8_H +#define ImageText8_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define IMAGETEXT8_ENABLE_CACHE 1 +#define IMAGETEXT8_ENABLE_DATA 0 +#define IMAGETEXT8_ENABLE_SPLIT 0 +#define IMAGETEXT8_ENABLE_COMPRESS 0 + +#define IMAGETEXT8_DATA_LIMIT 256 +#define IMAGETEXT8_DATA_OFFSET 16 + +#define IMAGETEXT8_CACHE_SLOTS 3000 +#define IMAGETEXT8_CACHE_THRESHOLD 5 +#define IMAGETEXT8_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class ImageText8Message : public Message +{ + friend class ImageText8Store; + + public: + + ImageText8Message() + { + } + + ~ImageText8Message() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char len; + + unsigned int drawable; + unsigned int gcontext; + + unsigned short x; + unsigned short y; +}; + +class ImageText8Store : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + ImageText8Store() : MessageStore() + { + enableCache = IMAGETEXT8_ENABLE_CACHE; + enableData = IMAGETEXT8_ENABLE_DATA; + enableSplit = IMAGETEXT8_ENABLE_SPLIT; + enableCompress = IMAGETEXT8_ENABLE_COMPRESS; + + dataLimit = IMAGETEXT8_DATA_LIMIT; + dataOffset = IMAGETEXT8_DATA_OFFSET; + + cacheSlots = IMAGETEXT8_CACHE_SLOTS; + cacheThreshold = IMAGETEXT8_CACHE_THRESHOLD; + cacheLowerThreshold = IMAGETEXT8_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~ImageText8Store() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "ImageText8"; + } + + virtual unsigned char opcode() const + { + return X_ImageText8; + } + + virtual unsigned int storage() const + { + return sizeof(ImageText8Message); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new ImageText8Message(); + } + + virtual Message *create(const Message &message) const + { + return new ImageText8Message((const ImageText8Message &) message); + } + + virtual void destroy(Message *message) const + { + delete (ImageText8Message *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* ImageText8_H */ diff --git a/nxcomp/IntCache.cpp b/nxcomp/IntCache.cpp new file mode 100644 index 000000000..8262d5605 --- /dev/null +++ b/nxcomp/IntCache.cpp @@ -0,0 +1,218 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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/IntCache.h b/nxcomp/IntCache.h new file mode 100644 index 000000000..15cc8ea53 --- /dev/null +++ b/nxcomp/IntCache.h @@ -0,0 +1,111 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef IntCache_H +#define IntCache_H + +class IntCache +{ + public: + + IntCache(unsigned int size); + + ~IntCache() + { + delete [] buffer_; + } + + unsigned int getSize() const + { + return length_; + } + + int lookup(unsigned int &value, unsigned int &index, + unsigned int mask, unsigned int &sameDiff); + + // + // This can be inlined as it is only + // called by decodeCachedValue(). + // + + unsigned int get(unsigned int index) + { + unsigned int result = buffer_[index]; + + if (index != 0) + { + // + // Using a memmove() appears to be slower. + // + // unsigned int target = index >> 1; + // + // memmove((char *) &buffer_[target + 1], (char *) &buffer_[target], + // sizeof(unsigned int) * (index - target)); + // + // buffer_[target] = result; + // + + unsigned int i = index; + + unsigned int target = (i >> 1); + + do + { + buffer_[i] = buffer_[i - 1]; + + i--; + } + while (i > target); + + buffer_[target] = result; + } + + return result; + } + + void insert(unsigned int &value, unsigned int mask); + + void push(unsigned int &value, unsigned int mask); + + void dump(); + + unsigned int getLastDiff(unsigned int mask) const + { + return lastDiff_; + } + + unsigned int getBlockSize(unsigned int bits) const + { + if (bits > 0) + { + return (predictedBlockSize_ < bits ? predictedBlockSize_ : bits); + } + + return predictedBlockSize_; + } + + private: + + unsigned int size_; + unsigned int length_; + unsigned int *buffer_; + unsigned int lastDiff_; + unsigned int lastValueInserted_; + unsigned int predictedBlockSize_; +}; + +#endif /* IntCache_H */ diff --git a/nxcomp/InternAtom.cpp b/nxcomp/InternAtom.cpp new file mode 100644 index 000000000..d90c8c058 --- /dev/null +++ b/nxcomp/InternAtom.cpp @@ -0,0 +1,119 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "InternAtom.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int InternAtomStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + InternAtomMessage *internAtom = (InternAtomMessage *) message; + + // + // Here is the fingerprint. + // + + internAtom -> only_if_exists = *(buffer + 1); + internAtom -> name_length = GetUINT(buffer + 4, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + // + // Clean up padding bytes. + // + + if ((int) size > dataOffset) + { + unsigned char *end = ((unsigned char *) buffer) + size; + + for (unsigned char *pad = ((unsigned char *) buffer) + 8 + + internAtom -> name_length; pad < end; pad++) + { + *pad = 0; + } + } + + return 1; +} + +int InternAtomStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + InternAtomMessage *internAtom = (InternAtomMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = internAtom -> only_if_exists; + + PutUINT(internAtom -> name_length, buffer + 4, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void InternAtomStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + InternAtomMessage *internAtom = (InternAtomMessage *) message; + + *logofs << name() << ": Identity only_if_exists " + << (unsigned int) internAtom -> only_if_exists + << ", name_length " << internAtom -> name_length + << ", name '"; + + for (int i = 0; i < internAtom -> name_length; i++) + { + *logofs << internAtom -> data_[i]; + } + + *logofs << "'.\n" << logofs_flush; + + #endif +} + +void InternAtomStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 1, 1); + md5_append(md5_state_, buffer + 4, 2); +} diff --git a/nxcomp/InternAtom.h b/nxcomp/InternAtom.h new file mode 100644 index 000000000..f7f366dce --- /dev/null +++ b/nxcomp/InternAtom.h @@ -0,0 +1,170 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef InternAtom_H +#define InternAtom_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define INTERNATOM_ENABLE_CACHE 1 +#define INTERNATOM_ENABLE_DATA 0 +#define INTERNATOM_ENABLE_SPLIT 0 +#define INTERNATOM_ENABLE_COMPRESS 0 + +#define INTERNATOM_DATA_LIMIT 80 +#define INTERNATOM_DATA_OFFSET 8 + +#define INTERNATOM_CACHE_SLOTS 2000 +#define INTERNATOM_CACHE_THRESHOLD 2 +#define INTERNATOM_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class InternAtomMessage : public Message +{ + friend class InternAtomStore; + + public: + + InternAtomMessage() + { + } + + ~InternAtomMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char only_if_exists; + unsigned short name_length; +}; + +class InternAtomStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + InternAtomStore() : MessageStore() + { + enableCache = INTERNATOM_ENABLE_CACHE; + enableData = INTERNATOM_ENABLE_DATA; + enableSplit = INTERNATOM_ENABLE_SPLIT; + enableCompress = INTERNATOM_ENABLE_COMPRESS; + + dataLimit = INTERNATOM_DATA_LIMIT; + dataOffset = INTERNATOM_DATA_OFFSET; + + cacheSlots = INTERNATOM_CACHE_SLOTS; + cacheThreshold = INTERNATOM_CACHE_THRESHOLD; + cacheLowerThreshold = INTERNATOM_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~InternAtomStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "InternAtom"; + } + + virtual unsigned char opcode() const + { + return X_InternAtom; + } + + virtual unsigned int storage() const + { + return sizeof(InternAtomMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new InternAtomMessage(); + } + + virtual Message *create(const Message &message) const + { + return new InternAtomMessage((const InternAtomMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (InternAtomMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* InternAtom_H */ diff --git a/nxcomp/Jpeg.cpp b/nxcomp/Jpeg.cpp new file mode 100644 index 000000000..b3973227c --- /dev/null +++ b/nxcomp/Jpeg.cpp @@ -0,0 +1,875 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <X11/Xmd.h> + +#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); + +// +// Colormap stuff. +// + +CARD16 srcRedMax, srcGreenMax, srcBlueMax; +CARD8 srcRedShift, srcGreenShift, srcBlueShift; + +// +// Error handler. +// + +static bool jpegError; + +jmp_buf UnpackJpegContext; + +void UnpackJpegErrorHandler(j_common_ptr cinfo) +{ + #ifdef PANIC + *logofs << "UnpackJpegErrorHandler: PANIC! Detected error in JPEG decompression.\n" + << logofs_flush; + + *logofs << "UnpackJpegErrorHandler: PANIC! Trying to revert to the previous context.\n" + << logofs_flush; + #endif + + jpegError = 1; + + longjmp(UnpackJpegContext, 1); +} + +// +// Attributes used for the jpeg decompression. +// + +static struct jpeg_source_mgr jpegSrcManager; +static JOCTET *jpegBufferPtr; +static size_t jpegBufferLen; + +static char *tmpBuf; +static int tmpBufSize = 0; + +int UnpackJpeg(T_geometry *geometry, unsigned char method, unsigned char *srcData, + int srcSize, int dstBpp, int dstWidth, int dstHeight, + unsigned char *dstData, int dstSize) +{ + int byteOrder = geometry -> image_byte_order; + + // + // Check if data is coming from a failed unsplit. + // + + if (srcSize < 2 || (srcData[0] == SPLIT_PATTERN && + srcData[1] == SPLIT_PATTERN)) + { + #ifdef WARNING + *logofs << "UnpackJpeg: WARNING! Skipping unpack of dummy data.\n" + << logofs_flush; + #endif + + return -1; + } + + #ifdef DEBUG + *logofs << "UnpackJpeg: Decompression. Source size " + << srcSize << " bits per plane " << dstBpp + << ".\n" << logofs_flush; + #endif + + srcRedShift = ffs(geometry -> red_mask) - 1; + srcGreenShift = ffs(geometry -> green_mask) - 1; + srcBlueShift = ffs(geometry -> blue_mask) - 1; + + #ifdef DEBUG + *logofs << "UnpackJpeg: Red shift " << (int) srcRedShift + << " green shift " << (int) srcGreenShift << " blue shift " + << (int) srcBlueShift << ".\n" << logofs_flush; + #endif + + srcRedMax = geometry -> red_mask >> srcRedShift; + srcGreenMax = geometry -> green_mask >> srcGreenShift; + srcBlueMax = geometry -> blue_mask >> srcBlueShift; + + #ifdef DEBUG + *logofs << "UnpackJpeg: Red mask " << (void *) geometry -> red_mask + << " green mask " << (void *) geometry -> green_mask + << " blue mask " << (void *) geometry -> blue_mask + << ".\n" << logofs_flush; + #endif + + #ifdef DEBUG + *logofs << "UnpackJpeg: Red max " << srcRedMax << " green max " + << srcGreenMax << " blue max " << srcBlueMax + << ".\n" << logofs_flush; + #endif + + // + // Make enough space in the temporary + // buffer to have one complete row of + // the image with 3 bytes for a pixel. + // + + tmpBufSize = dstWidth * 3; + tmpBuf = new char[tmpBufSize]; + + if (tmpBuf == NULL) + { + #ifdef PANIC + *logofs << "UnpackJpeg: PANIC! Cannot allocate " + << dstWidth * 3 << " bytes for Jpeg " + << "decompressed data.\n" << logofs_flush; + #endif + + delete [] tmpBuf; + + return -1; + } + + int result = 1; + + switch(dstBpp) + { + case 8: + { + // + // Simply move the data from srcData to dstData + // taking into consideration the correct padding. + // + + int row; + + unsigned char * dstBuff = dstData; + unsigned char * srcBuff = srcData; + + for (row = 0; row < dstHeight; row++) + { + memcpy(dstBuff, srcBuff, dstWidth); + + dstBuff += RoundUp4(dstWidth); + srcBuff += dstWidth; + } + + break; + } + case 16: + { + result = DecompressJpeg16(srcData, srcSize, dstWidth, + dstHeight, dstData, byteOrder); + break; + } + case 24: + { + result = DecompressJpeg24(srcData, srcSize, dstWidth, + dstHeight, dstData, byteOrder); + break; + } + case 32: + { + result = DecompressJpeg32(srcData, srcSize, dstWidth, + dstHeight, dstData, byteOrder); + break; + } + default: + { + #ifdef PANIC + *logofs << "UnpackJpeg: PANIC! Failed to decode Jpeg image. " + << " Unsupported Bpp value " << dstBpp + << " for the Jpeg compression" + << ".\n" << logofs_flush; + #endif + + delete [] tmpBuf; + + result = -1; + } + } + + #ifdef DEBUG + *logofs << "UnpackJpeg: Decompression finished with result " + << result << ".\n" << logofs_flush; + #endif + + if (result == -1) + { + delete [] tmpBuf; + + #ifdef PANIC + *logofs << "UnpackJpeg: PANIC! Failed to decode Jpeg image using " + << dstBpp << " Bpp destination.\n" << logofs_flush; + #endif + + return result; + } + + // + // Apply the correction for the brightness. + // + + int maskMethod; + + switch(method) + { + case PACK_JPEG_8_COLORS: + { + maskMethod = MASK_8_COLORS; + + break; + } + case PACK_JPEG_64_COLORS: + { + maskMethod = MASK_64_COLORS; + + break; + } + case PACK_JPEG_256_COLORS: + { + maskMethod = MASK_256_COLORS; + + break; + } + case PACK_JPEG_512_COLORS: + { + maskMethod = MASK_512_COLORS; + + break; + } + case PACK_JPEG_4K_COLORS: + { + maskMethod = MASK_4K_COLORS; + + break; + } + case PACK_JPEG_32K_COLORS: + { + maskMethod = MASK_32K_COLORS; + + break; + } + case PACK_JPEG_64K_COLORS: + { + maskMethod = MASK_64K_COLORS; + + break; + } + case PACK_JPEG_256K_COLORS: + { + maskMethod = MASK_256K_COLORS; + + break; + } + case PACK_JPEG_2M_COLORS: + { + maskMethod = MASK_2M_COLORS; + + break; + } + case PACK_JPEG_16M_COLORS: + { + maskMethod = MASK_16M_COLORS; + + break; + } + default: + { + delete [] tmpBuf; + + return -1; + } + } + + const T_colormask *colorMask = MethodColorMask(maskMethod); + + unsigned char *dstBuff = dstData; + + switch (dstBpp) + { + case 16: + { + Unpack16To16(colorMask, dstBuff, dstBuff, dstBuff + dstSize); + + break; + } + case 24: + { + break; + } + case 32: + { + Unpack32To32(colorMask, (unsigned int *) dstBuff, (unsigned int *) dstBuff, + (unsigned int *) (dstBuff + dstSize)); + break; + } + default: + { + delete [] tmpBuf; + + return -1; + } + } + + delete [] tmpBuf; + + return 1; +} + +// +// Functions that actually do the Jpeg decompression. +// + +int DecompressJpeg16(unsigned char *compressedData, int compressedLen, + unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + unsigned char *data; + JSAMPROW rowPointer[1]; + + unsigned int dx = 0; + unsigned int dy = 0; + + #ifdef DEBUG + *logofs << "DecompressJpeg16: Decompressing with length " + << compressedLen << " width " << w << " height " + << h << ".\n" << logofs_flush; + #endif + + jpegError = 0; + + cinfo.err = jpeg_std_error(&jerr); + + jerr.error_exit = UnpackJpegErrorHandler; + + if (setjmp(UnpackJpegContext) == 1) + { + #ifdef TEST + *logofs << "DecompressJpeg16: Out of the long jump with error '" + << jpegError << "'.\n" << logofs_flush; + #endif + + goto AbortDecompressJpeg16; + } + + jpeg_create_decompress(&cinfo); + + if (jpegError) goto AbortDecompressJpeg16; + + JpegSetSrcManager(&cinfo, compressedData, compressedLen); + + jpeg_read_header(&cinfo, 1); + + if (jpegError) goto AbortDecompressJpeg16; + + cinfo.out_color_space = JCS_RGB; + + jpeg_start_decompress(&cinfo); + + if (jpegError) goto AbortDecompressJpeg16; + + if (cinfo.output_width != w || + cinfo.output_height != h || + cinfo.output_components != 3) + { + #ifdef PANIC + *logofs << "DecompressJpeg16: PANIC! Wrong JPEG data received.\n" + << logofs_flush; + #endif + + jpeg_destroy_decompress(&cinfo); + + return -1; + } + + // + // PixelPtr points to dstBuf which is + // already padded correctly for the final + // image to put + // + + data = dstBuf; + + rowPointer[0] = (JSAMPROW) tmpBuf; + + unsigned long pixel; + + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, rowPointer, 1); + + if (jpegError) goto AbortDecompressJpeg16; + + for (dx = 0; dx < w; dx++) + { + pixel = RGB24_TO_PIXEL(16, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1], + tmpBuf[dx * 3 + 2]); + + // + // Follow the server byte order when arranging data. + // + + if (byteOrder == LSBFirst) + { + data[0] = (unsigned char) (pixel & 0xff); + data[1] = (unsigned char) ((pixel >> 8) & 0xff); + } + else + { + data[1] = (unsigned char) (pixel & 0xff); + data[0] = (unsigned char) ((pixel >> 8) & 0xff); + } + + data += 2; + } + + // + // Move data at the beginning of the + // next line. + // + + data = data + (RoundUp4(w * 2) - w * 2); + + dy++; + } + + AbortDecompressJpeg16: + + if (jpegError == 0) + { + jpeg_finish_decompress(&cinfo); + } + + jpeg_destroy_decompress(&cinfo); + + if (jpegError == 1) + { + #ifdef PANIC + *logofs << "DecompressJpeg16: Failed to decompress JPEG image.\n" + << logofs_flush; + #endif + + return -1; + } + + #ifdef TEST + *logofs << "DecompressJpeg16: Decompression finished with " + << dy << " lines handled.\n" << logofs_flush; + #endif + + return 1; +} + +int DecompressJpeg24(unsigned char *compressedData, int compressedLen, + unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + CARD8 *pixelPtr = NULL; + JSAMPROW rowPointer[1]; + + unsigned int dx = 0; + unsigned int dy = 0; + + #ifdef TEST + *logofs << "DecompressJpeg24: Decompressing with length " + << compressedLen << " width " << w << " height " + << h << ".\n" << logofs_flush; + #endif + + jpegError = 0; + + cinfo.err = jpeg_std_error(&jerr); + + jerr.error_exit = UnpackJpegErrorHandler; + + if (setjmp(UnpackJpegContext) == 1) + { + #ifdef TEST + *logofs << "DecompressJpeg24: Out of the long jump with error '" + << jpegError << "'.\n" << logofs_flush; + #endif + + goto AbortDecompressJpeg24; + } + + jpeg_create_decompress(&cinfo); + + if (jpegError) goto AbortDecompressJpeg24; + + JpegSetSrcManager(&cinfo, compressedData, compressedLen); + + jpeg_read_header(&cinfo, 1); + + if (jpegError) goto AbortDecompressJpeg24; + + cinfo.out_color_space = JCS_RGB; + + jpeg_start_decompress(&cinfo); + + if (jpegError) goto AbortDecompressJpeg24; + + if (cinfo.output_width != w || + cinfo.output_height != h || + cinfo.output_components != 3) + { + #ifdef PANIC + *logofs << "DecompressJpeg24: PANIC! Wrong JPEG data received.\n" + << logofs_flush; + #endif + + jpeg_destroy_decompress(&cinfo); + + return -1; + } + + // + // PixelPtr points to dstBuf which is + // already padded correctly for the final + // image to put. + // + + pixelPtr = (CARD8 *) dstBuf; + + rowPointer[0] = (JSAMPROW) tmpBuf; + + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, rowPointer, 1); + + if (jpegError) goto AbortDecompressJpeg24; + + for (dx = 0; dx < w; dx++) + { + // + // Follow the server byte order when arranging data. + // + + if (byteOrder == LSBFirst) + { + pixelPtr[0] = tmpBuf[dx * 3]; + pixelPtr[1] = tmpBuf[dx * 3 + 1]; + pixelPtr[2] = tmpBuf[dx * 3 + 2]; + } + else + { + pixelPtr[2] = tmpBuf[dx * 3]; + pixelPtr[1] = tmpBuf[dx * 3 + 1]; + pixelPtr[0] = tmpBuf[dx * 3 + 2]; + } + + pixelPtr += 3; + } + + // + // Go to the next line. + // + + pixelPtr = (CARD8 *) (((char *) pixelPtr) + (RoundUp4(w * 3) - w * 3)); + + dy++; + } + + AbortDecompressJpeg24: + + if (jpegError == 0) + { + jpeg_finish_decompress(&cinfo); + } + + jpeg_destroy_decompress(&cinfo); + + if (jpegError == 1) + { + #ifdef PANIC + *logofs << "DecompressJpeg24: Failed to decompress JPEG image.\n" + << logofs_flush; + #endif + + return -1; + } + + #ifdef TEST + *logofs << "DecompressJpeg24: Decompression finished with " + << dy << " lines handled.\n" << logofs_flush; + #endif + + return 1; +} + +int DecompressJpeg32(unsigned char *compressedData, int compressedLen, + unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + unsigned char *data; + JSAMPROW rowPointer[1]; + + unsigned int dx = 0; + unsigned int dy = 0; + + #ifdef TEST + *logofs << "DecompressJpeg32: Decompressing with length " + << compressedLen << " width " << w << " height " + << h << ".\n" << logofs_flush; + #endif + + jpegError = 0; + + cinfo.err = jpeg_std_error(&jerr); + + jerr.error_exit = UnpackJpegErrorHandler; + + if (setjmp(UnpackJpegContext) == 1) + { + #ifdef TEST + *logofs << "DecompressJpeg32: Out of the long jump with error '" + << jpegError << "'.\n" << logofs_flush; + #endif + + goto AbortDecompressJpeg32; + } + + jpeg_create_decompress(&cinfo); + + if (jpegError) goto AbortDecompressJpeg32; + + JpegSetSrcManager(&cinfo, compressedData, compressedLen); + + jpeg_read_header(&cinfo, 1); + + if (jpegError) goto AbortDecompressJpeg32; + + cinfo.out_color_space = JCS_RGB; + + jpeg_start_decompress(&cinfo); + + if (jpegError) goto AbortDecompressJpeg32; + + if (cinfo.output_width != w || + cinfo.output_height != h || + cinfo.output_components != 3) + { + #ifdef PANIC + *logofs << "DecompressJpeg32 : PANIC! Wrong JPEG data received.\n" + << logofs_flush; + #endif + + jpeg_destroy_decompress(&cinfo); + + return -1; + } + + // + // PixelPtr points to dstBuf which is + // already padded correctly for the final + // image to put + // + + data = dstBuf; + + rowPointer[0] = (JSAMPROW) tmpBuf; + + unsigned long pixel; + + int i; + + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, rowPointer, 1); + + if (jpegError) goto AbortDecompressJpeg32; + + for (dx = 0; dx < w; dx++) + { + pixel = RGB24_TO_PIXEL(32, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1], + tmpBuf[dx * 3 + 2]); + + // + // Follow the server byte order when arranging data. + // + + if (byteOrder == LSBFirst) + { + for (i = 0; i < 4; i++) + { + data[i] = (unsigned char)(pixel & 0xff); + pixel >>= 8; + } + } + else + { + for (i = 3; i >= 0; i--) + { + data[i] = (unsigned char) (pixel & 0xff); + pixel >>= 8; + } + } + + data += 4; + } + + dy++; + } + + AbortDecompressJpeg32: + + if (jpegError == 0) + { + jpeg_finish_decompress(&cinfo); + } + + jpeg_destroy_decompress(&cinfo); + + if (jpegError == 1) + { + #ifdef PANIC + *logofs << "DecompressJpeg32: Failed to decompress JPEG image.\n" + << logofs_flush; + #endif + + return -1; + } + + #ifdef TEST + *logofs << "DecompressJpeg32: Decompression finished with " + << dy << " lines handled.\n" << logofs_flush; + #endif + + return 1; +} + +static void JpegInitSource(j_decompress_ptr cinfo) +{ + jpegError = 0; +} + +static boolean JpegFillInputBuffer(j_decompress_ptr cinfo) +{ + jpegError = 1; + + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + + return 1; +} + +static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes) +{ + if (num_bytes < 0 || (unsigned long) num_bytes > jpegSrcManager.bytes_in_buffer) + { + jpegError = 1; + + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + } + else + { + jpegSrcManager.next_input_byte += (size_t) num_bytes; + jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void JpegTermSource(j_decompress_ptr cinfo) +{ +} + +static void JpegSetSrcManager(j_decompress_ptr cinfo, + CARD8 *compressedData, + int compressedLen) +{ + jpegBufferPtr = (JOCTET *) compressedData; + jpegBufferLen = (size_t) compressedLen; + + jpegSrcManager.init_source = JpegInitSource; + jpegSrcManager.fill_input_buffer = JpegFillInputBuffer; + jpegSrcManager.skip_input_data = JpegSkipInputData; + jpegSrcManager.resync_to_restart = jpeg_resync_to_restart; + jpegSrcManager.term_source = JpegTermSource; + jpegSrcManager.next_input_byte = jpegBufferPtr; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + + cinfo->src = &jpegSrcManager; +} diff --git a/nxcomp/Jpeg.h b/nxcomp/Jpeg.h new file mode 100644 index 000000000..f3743d07a --- /dev/null +++ b/nxcomp/Jpeg.h @@ -0,0 +1,28 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Jpeg_H +#define Jpeg_H + +#include "Misc.h" +#include "Unpack.h" + +int UnpackJpeg(T_geometry *geometry, unsigned char method, unsigned char *srcData, + int srcSize, int dstBpp, int dstWidth, int dstHeight, + unsigned char *dstData, int dstSize); + +#endif /* Jpeg_H */ diff --git a/nxcomp/Keeper.cpp b/nxcomp/Keeper.cpp new file mode 100644 index 000000000..fd9b79f45 --- /dev/null +++ b/nxcomp/Keeper.cpp @@ -0,0 +1,600 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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/Keeper.h b/nxcomp/Keeper.h new file mode 100644 index 000000000..c357b2116 --- /dev/null +++ b/nxcomp/Keeper.h @@ -0,0 +1,191 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Keeper_H +#define Keeper_H + +#include "Misc.h" +#include "Types.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Define this to check how many file +// nodes are allocated and deallocated. +// + +#undef REFERENCES + +class Keeper; + +class File +{ + friend class Keeper; + + public: + + File(); + + ~File(); + + // + // Allow sort by time and size. If time + // is the same, keep the bigger element. + // + + bool compare(File *b) const; + + private: + + char *name_; + + int size_; + time_t time_; + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +class Keeper +{ + public: + + Keeper(int caches, int images, const char *root, + int sleep, int parent); + + ~Keeper(); + + // + // Call this just once. + // + + int cleanupCaches(); + + // + // Call this at any given interval. + // + + int cleanupImages(); + + // + // Call this if it's time to exit. + // + + void setSignal(int signal) + { + signal_ = signal; + } + + int getSignal() + { + return signal_; + } + + int getParent() + { + return parent_; + } + + private: + + // + // Get a list of files in directory. + // + + int collect(const char *path); + + // + // Sort the collected files according to + // last modification time and delete the + // older ones until disk size is below + // the threshold. + // + + int cleanup(int threshold); + + // + // Empty the files repository. + // + + void empty(); + + // + // Size in bytes of total allowed + // storage for persistent caches. + // + + int caches_; + + // + // Size in bytes of total allowed + // storage for images cache. + // + + int images_; + + // + // Path of the NX root directory. + // + + char *root_; + + // + // The little delay to be introduced + // before reading a new entry. + // + + int sleep_; + + // + // Total size of files in repository. + // + + int total_; + + // + // The parent process, so we can exit + // if it is gone. + // + + int parent_; + + // + // Set if we need to give up because + // of a signal. + // + + int signal_; + + // + // Repository where to collect files. + // + + T_files *files_; +}; + +#endif /* Keeper_H */ + diff --git a/nxcomp/LICENSE b/nxcomp/LICENSE new file mode 100644 index 000000000..2b3203474 --- /dev/null +++ b/nxcomp/LICENSE @@ -0,0 +1,37 @@ +Copyright (c) 2001, 2010 NoMachine - http://www.nomachine.com/. + +NXCOMP library and NX extensions to X are copyright of NoMachine. +Redistribution and use of this software is allowed according to the +following terms: + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License Version 2, and +not any other version, as published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTA- +BILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, you can request a copy to NoMachine +or write to the Free Software Foundation, Inc., 59 Temple Place, Suite +330, Boston, MA 02111-1307 USA + +Parts of this software are derived from DXPC project. These copyright +notices apply to original DXPC code: + +Redistribution and use in source and binary forms are permitted provi- +ded that the above copyright notice and this paragraph are duplicated +in all such forms. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +Copyright (c) 1995,1996 Brian Pane +Copyright (c) 1996,1997 Zachary Vonler and Brian Pane +Copyright (c) 1999 Kevin Vigor and Brian Pane +Copyright (c) 2000,2006 Gian Filippo Pinzari and Brian Pane + +All rights reserved. diff --git a/nxcomp/List.cpp b/nxcomp/List.cpp new file mode 100644 index 000000000..1ba104b59 --- /dev/null +++ b/nxcomp/List.cpp @@ -0,0 +1,100 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "List.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Define this to know how many instances +// are allocated and deallocated. +// + +#undef REFERENCES + +#ifdef REFERENCES + +int List::references_ = 0; + +#endif + +List::List() +{ + #ifdef REFERENCES + + references_++; + + *logofs << "List: Created new List at " + << this << " out of " << references_ + << " allocated references.\n" << logofs_flush; + #endif +} + +List::~List() +{ + #ifdef REFERENCES + + references_--; + + *logofs << "List: Deleted List at " + << this << " out of " << references_ + << " allocated references.\n" << logofs_flush; + #endif +} + +void List::remove(int value) +{ + for (T_list::iterator i = list_.begin(); i != list_.end(); i++) + { + if (*i == value) + { + list_.erase(i); + + return; + } + } + + #ifdef PANIC + *logofs << "List: PANIC! Should not try to remove " + << "an element not found in the list.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Should not try to remove " + << "an element not found in the list.\n"; + + HandleAbort(); +} + +void List::rotate() +{ + if (list_.size() > 1) + { + int value = *(list_.begin()); + + list_.pop_front(); + + list_.push_back(value); + } +} diff --git a/nxcomp/List.h b/nxcomp/List.h new file mode 100644 index 000000000..b5e41ae11 --- /dev/null +++ b/nxcomp/List.h @@ -0,0 +1,87 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef List_H +#define List_H + +#include "Misc.h" +#include "Types.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Define this to log when lists are +// allocated and deallocated. +// + +#undef REFERENCES + +class List +{ + public: + + List(); + + ~List(); + + int getSize() + { + return list_.size(); + } + + T_list &getList() + { + return list_; + } + + T_list copyList() + { + return list_; + } + + void add(int value) + { + list_.push_back(value); + } + + void remove(int value); + + void rotate(); + + private: + + // + // The list container. + // + + T_list list_; + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +#endif /* List_H */ diff --git a/nxcomp/ListFontsReply.cpp b/nxcomp/ListFontsReply.cpp new file mode 100644 index 000000000..5bace82f2 --- /dev/null +++ b/nxcomp/ListFontsReply.cpp @@ -0,0 +1,204 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ListFontsReply.h" + +#include "ServerCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef DUMP +#undef TEST +#undef DEBUG + +ListFontsReplyStore::ListFontsReplyStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = LISTFONTSREPLY_ENABLE_CACHE; + enableData = LISTFONTSREPLY_ENABLE_DATA; + enableSplit = LISTFONTSREPLY_ENABLE_SPLIT; + enableCompress = LISTFONTSREPLY_ENABLE_COMPRESS; + + if (control -> isProtoStep7() == 1) + { + enableCompress = LISTFONTSREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7; + } + + dataLimit = LISTFONTSREPLY_DATA_LIMIT; + dataOffset = LISTFONTSREPLY_DATA_OFFSET; + + cacheSlots = LISTFONTSREPLY_CACHE_SLOTS; + cacheThreshold = LISTFONTSREPLY_CACHE_THRESHOLD; + cacheLowerThreshold = LISTFONTSREPLY_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +ListFontsReplyStore::~ListFontsReplyStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int ListFontsReplyStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message; + + // + // Here is the fingerprint. + // + + listFontsReply -> number_of_names = GetUINT(buffer + 8, bigEndian); + + // + // Clean up padding bytes. + // + + if ((int) size > dataOffset) + { + unsigned int current; + unsigned int length; + unsigned int nstringInNames; + + unsigned char *end = NULL; + unsigned char *pad = NULL; + + #ifdef DUMP + + *logofs << "\n" << logofs_flush; + + *logofs << "Number of STRING8 " << listFontsReply -> number_of_names << ".\n" << logofs_flush; + + *logofs << "Size " << size << ".\n" << logofs_flush; + + DumpHexData(buffer, size); + + *logofs << "\n" << logofs_flush; + + #endif + + length = LISTFONTSREPLY_DATA_OFFSET; + + for (nstringInNames = 0; + nstringInNames < listFontsReply -> number_of_names && + listFontsReply -> number_of_names > 0; + nstringInNames++) + { + // + // Start with offset LISTFONTSREPLY_DATA_OFFSET 32. + // + + current = buffer[length]; + + length += current + 1; + + #ifdef DUMP + *logofs << "\nString number : " << nstringInNames << " Current length : " + << current << "\n" << logofs_flush; + #endif + } + + #ifdef DUMP + *logofs << "\nFinal length " << length << "\n" << logofs_flush; + #endif + + end = ((unsigned char *) buffer) + size; + + for (pad = ((unsigned char *) buffer) + length; pad < end; pad++) + { + *pad = 0; + + #ifdef DUMP + *logofs << "\nPadding ." << "\n" << logofs_flush; + #endif + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ListFontsReplyStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message; + + // + // Fill all the message's fields. + // + + PutUINT(listFontsReply -> number_of_names, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void ListFontsReplyStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message; + + *logofs << name() << ": Identity number_of_names " + << listFontsReply -> number_of_names << ", size " + << listFontsReply -> size_ << ".\n"; + + #endif +} + +void ListFontsReplyStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // Field number_of_names. + // + + md5_append(md5_state_, buffer + 8, 2); +} diff --git a/nxcomp/ListFontsReply.h b/nxcomp/ListFontsReply.h new file mode 100644 index 000000000..078fd7ebc --- /dev/null +++ b/nxcomp/ListFontsReply.h @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ListFontsReply_H +#define ListFontsReply_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define LISTFONTSREPLY_ENABLE_CACHE 1 +#define LISTFONTSREPLY_ENABLE_DATA 1 +#define LISTFONTSREPLY_ENABLE_SPLIT 0 +#define LISTFONTSREPLY_ENABLE_COMPRESS 1 + +#define LISTFONTSREPLY_DATA_LIMIT 1048576 - 32 +#define LISTFONTSREPLY_DATA_OFFSET 32 + +#define LISTFONTSREPLY_CACHE_SLOTS 200 +#define LISTFONTSREPLY_CACHE_THRESHOLD 20 +#define LISTFONTSREPLY_CACHE_LOWER_THRESHOLD 5 + +#define LISTFONTSREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 + +// +// The message class. +// + +class ListFontsReplyMessage : public Message +{ + friend class ListFontsReplyStore; + + public: + + ListFontsReplyMessage() + { + } + + ~ListFontsReplyMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned short int number_of_names; +}; + +class ListFontsReplyStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + ListFontsReplyStore(StaticCompressor *compressor); + + virtual ~ListFontsReplyStore(); + + virtual const char *name() const + { + return "ListFontsReply"; + } + + virtual unsigned char opcode() const + { + return X_ListFonts; + } + + virtual unsigned int storage() const + { + return sizeof(ListFontsReplyMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new ListFontsReplyMessage(); + } + + virtual Message *create(const Message &message) const + { + return new ListFontsReplyMessage((const ListFontsReplyMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (ListFontsReplyMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* ListFontsReply_H */ diff --git a/nxcomp/Loop.cpp b/nxcomp/Loop.cpp new file mode 100644 index 000000000..92b6fc28f --- /dev/null +++ b/nxcomp/Loop.cpp @@ -0,0 +1,16683 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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 <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> +#include <sys/un.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" + +// +// System specific defines. +// + +#if defined(__EMX__) || defined(__CYGWIN32__) + +struct sockaddr_un +{ + u_short sun_family; + char sun_path[108]; +}; + +#endif + +// +// HP-UX hides this define. +// + +#if defined(hpux) && !defined(RLIM_INFINITY) + +#define RLIM_INFINITY 0x7fffffff + +#endif + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Enable log output in signal handler. +// This is likely to hang the proxy at +// random, at least on Linux. +// + +#undef UNSAFE + +// +// Let all logs go to the standard error. +// This is useful to interleave the Xlib +// log output with the proxy output in a +// single file. +// + +#undef MIXED + +// +// Define this to check if the client and +// server caches match at shutdown. This +// is a test facility as it requires that +// both proxies are running on the same +// host. +// + +#undef MATCH + +// +// If defined, reduce the size of the log +// file and be sure it never exceeds the +// limit. +// + +#undef QUOTA + +// +// If defined, force very strict limits for +// the proxy tokens and force the proxy to +// enter often in congestion state. +// + +#undef STRICT + +// +// Print a line in the log if the time we +// spent inside the select or handling the +// messages exceeded a given time value. +// + +#undef TIME + +// +// This can be useful when testing the forwarding +// of the SSHD connection by nxssh to the agent. +// The debug output will go to a well known file +// that will be opened also by nxssh when BINDER +// is enabled there. +// + +#undef BINDER + +// +// Define this to override the limits on +// the core dump size. +// + +#define COREDUMPS + +// +// Upper limit of pre-allocated buffers +// for string parameters. +// + +#define DEFAULT_STRING_LENGTH 256 + +// +// Maximum length of remote options data +// passed by peer proxy at startup. +// + +#define DEFAULT_REMOTE_OPTIONS_LENGTH 512 + +// +// Maximum length of NX display string. +// + +#define DEFAULT_DISPLAY_OPTIONS_LENGTH 1024 + +// +// Maximum number of cache file names to +// send to the server side. +// + +#define DEFAULT_REMOTE_CACHE_ENTRIES 100 + +// +// Maximum length of remote options string +// that can be received from the peer proxy. +// + +#define MAXIMUM_REMOTE_OPTIONS_LENGTH 4096 + +// +// Macro is true if we determined our proxy +// mode. +// + +#define WE_SET_PROXY_MODE (control -> ProxyMode != proxy_undefined) + +// +// Macro is true if our side is the one that +// should connect to remote. +// + +#define WE_INITIATE_CONNECTION (*connectHost != '\0') + +// +// Is true if we must provide our credentials +// to the remote peer. +// + +#define WE_PROVIDE_CREDENTIALS (control -> ProxyMode == proxy_server) + +// +// Is true if we listen for a local forwarder +// that will tunnel the traffic through a SSH +// or HTTP link. +// + +#define WE_LISTEN_FORWARDER (control -> ProxyMode == proxy_server && \ + listenPort != -1) + +// +// You must define FLUSH in Misc.h if +// you want an immediate flush of the +// log output. +// + +ostream *logofs = NULL; + +// +// Other stream destriptors used for +// logging. +// + +ostream *statofs = NULL; +ostream *errofs = NULL; + +// +// Save standard error's rdbuf here +// and restore it when exiting. +// + +static streambuf *errsbuf = NULL; + +// +// Allow faults to be recovered by +// jumping back into the main loop. +// + +jmp_buf context; + +// +// Provide operational parameters. +// + +Control *control = NULL; + +// +// Collect and print statistics. +// + +Statistics *statistics = NULL; + +// +// Keep data for X11 authentication. +// + +Auth *auth = NULL; + +// +// This class makes the hard work. +// + +Proxy *proxy = NULL; + +// +// Used to handle memory-to-memory +// transport to the X agent. +// + +Agent *agent = NULL; + +// +// The image cache house-keeping class. +// + +Keeper *keeper = NULL; + +// +// Callback set by the child process +// to be notified about signals. +// + +int (*handler)(int) = NULL; + +// +// Signal handling functions. +// + +void DisableSignals(); +void EnableSignals(); +void InstallSignals(); + +static void RestoreSignals(); +static void HandleSignal(int signal); + +// +// Signal handling utilities. +// + +static void InstallSignal(int signal, int action); +static void RestoreSignal(int signal); + +static int HandleChildren(); + +static int HandleChild(int child); +static int CheckChild(int pid, int status); +static int WaitChild(int child, const char *label, int force); + +int CheckParent(const char *name, const char *type, int parent); + +void RegisterChild(int child); + +static int CheckAbort(); + +// +// Timer handling utilities. +// + +void SetTimer(int timeout); +void ResetTimer(); + +static void HandleTimer(int signal); + +// +// Kill or check a running child. +// + +static int KillProcess(int pid, const char *label, int signal, int wait); +static int CheckProcess(int pid, const char *label); + +// +// Macros used to test the pid of a child. +// + +#define IsFailed(pid) ((pid) < 0) +#define IsRunning(pid) ((pid) > 1) +#define IsNotRunning(pid) ((pid) == 0) +#define IsRestarting(pid) ((pid) == 1) + +#define SetNotRunning(pid) ((pid) = 0) +#define SetRestarting(pid) ((pid) = 1) + +// +// Start or restart the house-keeper process. +// + +static int StartKeeper(); + +// +// Cleanup functions. +// + +void CleanupConnections(); +void CleanupListeners(); +void CleanupSockets(); +void CleanupGlobal(); + +static void CleanupChildren(); +static void CleanupLocal(); +static void CleanupKeeper(); +static void CleanupStreams(); + +// +// Loop forever until the connections +// to the peer proxy is dropped. +// + +static void WaitCleanup(); + +// +// Initialization functions. +// + +static int InitBeforeNegotiation(); +static int SetupProxyConnection(); +static int InitAfterNegotiation(); +static int SetupProxyInstance(); +static int SetupAuthInstance(); +static int SetupAgentInstance(); + +static int SetupTcpSocket(); +static int SetupUnixSocket(); +static int SetupServiceSockets(); +static int SetupDisplaySocket(int &xServerAddrFamily, sockaddr *&xServerAddr, + unsigned int &xServerAddrLength); + +// +// Setup a listening socket and accept +// a new connection. +// + +static int ListenConnection(int port, const char *label); +static int AcceptConnection(int fd, int domain, const char *label); + +// +// Other convenience functions. +// + +static int WaitForRemote(int portNum); +static int ConnectToRemote(const char *const hostName, int portNum); + +static int SendProxyOptions(int fd); +static int SendProxyCaches(int fd); +static int ReadProxyVersion(int fd); +static int ReadProxyOptions(int fd); +static int ReadProxyCaches(int fd); +static int ReadForwarderVersion(int fd); +static int ReadForwarderOptions(int fd); + +static int ReadRemoteData(int fd, char *buffer, int size, char stop); +static int WriteLocalData(int fd, const char *buffer, int size); + +static void PrintVersionInfo(); +static void PrintProcessInfo(); +static void PrintConnectionInfo(); +static void PrintUsageInfo(const char *option, const int error); +static void PrintOptionIgnored(const char *type, const char *name, const char *value); + +// +// This is not static to avoid a warning. +// + +void PrintCopyrightInfo(); + +static const char *GetOptions(const char *options); +static const char *GetArg(int &argi, int argc, const char **argv); +static int CheckArg(const char *type, const char *name, const char *value); +static int ParseArg(const char *type, const char *name, const char *value); +static int ValidateArg(const char *type, const char *name, const char *value); +static int LowercaseArg(const char *type, const char *name, char *value); +static int CheckSignal(int signal); + +extern "C" +{ + int ParseCommandLineOptions(int argc, const char **argv); + int ParseEnvironmentOptions(const char *env, int force); + int ParseBindOptions(char **host, int *port); +} + +static int ParseFileOptions(const char *file); +static int ParseRemoteOptions(char *opts); +static int ParseForwarderOptions(char *opts); + +// +// These functions are used to parse literal +// values provided by the user and set the +// control parameters accordingly. +// + +static int ParseLinkOption(const char *opt); +static int ParseBitrateOption(const char *opt); +static int ParseCacheOption(const char *opt); +static int ParseShmemOption(const char *opt); +static int ParseImagesOption(const char *opt); +static int ParsePackOption(const char *opt); + +// +// Set host and port where NX proxy is supposed +// to be listening in case such parameters are +// given on the command line. +// + +static int ParseHostOption(const char *opt, char *host, int &port); + +// +// Translate a font server port specification +// to the corresponding Unix socket path. +// + +static int ParseFontPath(char *path); + +// +// Determine the interface where to listen for +// the remote proxy connection or the local +// forwarder. +// + +static int ParseListenOption(int &interface); + +// +// Translate a pack method id in a literal. +// + +static int ParsePackMethod(const int method, const int quality); + +// +// Try to increase the size of the allowed +// core dumps. +// + +static int SetCore(); + +// +// Set the proxy mode to either client or +// server. +// + +static int SetMode(int mode); + +// +// Determine the path of the NX_* directories +// from the environment. +// + +static int SetDirectories(); + +// +// Set the defaults used for the log file and +// statistics. +// + +static int SetLogs(); + +// +// Check if local and remote protocol versions +// are compatible and, eventually, downgrade +// local version to the minimum level that is +// known to work. +// + +static int SetVersion(); + +// +// Setup the listening TCP ports used for the +// additional channels according to user's +// wishes. +// + +static int SetPorts(); + +// +// Set the maximum number of open descriptors. +// + +static int SetDescriptors(); + +// +// Set the path used for choosing the cache. +// It must be selected after determining the +// session type. +// + +static int SetCaches(); + +// +// Initialize, one after the other, all the +// configuration parameters. +// + +static int SetParameters(); + +// +// Set the specific configuration parameter. +// + +static int SetSession(); +static int SetStorage(); +static int SetShmem(); +static int SetPack(); +static int SetImages(); +static int SetLimits(); + +// +// Set up the control parameters based on +// the link speed negotiated between the +// proxies. +// + +static int SetLink(); + +static int SetLinkModem(); +static int SetLinkIsdn(); +static int SetLinkAdsl(); +static int SetLinkWan(); +static int SetLinkLan(); + +// +// Adjust the compression parameters. +// + +static int SetCompression(); + +static int SetCompressionModem(); +static int SetCompressionIsdn(); +static int SetCompressionAdsl(); +static int SetCompressionWan(); +static int SetCompressionLan(); + +// +// Determine the NX paths based on the +// user's parameters or the environment. +// + +char *GetClientPath(); + +static char *GetSystemPath(); +static char *GetHomePath(); +static char *GetTempPath(); +static char *GetRootPath(); +static char *GetCachePath(); +static char *GetImagesPath(); +static char *GetSessionPath(); +static char *GetLastCache(char *list, const char *path); + +static int OpenLogFile(char *name, ostream *&stream); +static int ReopenLogFile(char *name, ostream *&stream, int limit); + +// +// Perform operations on the managed +// descriptors in the main loop. +// + +static void handleCheckSessionInLoop(); +static void handleCheckBitrateInLoop(); + +#if defined(TEST) || defined(INFO) +static void handleCheckSelectInLoop(int &setFDs, fd_set &readSet, + fd_set &writeSet, T_timestamp selectTs); +static void handleCheckResultInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, + fd_set &writeSet, struct timeval &selectTs, + struct timeval &startTs); +static void handleCheckStateInLoop(int &setFDs); +#endif + +static void handleCheckSessionInConnect(); + +static inline void handleSetReadInLoop(fd_set &readSet, int &setFDs, struct timeval &selectTs); +static inline void handleSetWriteInLoop(fd_set &writeSet, int &setFDs, struct timeval &selectTs); +static inline void handleSetListenersInLoop(fd_set &writeSet, int &setFDs); +static inline void handleSetAgentInLoop(int &setFDs, fd_set &readSet, fd_set &writeSet, + struct timeval &selectTs); + +static void handleAlertInLoop(); +static void handleStatisticsInLoop(); + +static inline void handleAgentInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, + fd_set &writeSet, struct timeval &selectTs); +static inline void handleAgentLateInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, + fd_set &writeSet, struct timeval &selectTs); + +static inline void handleReadableInLoop(int &resultFDs, fd_set &readSet); +static inline void handleWritableInLoop(int &resultFDs, fd_set &writeSet); + +static inline void handleRotateInLoop(); +static inline void handleEventsInLoop(); +static inline void handleFlushInLoop(); + +// +// Manage the proxy link during the negotiation +// phase. +// + +static void handleNegotiationInLoop(int &setFDs, fd_set &readSet, + fd_set &writeSet, T_timestamp &selectTs); + +// +// Print the 'terminating' messages in the +// session log. +// + +static inline void handleTerminatingInLoop(); +static inline void handleTerminatedInLoop(); + +// +// Monitor the size of the log file. +// + +static void handleLogReopenInLoop(T_timestamp &logsTs, T_timestamp &nowTs); + +// +// Directory where the NX binaries and libraries reside. +// + +static char systemDir[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Directory used for temporary files. +// + +static char tempDir[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Actually the full path to the client. +// + +static char clientDir[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// User's home directory. +// + +static char homeDir[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Root of directory structure to be created by proxy. +// + +static char rootDir[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Root of statistics and log files to be created by proxy. +// + +static char sessionDir[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Log files for errors and statistics. Error log is +// the place where we print also debug informations. +// Both files are closed, deleted and reopened as +// their size exceed the limit set in control class. +// The session log is not reopened, as it is used by +// the NX client and server to track the advance of +// the session. +// + +static char errorsFileName[DEFAULT_STRING_LENGTH] = { 0 }; +static char statsFileName[DEFAULT_STRING_LENGTH] = { 0 }; +static char sessionFileName[DEFAULT_STRING_LENGTH] = { 0 }; +static char optionsFileName[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// String literal representing selected link speed +// parameter. Value is translated in control values +// used by proxies to stay synchronized. +// + +static char linkSpeedName[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// String literal representing selected +// cache size. +// + +static char cacheSizeName[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// String literal representing selected +// shared memory segment size. +// + +static char shsegSizeName[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// String literal of images cache size. +// + +static char imagesSizeName[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// String literal for bandwidth limit. +// + +static char bitrateLimitName[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// String literal for image packing method. +// + +static char packMethodName[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Product name provided by the server or client. +// + +static char productName[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Its corresponding value from NXpack.h. +// + +static int packMethod = -1; +static int packQuality = -1; + +// +// String literal for session type. Persistent caches +// are searched in directory whose name matches this +// parameter. +// + +static char sessionType[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Unique id assigned to session. It is used as +// name of directory where all files are placed. +// + +static char sessionId[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Set if we already parsed the options. +// + +static int parsedOptions = 0; +static int parsedCommand = 0; + +// +// Buffer data received from the remote proxy at +// session negotiation. +// + +static char remoteData[MAXIMUM_REMOTE_OPTIONS_LENGTH] = { 0 }; +static int remotePosition = 0; + +// +// Main loop file descriptors. +// + +static int tcpFD = -1; +static int unixFD = -1; +static int cupsFD = -1; +static int auxFD = -1; +static int smbFD = -1; +static int mediaFD = -1; +static int httpFD = -1; +static int fontFD = -1; +static int slaveFD = -1; +static int proxyFD = -1; + +// +// Used for internal communication +// with the X agent. +// + +static int agentFD[2] = { -1, -1 }; + +// +// Flags determining which protocols and +// ports are forwarded. +// + +int useUnixSocket = 1; + +static int useTcpSocket = 1; +static int useCupsSocket = 0; +static int useAuxSocket = 0; +static int useSmbSocket = 0; +static int useMediaSocket = 0; +static int useHttpSocket = 0; +static int useFontSocket = 0; +static int useSlaveSocket = 0; +static int useAgentSocket = 0; + +// +// Set if the launchd service is running +// and its socket must be used as X socket. +// + +static int useLaunchdSocket = 0; + +// +// Set by user if he/she wants to modify +// the default TCP_NODELAY option as set +// in control. +// + +static int useNoDelay = -1; + +// +// Set if user wants to override default +// flush timeout set according to link. +// + +static int usePolicy = -1; + +// +// Set if user wants to hide the RENDER +// extension or wants to short-circuit +// some simple replies at client side. +// + +static int useRender = -1; +static int useTaint = -1; + +// +// Set if the user wants to reduce the +// nominal size of the token messages +// exchanged between the proxies. +// + +static int useStrict = -1; + +// +// Set if the proxy is running as part +// of SSH on the client. +// + +static int useEncryption = -1; + +// +// Name of Unix socket created by the client proxy to +// accept client connections. File must be unlinked +// by cleanup function. +// + +static char unixSocketName[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Other parameters. +// + +static char connectHost[DEFAULT_STRING_LENGTH] = { 0 }; +static char acceptHost[DEFAULT_STRING_LENGTH] = { 0 }; +static char listenHost[DEFAULT_STRING_LENGTH] = { 0 }; +static char displayHost[DEFAULT_STRING_LENGTH] = { 0 }; +static char authCookie[DEFAULT_STRING_LENGTH] = { 0 }; + +static int proxyPort = DEFAULT_NX_PROXY_PORT; +static int xPort = DEFAULT_NX_X_PORT; + +// +// Used to setup the connection the real +// X display socket. +// + +static int xServerAddrFamily = -1; +static sockaddr *xServerAddr = NULL; +static unsigned int xServerAddrLength = 0; + +// +// The port where the local proxy will await +// the peer connection or where the remote +// proxy will be contacted. +// + +static int listenPort = -1; +static int connectPort = -1; + +// +// Helper channels are disabled by default. +// + +static int cupsPort = -1; +static int auxPort = -1; +static int smbPort = -1; +static int mediaPort = -1; +static int httpPort = -1; +static int slavePort = -1; + +// +// Can be either a port number or a Unix +// socket. +// + +static char fontPort[DEFAULT_STRING_LENGTH] = { 0 }; + +// +// Host and port where the existing proxy +// is running. +// + +static char bindHost[DEFAULT_STRING_LENGTH] = { 0 }; +static int bindPort = -1; + +// +// Pointers to the callback functions and +// parameter set by the agent +// + +static void (*flushCallback)(void *, int) = NULL; +static void *flushParameter = NULL; + +static void (*statisticsCallback)(void *, int) = NULL; +static void *statisticsParameter = NULL; + +// +// State variables shared between the init +// function and the main loop. +// + +T_timestamp initTs; +T_timestamp startTs; +T_timestamp logsTs; +T_timestamp nowTs; + +int diffTs; + +// +// This is set to the main proxy process id. +// + +int lastProxy = 0; + +// +// Set to last dialog process launched by proxy. +// + +int lastDialog = 0; + +// +// Set to watchdog process launched by proxy. +// + +int lastWatchdog = 0; + +// +// Set if a cache house-keeper process is running. +// + +int lastKeeper = 0; + +// +// Let an inner routine register the pid of a slave +// process. +// + +static int lastChild = 0; + +// +// Exit code of the last child process exited. +// + +static int lastStatus = 0; + +// +// Set if shutdown was requested through a signal. +// + +static int lastKill = 0; + +// +// Set if the agent confirmed the destruction of +// the NX transport. +// + +static int lastDestroy = 0; + +// +// This is set to the code and local flag of the +// last requested alert. +// + +static struct +{ + int code; + int local; + +} lastAlert; + +// +// Manage the current signal masks. +// + +static struct +{ + sigset_t saved; + + int blocked; + int installed; + + int enabled[32]; + int forward[32]; + + struct sigaction action[32]; + +} lastMasks; + +// +// Manage the current timer. +// + +static struct +{ + struct sigaction action; + struct itimerval value; + struct timeval start; + struct timeval next; + +} lastTimer; + +// +// This is set to last signal received in handler. +// + +static int lastSignal = 0; + +// +// Set to the last time bytes readable were queried +// by the agent. +// + +static T_timestamp lastReadableTs = nullTimestamp(); + +// +// Here are interfaces declared in NX.h. +// + +int NXTransProxy(int fd, int mode, const char* options) +{ + // + // Let the log temporarily go to the standard + // error. Be also sure we have a jump context, + // in the case any subsequent operation will + // cause a cleanup. + // + + if (logofs == NULL) + { + logofs = &cerr; + } + + if (setjmp(context) == 1) + { + #ifdef TEST + *logofs << "NXTransProxy: Out of the long jump with pid '" + << lastProxy << "'.\n" << logofs_flush; + #endif + + return -1; + } + + // + // Check if have already performed a parsing of + // parameters, as in the case we are running as + // a stand-alone process. If needed create the + // parameters repository + // + + if (control == NULL) + { + control = new Control(); + } + + lastProxy = getpid(); + + #ifdef TEST + *logofs << "NXTransProxy: Main process started with pid '" + << lastProxy << "'.\n" << logofs_flush; + #endif + + SetMode(mode); + + if (mode == NX_MODE_CLIENT) + { + if (fd != NX_FD_ANY) + { + #ifdef TEST + + *logofs << "NXTransProxy: Agent descriptor for X client connections is FD#" + << fd << ".\n" << logofs_flush; + + *logofs << "NXTransProxy: Disabling listening on further X client connections.\n" + << logofs_flush; + + #endif + + useTcpSocket = 0; + useUnixSocket = 0; + useAgentSocket = 1; + + agentFD[1] = fd; + } + } + else if (mode == NX_MODE_SERVER) + { + if (fd != NX_FD_ANY) + { + #ifdef TEST + *logofs << "NXTransProxy: PANIC! Agent descriptor for X server connections " + << "not supported yet.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Agent descriptor for X server connections " + << "not supported yet.\n"; + + return -1; + } + } + + const char *env = GetOptions(options); + + if (ParseEnvironmentOptions(env, 0) < 0) + { + cerr << "Error" << ": Parsing of NX transport options failed.\n"; + + return -1; + } + + // + // Set the path of the NX directories. + // + + SetDirectories(); + + // + // Open the log files. + // + + SetLogs(); + + #ifdef TEST + *logofs << "NXTransProxy: Going to run the NX transport loop.\n" + << logofs_flush; + #endif + + WaitCleanup(); + + // + // This function should never return. + // + + exit(0); +} + +void NXTransExit(int code) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + static int recurse; + + if (++recurse > 1) + { + #ifdef TEST + *logofs << "NXTransExit: Aborting process with pid '" + << getpid() << "' due to recursion through " + << "exit.\n" << logofs_flush; + #endif + + abort(); + } + + #ifdef TEST + *logofs << "NXTransExit: Process with pid '" + << getpid() << "' called exit with code '" + << code << "'.\n" << logofs_flush; + #endif + + if (control != NULL) + { + // + // Be sure that there we can detect the + // termination of the watchdog. + // + + EnableSignals(); + + // + // Close the NX transport if it was not + // shut down already. + // + + NXTransDestroy(NX_FD_ANY); + } + + exit(code); +} + +int NXTransParseCommandLine(int argc, const char **argv) +{ + return ParseCommandLineOptions(argc, argv); +} + +int NXTransParseEnvironment(const char *env, int force) +{ + return ParseEnvironmentOptions(env, force); +} + +void NXTransCleanup() +{ + HandleCleanup(); +} + +// +// Check the parameters for subsequent +// initialization of the NX transport. +// + +int NXTransCreate(int fd, int mode, const char* options) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + // + // Be sure we have a jump context, in the + // case a subsequent operation will cause + // a cleanup. + // + + if (setjmp(context) == 1) + { + return -1; + } + + // + // Create the parameters repository + // + + if (control != NULL) + { + #ifdef PANIC + *logofs << "NXTransCreate: PANIC! The NX transport seems " + << "to be already running.\n" << logofs_flush; + #endif + + cerr << "Error" << ": The NX transport seems " + << "to be already running.\n"; + + return -1; + } + + control = new Control(); + + if (control == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error creating the NX transport.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error creating the NX transport.\n"; + + return -1; + } + + lastProxy = getpid(); + + #ifdef TEST + *logofs << "NXTransCreate: Caller process running with pid '" + << lastProxy << "'.\n" << logofs_flush; + #endif + + // + // Set the local proxy mode an parse the + // display NX options. + // + + SetMode(mode); + + const char *env = GetOptions(options); + + if (ParseEnvironmentOptions(env, 0) < 0) + { + cerr << "Error" << ": Parsing of NX transport options failed.\n"; + + return -1; + } + + // + // Set the path of the NX directories. + // + + SetDirectories(); + + // + // Open the log files. + // + + SetLogs(); + + // + // Use the provided descriptor. + // + + proxyFD = fd; + + #ifdef TEST + *logofs << "NXTransCreate: Called with NX proxy descriptor '" + << proxyFD << "'.\n" << logofs_flush; + #endif + + #ifdef TEST + *logofs << "NXTransCreate: Creation of the NX transport completed.\n" + << logofs_flush; + #endif + + return 1; +} + +// +// Tell the proxy to use the descriptor as the internal +// connection to the X client side NX agent. This will +// have the side effect of disabling listening for add- +// itional X client connections. +// + +int NXTransAgent(int fd[2]) +{ + // + // Be sure we have a jump context, in the + // case a subsequent operation will cause + // a cleanup. + // + + if (logofs == NULL) + { + logofs = &cerr; + } + + if (setjmp(context) == 1) + { + return -1; + } + + if (control == NULL) + { + cerr << "Error" << ": Can't set the NX agent without a NX transport.\n"; + + return -1; + } + else if (control -> ProxyMode != proxy_client) + { + #ifdef PANIC + *logofs << "NXTransAgent: Invalid mode while setting the NX agent.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid mode while setting the NX agent.\n\n"; + + return -1; + } + + useTcpSocket = 0; + useUnixSocket = 0; + useAgentSocket = 1; + + agentFD[0] = fd[0]; + agentFD[1] = fd[1]; + + #ifdef TEST + + *logofs << "NXTransAgent: Internal descriptors for agent are FD#" + << agentFD[0] << " and FD#" << agentFD[1] << ".\n" + << logofs_flush; + + *logofs << "NXTransAgent: Disabling listening for further X client " + << "connections.\n" << logofs_flush; + + #endif + + agent = new Agent(agentFD); + + if (agent == NULL || agent -> isValid() != 1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error creating the NX memory transport .\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error creating the NX memory transport.\n"; + + HandleCleanup(); + } + + #ifdef TEST + *logofs << "NXTransAgent: Enabling memory-to-memory transport.\n" + << logofs_flush; + #endif + + return 1; +} + +int NXTransClose(int fd) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + /* + * Only handle the proxy connection. The X + * transport will take care of closing its + * end of the socket pair. + */ + + if (control != NULL && ((agent != NULL && + (fd == agentFD[0] || fd == NX_FD_ANY)) || + (fd == proxyFD || fd == NX_FD_ANY))) + { + if (proxy != NULL) + { + #ifdef TEST + *logofs << "NXTransClose: Closing down all the X connections.\n" + << logofs_flush; + #endif + + CleanupConnections(); + } + } + #ifdef TEST + else + { + *logofs << "NXTransClose: The NX transport is not running.\n" + << logofs_flush; + } + #endif + + return 1; +} + +// +// Close down the transport and free the +// allocated NX resources. +// + +int NXTransDestroy(int fd) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + if (control != NULL && ((agent != NULL && + (fd == agentFD[0] || fd == NX_FD_ANY)) || + (fd == proxyFD || fd == NX_FD_ANY))) + { + // + // Shut down the X connections and + // wait the cleanup to complete. + // + + if (proxy != NULL) + { + #ifdef TEST + *logofs << "NXTransDestroy: Closing down all the X connections.\n" + << logofs_flush; + #endif + + CleanupConnections(); + } + + #ifdef TEST + *logofs << "NXTransDestroy: Waiting for the NX transport to terminate.\n" + << logofs_flush; + #endif + + lastDestroy = 1; + + WaitCleanup(); + } + #ifdef TEST + else + { + *logofs << "NXTransDestroy: The NX transport is not running.\n" + << logofs_flush; + } + #endif + + return 1; +} + +// +// Assume that the NX transport is valid +// as long as the control class has not +// been destroyed. +// + +int NXTransRunning(int fd) +{ + return (control != NULL); +} + +int NXTransContinue(struct timeval *selectTs) +{ + if (control != NULL) + { + // + // If no timeout is provided use + // the default. + // + + T_timestamp newTs; + + if (selectTs == NULL) + { + setTimestamp(newTs, control -> PingTimeout); + + selectTs = &newTs; + } + + // + // Use empty masks and only get the + // descriptors set by the proxy. + // + + fd_set readSet; + fd_set writeSet; + + int setFDs; + int errorFDs; + int resultFDs; + + setFDs = 0; + + FD_ZERO(&readSet); + FD_ZERO(&writeSet); + + // + // Run a new loop. If the transport + // is gone avoid sleeping until the + // timeout. + // + + if (NXTransPrepare(&setFDs, &readSet, &writeSet, selectTs) != 0) + { + NXTransSelect(&resultFDs, &errorFDs, &setFDs, &readSet, &writeSet, selectTs); + + NXTransExecute(&resultFDs, &errorFDs, &setFDs, &readSet, &writeSet, selectTs); + } + } + + return (control != NULL); +} + +int NXTransSignal(int signal, int action) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + if (control == NULL) + { + return 0; + } + + if (action == NX_SIGNAL_RAISE) + { + #ifdef TEST + *logofs << "NXTransSignal: Raising signal '" << DumpSignal(signal) + << "' in the proxy handler.\n" << logofs_flush; + #endif + + HandleSignal(signal); + + return 1; + } + else if (signal == NX_SIGNAL_ANY) + { + #ifdef TEST + *logofs << "NXTransSignal: Setting action of all signals to '" + << action << "'.\n" << logofs_flush; + #endif + + for (int i = 0; i < 32; i++) + { + if (CheckSignal(i) == 1) + { + NXTransSignal(i, action); + } + } + + return 1; + } + else if (CheckSignal(signal) == 1) + { + #ifdef TEST + *logofs << "NXTransSignal: Setting action of signal '" + << DumpSignal(signal) << "' to '" << action + << "'.\n" << logofs_flush; + #endif + + if (action == NX_SIGNAL_ENABLE || + action == NX_SIGNAL_FORWARD) + { + InstallSignal(signal, action); + + return 1; + } + else if (action == NX_SIGNAL_DISABLE) + { + RestoreSignal(signal); + + return 1; + } + } + + #ifdef WARNING + *logofs << "NXTransSignal: WARNING! Unable to perform action '" + << action << "' on signal '" << DumpSignal(signal) + << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Unable to perform action '" << action + << "' on signal '" << DumpSignal(signal) + << "'.\n"; + + return -1; +} + +int NXTransCongestion(int fd) +{ + if (control != NULL && proxy != NULL) + { + #ifdef DUMP + + int congestion = proxy -> getCongestion(proxyFD); + + *logofs << "NXTransCongestion: Returning " << congestion + << " as current congestion level.\n" << logofs_flush; + + return congestion; + + #endif + + return (proxy -> getCongestion(proxyFD)); + } + + return 0; +} + +int NXTransHandler(int fd, int type, void (*handler)(void *parameter, + int reason), void *parameter) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + switch (type) + { + case NX_HANDLER_FLUSH: + { + flushCallback = handler; + flushParameter = parameter; + + break; + } + case NX_HANDLER_STATISTICS: + { + // + // Reporting of statistics by the agent + // still needs to be implemented. + // + + statisticsCallback = handler; + statisticsParameter = parameter; + + break; + } + default: + { + #ifdef TEST + *logofs << "NXTransHandler: WARNING! Failed to set " + << "the NX callback for event '" << type << "' to '" + << (void *) handler << "' and parameter '" + << parameter << "'.\n" << logofs_flush; + #endif + + return 0; + } + } + + #ifdef TEST + *logofs << "NXTransHandler: Set the NX " + << "callback for event '" << type << "' to '" + << (void *) handler << "' and parameter '" + << parameter << "'.\n" << logofs_flush; + #endif + + return 1; +} + +int NXTransRead(int fd, char *data, int size) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + if (control != NULL && agent != NULL && + fd == agentFD[0]) + { + #ifdef DUMP + *logofs << "NXTransRead: Dequeuing " << size << " bytes " + << "from FD#" << agentFD[0] << ".\n" << logofs_flush; + #endif + + int result = agent -> dequeueData(data, size); + + #ifdef DUMP + + if (result < 0 && EGET() == EAGAIN) + { + *logofs << "NXTransRead: WARNING! Dequeuing from FD#" + << agentFD[0] << " would block.\n" << logofs_flush; + } + else + { + *logofs << "NXTransRead: Dequeued " << result << " bytes " + << "to FD#" << agentFD[0] << ".\n" << logofs_flush; + } + + #endif + + return result; + } + else + { + #ifdef DUMP + *logofs << "NXTransRead: Reading " << size << " bytes " + << "from FD#" << fd << ".\n" << logofs_flush; + #endif + + return read(fd, data, size); + } +} + +int NXTransReadVector(int fd, struct iovec *iovdata, int iovsize) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + if (control != NULL && agent != NULL && + fd == agentFD[0]) + { + #if defined(DUMP) + + if (control -> ProxyStage >= stage_operational && + agent -> localReadable() > 0) + { + *logofs << "NXTransReadVector: WARNING! Agent has data readable.\n" + << logofs_flush; + } + + #endif + + char *base; + + int length; + int result; + + struct iovec *vector = iovdata; + int count = iovsize; + + ESET(0); + + int i = 0; + int total = 0; + + for (; i < count; i++, vector++) + { + length = vector -> iov_len; + base = (char *) vector -> iov_base; + + while (length > 0) + { + #ifdef DUMP + *logofs << "NXTransReadVector: Dequeuing " << length + << " bytes " << "from FD#" << agentFD[0] << ".\n" + << logofs_flush; + #endif + + result = agent -> dequeueData(base, length); + + #ifdef DUMP + + if (result < 0 && EGET() == EAGAIN) + { + *logofs << "NXTransReadVector: WARNING! Dequeuing from FD#" + << agentFD[0] << " would block.\n" << logofs_flush; + } + else + { + *logofs << "NXTransReadVector: Dequeued " << result + << " bytes " << "from FD#" << agentFD[0] << ".\n" + << logofs_flush; + } + + #endif + + if (result < 0 && total == 0) + { + return result; + } + else if (result <= 0) + { + return total; + } + + ESET(0); + + length -= result; + total += result; + base += result; + } + } + + return total; + } + else + { + #ifdef DUMP + *logofs << "NXTransReadVector: Reading vector with " + << iovsize << " elements from FD#" << fd << ".\n" + << logofs_flush; + #endif + + return readv(fd, iovdata, iovsize); + } +} + +int NXTransReadable(int fd, int *readable) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + if (control == NULL || agent == NULL || + fd != agentFD[0]) + { + #ifdef DUMP + + int result = GetBytesReadable(fd, readable); + + if (result == -1) + { + *logofs << "NXTransReadable: Error detected on FD#" + << fd << ".\n" << logofs_flush; + } + else + { + *logofs << "NXTransReadable: Returning " << *readable + << " bytes as readable from FD#" << fd + << ".\n" << logofs_flush; + } + + return result; + + #else + + return GetBytesReadable(fd, readable); + + #endif + } + + int result = agent -> dequeuableData(); + + switch (result) + { + case 0: + { + // + // The client might have enqueued data to our side + // and is now checking for the available events. As + // _XEventsQueued() may omit to call _XSelect(), we + // handle here the new data that is coming from the + // proxy to avoid spinning through this function + // again. + // + + if (proxy != NULL && proxy -> canRead() == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "NXTransReadable: WARNING! Trying to " + << "read to generate new agent data.\n" + << logofs_flush; + #endif + + // + // Set the context as the function + // can cause a cleanup. + // + + if (setjmp(context) == 1) + { + return -1; + } + + if (proxy -> handleRead() < 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "NXTransReadable: Failure reading " + << "messages from proxy FD#" << proxyFD + << ".\n" << logofs_flush; + #endif + + HandleShutdown(); + } + + // + // Call again the routine. By reading + // new control messages from the proxy + // the agent channel may be gone. + // + + return NXTransReadable(fd, readable); + } + + #ifdef DUMP + *logofs << "NXTransReadable: Returning " << 0 + << " bytes as readable from FD#" << fd + << " with result 0.\n" << logofs_flush; + #endif + + *readable = 0; + + return 0; + } + case -1: + { + #ifdef DUMP + *logofs << "NXTransReadable: Returning " << 0 + << " bytes as readable from FD#" << fd + << " with result -1.\n" << logofs_flush; + #endif + + *readable = 0; + + return -1; + } + default: + { + #ifdef DUMP + *logofs << "NXTransReadable: Returning " << result + << " bytes as readable from FD#" << fd + << " with result 0.\n" << logofs_flush; + #endif + + *readable = result; + + return 0; + } + } +} + +int NXTransWrite(int fd, char *data, int size) +{ + // + // Be sure we have a valid log file. + // + + if (logofs == NULL) + { + logofs = &cerr; + } + + if (control != NULL && agent != NULL && + fd == agentFD[0]) + { + int result; + + if (proxy != NULL) + { + if (proxy -> canRead(agentFD[1]) == 0) + { + #if defined(DUMP) || defined(TEST) + *logofs << "NXTransWrite: WARNING! Delayed enqueuing to FD#" + << agentFD[0] << " with proxy unable to read.\n" + << logofs_flush; + #endif + + ESET(EAGAIN); + + return -1; + } + + // + // Set the context as the function + // can cause a cleanup. + // + + if (setjmp(context) == 1) + { + return -1; + } + + // + // Don't enqueue the data to the transport + // but let the channel borrow the buffer. + // + + #ifdef DUMP + *logofs << "NXTransWrite: Letting the channel borrow " + << size << " bytes from FD#" << agentFD[0] + << ".\n" << logofs_flush; + #endif + + result = proxy -> handleRead(agentFD[1], data, size); + + if (result == 1) + { + result = size; + } + else + { + if (result == 0) + { + ESET(EAGAIN); + } + else + { + ESET(EPIPE); + } + + result = -1; + } + } + else + { + // + // We don't have a proxy connection, yet. + // Enqueue the data to the agent transport. + // + + #ifdef DUMP + *logofs << "NXTransWrite: Enqueuing " << size << " bytes " + << "to FD#" << agentFD[0] << ".\n" << logofs_flush; + #endif + + result = agent -> enqueueData(data, size); + } + + #ifdef DUMP + + if (result < 0) + { + if (EGET() == EAGAIN) + { + *logofs << "NXTransWrite: WARNING! Enqueuing to FD#" + << agentFD[0] << " would block.\n" + << logofs_flush; + } + else + { + *logofs << "NXTransWrite: WARNING! Error enqueuing to FD#" + << agentFD[0] << ".\n" << logofs_flush; + } + } + else + { + *logofs << "NXTransWrite: Enqueued " << result << " bytes " + << "to FD#" << agentFD[0] << ".\n" << logofs_flush; + } + + #endif + + return result; + } + else + { + #ifdef DUMP + *logofs << "NXTransWrite: Writing " << size << " bytes " + << "to FD#" << fd << ".\n" << logofs_flush; + #endif + + return write(fd, data, size); + } +} + +int NXTransWriteVector(int fd, struct iovec *iovdata, int iovsize) +{ + // + // Be sure we have a valid log file and a + // jump context because we will later call + // functions that can perform a cleanup. + // + + if (logofs == NULL) + { + logofs = &cerr; + } + + int result = 0; + + if (control != NULL && agent != NULL && + fd == agentFD[0]) + { + // + // See the comment in NXTransWrite(). + // + + if (proxy != NULL) + { + if (proxy -> canRead(agentFD[1]) == 0) + { + #if defined(DUMP) || defined(TEST) + *logofs << "NXTransWriteVector: WARNING! Delayed enqueuing to FD#" + << agentFD[0] << " with proxy unable to read.\n" + << logofs_flush; + #endif + + ESET(EAGAIN); + + return -1; + } + } + + // + // Set the context as the function + // can cause a cleanup. + // + + if (setjmp(context) == 1) + { + return -1; + } + + char *base; + + int length; + + struct iovec *vector = iovdata; + int count = iovsize; + + ESET(0); + + int i = 0; + int total = 0; + + for (; i < count; i++, vector++) + { + length = vector -> iov_len; + base = (char *) vector -> iov_base; + + while (length > 0) + { + if (proxy != NULL) + { + // + // Don't enqueue the data to the transport + // but let the channel borrow the buffer. + // + + #ifdef DUMP + *logofs << "NXTransWriteVector: Letting the channel borrow " + << length << " bytes from FD#" << agentFD[0] + << ".\n" << logofs_flush; + #endif + + result = proxy -> handleRead(agentFD[1], base, length); + + if (result == 1) + { + result = length; + } + else + { + if (result == 0) + { + ESET(EAGAIN); + } + else + { + ESET(EPIPE); + } + + result = -1; + } + } + else + { + // + // We don't have a proxy connection, yet. + // Enqueue the data to the agent transport. + // + + #ifdef DUMP + *logofs << "NXTransWriteVector: Enqueuing " << length + << " bytes " << "to FD#" << agentFD[0] << ".\n" + << logofs_flush; + #endif + + result = agent -> enqueueData(base, length); + } + + #ifdef DUMP + + if (result < 0) + { + if (EGET() == EAGAIN) + { + *logofs << "NXTransWriteVector: WARNING! Enqueuing to FD#" + << agentFD[0] << " would block.\n" + << logofs_flush; + } + else + { + *logofs << "NXTransWriteVector: WARNING! Error enqueuing to FD#" + << agentFD[0] << ".\n" << logofs_flush; + } + } + else + { + *logofs << "NXTransWriteVector: Enqueued " << result + << " bytes " << "to FD#" << agentFD[0] << ".\n" + << logofs_flush; + } + + #endif + + if (result < 0 && total == 0) + { + return result; + } + else if (result <= 0) + { + return total; + } + + ESET(0); + + length -= result; + total += result; + base += result; + } + } + + return total; + } + else + { + #ifdef DUMP + *logofs << "NXTransWriteVector: Writing vector with " + << iovsize << " elements to FD#" << fd << ".\n" + << logofs_flush; + #endif + + return writev(fd, iovdata, iovsize); + } +} + +int NXTransPolicy(int fd, int type) +{ + if (control != NULL) + { + if (usePolicy == -1) + { + #if defined(TEST) || defined(INFO) + *logofs << "NXTransPolicy: Setting flush policy on " + << "proxy FD#" << proxyFD << " to '" + << DumpPolicy(type == NX_POLICY_DEFERRED ? + policy_deferred : policy_immediate) + << "'.\n" << logofs_flush; + #endif + + control -> FlushPolicy = (type == NX_POLICY_DEFERRED ? + policy_deferred : policy_immediate); + + if (proxy != NULL) + { + proxy -> handleFlush(); + } + + return 1; + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "NXTransPolicy: Ignoring the agent " + << "setting with user policy set to '" + << DumpPolicy(control -> FlushPolicy) + << "'.\n" << logofs_flush; + #endif + + return 0; + } + } + + return 0; +} + +int NXTransFlushable(int fd) +{ + if (proxy == NULL || agent == NULL || + fd != agentFD[0]) + { + #ifdef DUMP + *logofs << "NXTransFlushable: Returning 0 bytes as " + << "flushable for unrecognized FD#" << fd + << ".\n" << logofs_flush; + #endif + + return 0; + } + else + { + #if defined(DUMP) || defined(INFO) + *logofs << "NXTransFlushable: Returning " << proxy -> + getFlushable(proxyFD) << " as bytes flushable on " + << "proxy FD#" << proxyFD << ".\n" + << logofs_flush; + #endif + + return proxy -> getFlushable(proxyFD); + } +} + +int NXTransFlush(int fd) +{ + if (proxy != NULL) + { + #if defined(TEST) || defined(INFO) + *logofs << "NXTransFlush: Requesting an immediate flush of " + << "proxy FD#" << proxyFD << ".\n" + << logofs_flush; + #endif + + return proxy -> handleFlush(); + } + + return 0; +} + +int NXTransChannel(int fd, int channelFd, int type) +{ + if (proxy != NULL) + { + // + // Set the context as the function + // can cause a cleanup. + // + + if (setjmp(context) == 1) + { + return -1; + } + + #if defined(TEST) || defined(INFO) + *logofs << "NXTransChannel: Going to create a new channel " + << "with type '" << type << "' on FD#" << channelFd + << ".\n" << logofs_flush; + #endif + + int result = -1; + + switch (type) + { + case NX_CHANNEL_X11: + { + if (useUnixSocket == 1 || useTcpSocket == 1 || + useAgentSocket == 1 || useAuxSocket == 1) + { + result = proxy -> handleNewConnection(channel_x11, channelFd); + } + + break; + } + case NX_CHANNEL_CUPS: + { + if (useCupsSocket == 1) + { + result = proxy -> handleNewConnection(channel_cups, channelFd); + } + + break; + } + case NX_CHANNEL_SMB: + { + if (useSmbSocket == 1) + { + result = proxy -> handleNewConnection(channel_smb, channelFd); + } + + break; + } + case NX_CHANNEL_MEDIA: + { + if (useMediaSocket == 1) + { + result = proxy -> handleNewConnection(channel_media, channelFd); + } + + break; + } + case NX_CHANNEL_HTTP: + { + if (useHttpSocket == 1) + { + result = proxy -> handleNewConnection(channel_http, channelFd); + } + + break; + } + case NX_CHANNEL_FONT: + { + if (useFontSocket == 1) + { + result = proxy -> handleNewConnection(channel_font, channelFd); + } + + break; + } + case NX_CHANNEL_SLAVE: + { + if (useSlaveSocket == 1) + { + result = proxy -> handleNewConnection(channel_slave, channelFd); + } + + break; + } + default: + { + #ifdef WARNING + *logofs << "NXTransChannel: WARNING! Unrecognized channel " + << "type '" << type << "'.\n" << logofs_flush; + #endif + + break; + } + } + + #ifdef WARNING + + if (result != 1) + { + *logofs << "NXTransChannel: WARNING! Could not create the " + << "new channel with type '" << type << "' on FD#" + << channelFd << ".\n" << logofs_flush; + } + + #endif + + return result; + } + + return 0; +} + +const char *NXTransFile(int type) +{ + char *name = NULL; + + switch (type) + { + case NX_FILE_SESSION: + { + name = sessionFileName; + + break; + } + case NX_FILE_ERRORS: + { + name = errorsFileName; + + break; + } + case NX_FILE_OPTIONS: + { + name = optionsFileName; + + break; + } + case NX_FILE_STATS: + { + name = statsFileName; + + break; + } + } + + if (name != NULL && *name != '\0') + { + return name; + } + + return NULL; +} + +long NXTransTime() +{ + static T_timestamp last = getTimestamp(); + + T_timestamp now = getTimestamp(); + + long diff = diffTimestamp(last, now); + + last = now; + + return diff; +} + +int NXTransAlert(int code, int local) +{ + if (proxy != NULL) + { + #if defined(DUMP) || defined(INFO) + *logofs << "NXTransAlert: Requesting a NX dialog with code " + << code << " and local " << local << ".\n" + << logofs_flush; + #endif + + if (local == 0) + { + // + // Set the context as the function + // can cause a cleanup. + // + + if (setjmp(context) == 1) + { + return -1; + } + + proxy -> handleAlert(code); + } + else + { + // + // Show the alert at the next loop. + // + + HandleAlert(code, local); + } + + return 1; + } + #if defined(DUMP) || defined(INFO) + else + { + if (logofs == NULL) + { + logofs = &cerr; + } + + *logofs << "NXTransAlert: Can't request an alert without " + << "a valid NX transport.\n" << logofs_flush; + } + #endif + + return 0; +} + +// +// Prepare the file sets and the timeout +// for a later execution of the select(). +// + +int NXTransPrepare(int *setFDs, fd_set *readSet, + fd_set *writeSet, struct timeval *selectTs) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + // + // Control is NULL if the NX transport was + // reset or was never created. If control + // is valid then prepare to jump back when + // the transport is destroyed. + // + + if (control == NULL || setjmp(context) == 1) + { + return 0; + } + + #if defined(TEST) || defined(INFO) + *logofs << "\nNXTransPrepare: Going to prepare the NX transport.\n" + << logofs_flush; + #endif + + if (control -> ProxyStage < stage_operational) + { + handleNegotiationInLoop(*setFDs, *readSet, *writeSet, *selectTs); + } + else + { + #if defined(TEST) || defined(INFO) + + if (isTimestamp(*selectTs) == 0) + { + *logofs << "Loop: WARNING! Preparing the select with requested " + << "timeout of " << selectTs -> tv_sec << " S and " + << (double) selectTs -> tv_usec / 1000 << " Ms.\n" + << logofs_flush; + } + else + { + *logofs << "Loop: Preparing the select with requested " + << "timeout of " << selectTs -> tv_sec << " S and " + << (double) selectTs -> tv_usec / 1000 << " Ms.\n" + << logofs_flush; + } + + #endif + + // + // Set descriptors of listening sockets. + // + + handleSetListenersInLoop(*readSet, *setFDs); + + // + // Set descriptors of both proxy and X + // connections. + // + + handleSetReadInLoop(*readSet, *setFDs, *selectTs); + + // + // Find out which file descriptors have + // data to write. + // + + handleSetWriteInLoop(*writeSet, *setFDs, *selectTs); + } + + // + // Prepare the masks for handling the memory- + // to-memory transport. This is required even + // during session negotiation. + // + + if (agent != NULL) + { + handleSetAgentInLoop(*setFDs, *readSet, *writeSet, *selectTs); + } + + // + // Register time spent handling messages. + // + + nowTs = getNewTimestamp(); + + diffTs = diffTimestamp(startTs, nowTs); + + #ifdef TEST + *logofs << "Loop: Mark - 0 - at " << strMsTimestamp() + << " with " << diffTs << " Ms elapsed.\n" + << logofs_flush; + #endif + + // + // TODO: Should add the read time in two + // parts otherwise the limits are checked + // before the counters are updated with + // time spent in the last loop. + // + + if (control -> ProxyStage >= stage_operational) + { + statistics -> addReadTime(diffTs); + } + + startTs = nowTs; + + #ifdef DEBUG + *logofs << "Loop: New timestamp is " << strMsTimestamp(startTs) + << ".\n" << logofs_flush; + #endif + + return 1; +} + +// +// Let's say that we call select() to find out +// if any of the handled descriptors has data, +// but actually things are a bit more complex +// than that. +// + +int NXTransSelect(int *resultFDs, int *errorFDs, int *setFDs, fd_set *readSet, + fd_set *writeSet, struct timeval *selectTs) +{ + #ifdef TIME + + static T_timestamp lastTs; + + #endif + + if (logofs == NULL) + { + logofs = &cerr; + } + + // + // Control is NULL if the NX transport was + // reset or never created. If control is + // valid then prepare for jumping back in + // the case of an error. + // + + if (control == NULL || setjmp(context) == 1) + { + *resultFDs = select(*setFDs, readSet, writeSet, NULL, selectTs); + + *errorFDs = errno; + + return 0; + } + + #if defined(TEST) || defined(INFO) + *logofs << "\nNXTransSelect: Going to select the NX descriptors.\n" + << logofs_flush; + #endif + + #if defined(TEST) || defined(INFO) + + handleCheckSelectInLoop(*setFDs, *readSet, *writeSet, *selectTs); + + #endif + + #ifdef TIME + + diffTs = diffTimestamp(lastTs, getNewTimestamp()); + + if (diffTs > 20) + { + *logofs << "Loop: TIME! Spent " << diffTs + << " Ms handling messages for proxy FD#" + << proxyFD << ".\n" << logofs_flush; + } + + lastTs = getNewTimestamp(); + + #endif + + #if defined(TEST) || defined(INFO) + + if (isTimestamp(*selectTs) == 0) + { + *logofs << "Loop: WARNING! Executing the select with requested " + << "timeout of " << selectTs -> tv_sec << " S and " + << (double) selectTs -> tv_usec / 1000 << " Ms.\n" + << logofs_flush; + } + else if (proxy != NULL && proxy -> getFlushable(proxyFD) > 0) + { + *logofs << "Loop: WARNING! Proxy FD#" << proxyFD + << " has " << proxy -> getFlushable(proxyFD) + << " bytes to write but timeout is " + << selectTs -> tv_sec << " S and " + << selectTs -> tv_usec / 1000 << " Ms.\n" + << logofs_flush; + } + + #endif + + // + // Wait for the selected sockets + // or the timeout. + // + + ESET(0); + + *resultFDs = select(*setFDs, readSet, writeSet, NULL, selectTs); + + *errorFDs = EGET(); + + #ifdef TIME + + diffTs = diffTimestamp(lastTs, getNewTimestamp()); + + if (diffTs > 100) + { + *logofs << "Loop: TIME! Spent " << diffTs + << " Ms waiting for new data for proxy FD#" + << proxyFD << ".\n" << logofs_flush; + } + + lastTs = getNewTimestamp(); + + #endif + + // + // Check the result of the select. + // + + #if defined(TEST) || defined(INFO) + + handleCheckResultInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs, startTs); + + #endif + + // + // Get time spent in select. The accouting is done + // in milliseconds. This is a real problem on fast + // machines where each loop is unlikely to take + // more than 500 us, so consider that the results + // can be inaccurate. + // + + nowTs = getNewTimestamp(); + + diffTs = diffTimestamp(startTs, nowTs); + + #ifdef TEST + *logofs << "Loop: Out of select after " << diffTs << " Ms " + << "at " << strMsTimestamp(nowTs) << " with result " + << *resultFDs << ".\n" << logofs_flush; + #endif + + startTs = nowTs; + + #ifdef DEBUG + *logofs << "Loop: New timestamp is " << strMsTimestamp(startTs) + << ".\n" << logofs_flush; + #endif + + if (control -> ProxyStage >= stage_operational) + { + statistics -> addIdleTime(diffTs); + } + + if (*resultFDs < 0) + { + // + // Check if the call was interrupted or if any of the + // managed descriptors has become invalid. This can + // happen to the X11 code, before the descriptor is + // removed from the managed set. + // + + #ifdef __sun + + if (*errorFDs == EINTR || *errorFDs == EBADF || + *errorFDs == EINVAL) + + #else + + if (*errorFDs == EINTR || *errorFDs == EBADF) + + #endif + + { + #ifdef TEST + + if (*errorFDs == EINTR) + { + *logofs << "Loop: Select failed due to EINTR error.\n" + << logofs_flush; + } + else + { + *logofs << "Loop: WARNING! Call to select failed. Error is " + << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + } + + #endif + } + else + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to select failed. Error is " + << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Call to select failed. Error is " + << EGET() << " '" << ESTR() << "'.\n"; + + HandleCleanup(); + } + } + + return 1; +} + +// +// Perform the required actions on all +// the descriptors having I/O pending. +// + +int NXTransExecute(int *resultFDs, int *errorFDs, int *setFDs, fd_set *readSet, + fd_set *writeSet, struct timeval *selectTs) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + // + // Control is NULL if the NX transport was + // reset or never created. If control is + // valid then prepare for jumping back in + // the case of an error. + // + + if (control == NULL || setjmp(context) == 1) + { + return 0; + } + + #if defined(TEST) || defined(INFO) + *logofs << "\nNXTransExecute: Going to execute I/O on the NX descriptors.\n" + << logofs_flush; + #endif + + if (control -> ProxyStage >= stage_operational) + { + // + // Check if I/O is possible on the proxy and + // local agent descriptors. + // + + if (agent != NULL) + { + handleAgentInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs); + } + + #ifdef TEST + *logofs << "Loop: Mark - 1 - at " << strMsTimestamp() + << " with " << diffTimestamp(startTs, getTimestamp()) + << " Ms elapsed.\n" << logofs_flush; + #endif + + // + // Rotate the channel that will be handled + // first. + // + + handleRotateInLoop(); + + // + // Flush any data on newly writable sockets. + // + + handleWritableInLoop(*resultFDs, *writeSet); + + #ifdef TEST + *logofs << "Loop: Mark - 2 - at " << strMsTimestamp() + << " with " << diffTimestamp(startTs, getTimestamp()) + << " Ms elapsed.\n" << logofs_flush; + #endif + + // + // Check if any socket has become readable. + // + + handleReadableInLoop(*resultFDs, *readSet); + + #ifdef TEST + *logofs << "Loop: Mark - 3 - at " << strMsTimestamp() + << " with " << diffTimestamp(startTs, getTimestamp()) + << " Ms elapsed.\n" << logofs_flush; + #endif + + // + // Handle the scheduled events on channels. + // + // - Restart, if possible, any client that was + // put to sleep. + // + // - Check if there are pointer motion events to + // flush. This applies only to X server side. + // + // - Check if any channel has exited the conges- + // tion state. + // + // - Check if there are images that are currently + // being streamed. + // + + handleEventsInLoop(); + + #ifdef TEST + *logofs << "Loop: Mark - 4 - at " << strMsTimestamp() + << " with " << diffTimestamp(startTs, getTimestamp()) + << " Ms elapsed.\n" << logofs_flush; + #endif + + // + // Check if user sent a signal to produce + // statistics. + // + + handleStatisticsInLoop(); + + // + // We may have flushed the proxy link or + // handled data coming from the remote. + // Post-process the masks and set the + // selected agent descriptors as ready. + // + + if (agent != NULL) + { + handleAgentLateInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs); + } + + #ifdef TEST + *logofs << "Loop: Mark - 5 - at " << strMsTimestamp() + << " with " << diffTimestamp(startTs, getTimestamp()) + << " Ms elapsed.\n" << logofs_flush; + #endif + + // + // Check if there is any data to flush. + // Agents should flush the proxy link + // explicitly. + // + + handleFlushInLoop(); + + #ifdef TEST + *logofs << "Loop: Mark - 6 - at " << strMsTimestamp() + << " with " << diffTimestamp(startTs, getTimestamp()) + << " Ms elapsed.\n" << logofs_flush; + #endif + } + + // + // Check if we have an alert to show. + // + + handleAlertInLoop(); + + if (control -> ProxyStage >= stage_operational) + { + // + // Check if it's time to give up. + // + + handleCheckSessionInLoop(); + + // + // Check if local proxy is consuming + // too many resources. + // + + handleCheckBitrateInLoop(); + + // + // Check coherency of internal state. + // + + #if defined(TEST) || defined(INFO) + + handleCheckStateInLoop(*setFDs); + + #endif + + #ifdef TEST + *logofs << "Loop: Mark - 7 - at " << strMsTimestamp() + << " with " << diffTimestamp(startTs, getTimestamp()) + << " Ms elapsed.\n" << logofs_flush; + #endif + } + + // + // Truncate the logs if needed. + // + + handleLogReopenInLoop(logsTs, nowTs); + + return 1; +} + +// +// Initialize the connection parameters and +// prepare for negotiating the link with the +// remote proxy. +// + +int InitBeforeNegotiation() +{ + // + // Disable limits on core dumps. + // + + SetCore(); + + // + // Install the signal handlers. + // + + InstallSignals(); + + // + // Track how much time we spent in initialization. + // + + nowTs = getNewTimestamp(); + + startTs = nowTs; + initTs = nowTs; + + #ifdef TEST + *logofs << "Loop: INIT! Taking mark for initialization at " + << strMsTimestamp(initTs) << ".\n" + << logofs_flush; + #endif + + // + // If not explicitly specified, determine if local + // mode is client or server according to parameters + // provided so far. + // + + if (WE_SET_PROXY_MODE == 0) + { + cerr << "Error" << ": Please specify either the -C or -S option.\n"; + + HandleCleanup(); + } + + // + // Start a watchdog. If initialization cannot + // be completed before timeout, then clean up + // everything and exit. + // + + if (control -> ProxyMode == proxy_client) + { + #ifdef TEST + *logofs << "Loop: Starting watchdog process with timeout of " + << control -> InitTimeout / 1000 << " seconds.\n" + << logofs_flush; + #endif + + lastWatchdog = NXTransWatchdog(control -> InitTimeout); + + if (IsFailed(lastWatchdog)) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't start the NX watchdog process.\n" + << logofs_flush; + #endif + + SetNotRunning(lastWatchdog); + } + #ifdef TEST + else + { + *logofs << "Loop: Watchdog started with pid '" + << lastWatchdog << "'.\n" << logofs_flush; + } + #endif + } + + // + // Print preliminary info. + // + + PrintProcessInfo(); + + // + // Set cups, multimedia and other + // auxiliary ports. + // + + SetPorts(); + + // + // Increase the number of maximum open + // file descriptors for this process. + // + + SetDescriptors(); + + // + // Set local endianess. + // + + unsigned int test = 1; + + setHostBigEndian(*((unsigned char *) (&test)) == 0); + + #ifdef TEST + *logofs << "Loop: Local host is " + << (hostBigEndian() ? "big endian" : "little endian") + << ".\n" << logofs_flush; + #endif + + if (control -> ProxyMode == proxy_client) + { + // + // Listen on sockets that mimic an X display to + // which X clients will be able to connect (e.g. + // unix:8 and/or localhost:8). + // + + if (useTcpSocket == 1) + { + SetupTcpSocket(); + } + + if (useUnixSocket == 1) + { + SetupUnixSocket(); + } + } + else + { + // + // Don't listen for X connections. + // + + useUnixSocket = 0; + useTcpSocket = 0; + useAgentSocket = 0; + + // + // Get ready to open the local display. + // + + SetupDisplaySocket(xServerAddrFamily, xServerAddr, xServerAddrLength); + } + + // + // If we are the NX server-side proxy we need to + // complete our initializazion. We will mandate + // our parameters at the time the NX client will + // connect. + // + + if (control -> ProxyMode == proxy_client) + { + SetParameters(); + } + + return 1; +} + +int SetupProxyConnection() +{ + if (proxyFD == -1) + { + if (WE_INITIATE_CONNECTION) + { + if (connectPort < 0) + { + connectPort = DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort; + } + + #ifdef TEST + *logofs << "Loop: Going to connect to " << connectHost + << ":" << connectPort << ".\n" << logofs_flush; + #endif + + proxyFD = ConnectToRemote(connectHost, connectPort); + + #ifdef TEST + *logofs << "Loop: Connected to remote proxy on FD#" + << proxyFD << ".\n" << logofs_flush; + #endif + + cerr << "Info" << ": Connection to remote proxy '" << connectHost + << ":" << connectPort << "' established.\n"; + } + else + { + if (listenPort < 0) + { + listenPort = DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort; + } + + #ifdef TEST + *logofs << "Loop: Going to wait for connection on port " + << listenPort << ".\n" << logofs_flush; + #endif + + proxyFD = WaitForRemote(listenPort); + + #ifdef TEST + + if (WE_LISTEN_FORWARDER) + { + *logofs << "Loop: Connected to remote forwarder on FD#" + << proxyFD << ".\n" << logofs_flush; + } + else + { + *logofs << "Loop: Connected to remote proxy on FD#" + << proxyFD << ".\n" << logofs_flush; + } + + #endif + } + } + #ifdef TEST + else + { + *logofs << "Loop: Using the inherited connection on FD#" + << proxyFD << ".\n" << logofs_flush; + } + #endif + + // + // Set TCP_NODELAY on proxy descriptor + // to reduce startup time. Option will + // later be disabled if needed. + // + + SetNoDelay(proxyFD, 1); + + // + // We need non-blocking input since the + // negotiation phase. + // + + SetNonBlocking(proxyFD, 1); + + return 1; +} + +// +// Create the required proxy and channel classes +// and get ready for handling the encoded traffic. +// + +int InitAfterNegotiation() +{ + #ifdef TEST + *logofs << "Loop: Connection with remote proxy completed.\n" + << logofs_flush; + #endif + + cerr << "Info" << ": Connection with remote proxy completed.\n" + << logofs_flush; + + // + // If we are the server proxy we completed + // our initializazion phase according to + // the values provided by the client side. + // + + if (control -> ProxyMode == proxy_server) + { + SetParameters(); + } + + // + // Set up the listeners for the additional + // services. + // + + SetupServiceSockets(); + + // + // Create the proxy class and the statistics + // repository and pass all the configuration + // data we negotiated with the remote peer. + // + + SetupProxyInstance(); + + // + // We completed both parsing of user's parameters + // and handlshaking with remote proxy. Now print + // a brief summary including the most significant + // control values. + // + + PrintConnectionInfo(); + + // + // Cancel the initialization watchdog. + // + + if (IsRunning(lastWatchdog)) + { + KillProcess(lastWatchdog, "watchdog", SIGTERM, 1); + + SetNotRunning(lastWatchdog); + + lastSignal = 0; + } + + // + // Start the house-keeper process. It will + // remove the oldest persistent caches, if + // the amount of storage exceed the limits + // set by the user. + // + + StartKeeper(); + + // + // Set the log size check timestamp. + // + + nowTs = getNewTimestamp(); + + logsTs = nowTs; + + // + // TODO: Due to the way the new NX transport is working, + // the accounting of time spent handling messages must + // be rewritten. In particular, at the moment it only + // shows the time spent encoding and decoding messages + // in the main loop, after executing a select. It doesn't + // take into account the time spent in the NXTrans* calls + // where messages can be encoded and decoded implicitly, + // on demand of the agent. When the agent transport is + // in use, these calls constitute the vast majority of + // the encoding activity. The result is that the number + // of KB encoded per second shown by the proxy statistics + // is actually much lower than the real throughput gene- + // rated by the proxy. + // + + #ifdef TEST + *logofs << "Loop: INIT! Completed initialization at " + << strMsTimestamp(nowTs) << " with " + << diffTimestamp(initTs, nowTs) << " Ms " + << "since the init mark.\n" << logofs_flush; + #endif + + initTs = getNewTimestamp(); + + // + // We can now start handling binary data from + // our peer proxy. + // + + if (agent == NULL) + { + cerr << "Session" << ": Session started at '" + << strTimestamp() << "'.\n"; + } + + return 1; +} + +int SetMode(int mode) +{ + // + // Set the local proxy mode. + // + + if (control -> ProxyMode == proxy_undefined) + { + if (mode == NX_MODE_CLIENT) + { + #ifdef TEST + *logofs << "Loop: INIT! Initializing with mode " + << "NX_MODE_CLIENT at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + control -> ProxyMode = proxy_client; + } + else if (mode == NX_MODE_SERVER) + { + #ifdef TEST + *logofs << "Loop: INIT! Initializing with mode " + << "NX_MODE_SERVER at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + control -> ProxyMode = proxy_server; + } + else + { + cerr << "Error" << ": Please specify either " + << "the -C or -S option.\n"; + + HandleCleanup(); + } + } + + return 1; +} + +int SetupProxyInstance() +{ + if (control -> ProxyMode == proxy_client) + { + proxy = new ClientProxy(proxyFD); + } + else + { + proxy = new ServerProxy(proxyFD); + } + + if (proxy == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error creating the NX proxy.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error creating the NX proxy.\n"; + + HandleCleanup(); + } + + // + // Create the statistics repository. + // + + statistics = new Statistics(proxy); + + if (statistics == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error creating the NX statistics.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error creating the NX statistics.\n"; + + HandleCleanup(); + } + + // + // If user gave us a proxy cookie than create the + // X11 authorization repository and find the real + // cookie to be used for our X display. + // + + SetupAuthInstance(); + + // + // Reset the static members in channels. + // + + proxy -> handleChannelConfiguration(); + + // + // Inform the proxies about the ports where they + // will have to forward the network connections. + // + + proxy -> handleDisplayConfiguration(displayHost, xServerAddrFamily, + xServerAddr, xServerAddrLength); + + proxy -> handlePortConfiguration(cupsPort, smbPort, mediaPort, + httpPort, fontPort); + + // + // We handed over the sockaddr structure we + // created when we set up the display socket + // to the proxy. + // + + xServerAddr = NULL; + + // + // Set socket options on proxy link, then propagate link + // configuration to proxy. This includes translating some + // control parameters in 'local' and 'remote'. Finally + // adjust cache parameters according to pack method and + // session type selected by user. + // + + if (proxy -> handleSocketConfiguration() < 0 || + proxy -> handleLinkConfiguration() < 0 || + proxy -> handleCacheConfiguration() < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error configuring the NX transport.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error configuring the NX transport.\n"; + + HandleCleanup(); + } + + // + // Load the message stores from the persistent + // cache. + // + + proxy -> handleLoad(load_if_first); + + // + // Inform the proxy that from now on it can + // start handling the encoded data. + // + + proxy -> setOperational(); + + // + // Are we going to use an internal IPC connection + // with an agent? In this case create the channel + // by using the socket descriptor provided by the + // caller at the proxy initialization. + // + + SetupAgentInstance(); + + // + // Check if we need to verify the existence of + // a matching client cache at shutdown. + // + + #ifdef MATCH + + control -> PersistentCacheCheckOnShutdown = 1; + + #endif + + // + // Flush any data produced so far. + // + + proxy -> handleFlush(); + + #if defined(TEST) || defined(INFO) + + if (proxy -> getFlushable(proxyFD) > 0) + { + *logofs << "Loop: WARNING! Proxy FD#" << proxyFD << " has data " + << "to flush after setup of the NX transport.\n" + << logofs_flush; + } + + #endif + + return 1; +} + +int SetupAuthInstance() +{ + // + // If user gave us a proxy cookie, then create the + // X11 authorization repository and find the real + // cookie to be used for our X display. + // + + if (control -> ProxyMode == proxy_server) + { + if (authCookie != NULL && *authCookie != '\0') + { + if (useLaunchdSocket == 1) + { + // + // If we are going to retrieve the X11 autho- + // rization through the launchd service, make + // a connection to its socket to trigger the + // X server starting. + // + + sockaddr_un launchdAddrUnix; + + unsigned int launchdAddrLength = sizeof(sockaddr_un); + + int launchdAddrFamily = AF_UNIX; + + launchdAddrUnix.sun_family = AF_UNIX; + + const int launchdAddrNameLength = 108; + + int success = -1; + + strncpy(launchdAddrUnix.sun_path, displayHost, launchdAddrNameLength); + + *(launchdAddrUnix.sun_path + launchdAddrNameLength - 1) = '\0'; + + #ifdef TEST + *logofs << "Loop: Connecting to launchd service " + << "on Unix port '" << displayHost << "'.\n" << logofs_flush; + #endif + + int launchdFd = socket(launchdAddrFamily, SOCK_STREAM, PF_UNSPEC); + + if (launchdFd < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + } + else if ((success = connect(launchdFd, (sockaddr *) &launchdAddrUnix, launchdAddrLength)) < 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Connection to launchd service " + << "on Unix port '" << displayHost << "' failed " + << "with error " << EGET() << ", '" << ESTR() << "'.\n" + << logofs_flush; + #endif + } + + if (launchdFd >= 0) + { + close(launchdFd); + } + + // + // The real cookie will not be available + // until the X server starts. Query for the + // cookie in a loop, unless the connection + // to the launchd service failed. + // + + int attempts = (success < 0 ? 1 : 10); + + for (int i = 0; i < attempts; i++) + { + delete auth; + + auth = new Auth(displayHost, authCookie); + + if (auth != NULL && auth -> isFake() == 1) + { + usleep(200000); + + continue; + } + + break; + } + } + else + { + auth = new Auth(displayHost, authCookie); + } + + if (auth == NULL || auth -> isValid() != 1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error creating the X authorization.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error creating the X authorization.\n"; + + HandleCleanup(); + } + else if (auth -> isFake() == 1) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Could not retrieve the X server " + << "authentication cookie.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Failed to read data from the X " + << "auth command.\n"; + + cerr << "Warning" << ": Generated a fake cookie for X " + << "authentication.\n"; + } + } + else + { + #ifdef TEST + *logofs << "Loop: No proxy cookie was provided for " + << "authentication.\n" << logofs_flush; + #endif + + cerr << "Info" << ": No proxy cookie was provided for " + << "authentication.\n"; + + #ifdef TEST + *logofs << "Loop: Forwarding the real X authorization " + << "cookie.\n" << logofs_flush; + #endif + + cerr << "Info" << ": Forwarding the real X authorization " + << "cookie.\n"; + } + } + + return 1; +} + +int SetupAgentInstance() +{ + if (control -> ProxyMode == proxy_client && + useAgentSocket == 1) + { + // + // This will temporarily disable signals to safely + // load the cache, then will send a control packet + // to the remote end, telling that cache has to be + // loaded, so it's important that proxy is already + // set in operational state. + // + + int result; + + if (agent != NULL) + { + result = proxy -> handleNewAgentConnection(agent); + } + else + { + result = proxy -> handleNewConnection(channel_x11, agentFD[1]); + } + + if (result < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error creating the NX agent connection.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error creating the NX agent connection.\n"; + + HandleCleanup(); + } + } + + return 1; +} + +int SetupTcpSocket() +{ + // + // Open TCP socket emulating local display. + // + + tcpFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); + + if (tcpFD == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed for TCP socket" + << ". Error is " << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed for TCP socket" + << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; + + HandleCleanup(); + } + else if (SetReuseAddress(tcpFD) < 0) + { + HandleCleanup(); + } + + unsigned int proxyPortTCP = X_TCP_PORT + proxyPort; + + sockaddr_in tcpAddr; + + tcpAddr.sin_family = AF_INET; + tcpAddr.sin_port = htons(proxyPortTCP); + tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(tcpFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to bind failed for TCP port " + << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to bind failed for TCP port " + << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + HandleCleanup(); + } + + if (listen(tcpFD, 8) == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to listen failed for TCP port " + << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to listen failed for TCP port " + << proxyPortTCP << ". Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + HandleCleanup(); + } + + return 1; +} + +int SetupUnixSocket() +{ + // + // Open UNIX domain socket for display. + // + + unixFD = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); + + if (unixFD == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed for UNIX domain" + << ". Error is " << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed for UNIX domain" + << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; + + HandleCleanup(); + } + + sockaddr_un unixAddr; + unixAddr.sun_family = AF_UNIX; + + char dirName[DEFAULT_STRING_LENGTH]; + + snprintf(dirName, DEFAULT_STRING_LENGTH - 1, "%s/.X11-unix", + control -> TempPath); + + *(dirName + DEFAULT_STRING_LENGTH - 1) = '\0'; + + struct stat dirStat; + + if ((stat(dirName, &dirStat) == -1) && (EGET() == ENOENT)) + { + mkdir(dirName, (0777 | S_ISVTX)); + chmod(dirName, (0777 | S_ISVTX)); + } + + snprintf(unixSocketName, DEFAULT_STRING_LENGTH - 1, "%s/X%d", + dirName, proxyPort); + + strncpy(unixAddr.sun_path, unixSocketName, 108); + + #ifdef TEST + *logofs << "Loop: Assuming Unix socket with name '" + << unixAddr.sun_path << "'.\n" + << logofs_flush; + #endif + + *(unixAddr.sun_path + 107) = '\0'; + + if (bind(unixFD, (sockaddr *) &unixAddr, sizeof(unixAddr)) == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to bind failed for UNIX domain socket " + << unixSocketName << ". Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to bind failed for UNIX domain socket " + << unixSocketName << ". Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + HandleCleanup(); + } + + if (listen(unixFD, 8) == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to listen failed for UNIX domain socket " + << unixSocketName << ". Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to listen failed for UNIX domain socket " + << unixSocketName << ". Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + HandleCleanup(); + } + + // + // Let any local user to gain access to socket. + // + + chmod(unixSocketName, 0777); + + return 1; +} + +// +// The following is a dumb copy-paste. The +// nxcompsh library should offer a better +// implementation. +// + +int SetupDisplaySocket(int &xServerAddrFamily, sockaddr *&xServerAddr, + unsigned int &xServerAddrLength) +{ + xServerAddrFamily = AF_INET; + xServerAddr = NULL; + xServerAddrLength = 0; + + char *display; + + if (*displayHost == '\0') + { + // + // Assume DISPLAY as the X server to which + // we will forward the proxied connections. + // This means that NX parameters have been + // passed through other means. + // + + display = getenv("DISPLAY"); + + if (display == NULL || *display == '\0') + { + #ifdef PANIC + *logofs << "Loop: PANIC! Host X server DISPLAY is not set.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Host X server DISPLAY is not set.\n"; + + HandleCleanup(); + } + else if (strncasecmp(display, "nx/nx,", 6) == 0 || + strncasecmp(display, "nx,", 3) == 0 || + strncasecmp(display, "nx:", 3) == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! NX transport on host X server '" + << display << "' not supported.\n" << logofs_flush; + #endif + + cerr << "Error" << ": NX transport on host X server '" + << display << "' not supported.\n"; + + cerr << "Error" << ": Please run the local proxy specifying " + << "the host X server to connect to.\n"; + + HandleCleanup(); + } + else if (strlen(display) >= DEFAULT_STRING_LENGTH) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Host X server DISPLAY cannot exceed " + << DEFAULT_STRING_LENGTH << " characters.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Host X server DISPLAY cannot exceed " + << DEFAULT_STRING_LENGTH << " characters.\n"; + + HandleCleanup(); + } + + strcpy(displayHost, display); + } + + display = new char[strlen(displayHost) + 1]; + + if (display == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Out of memory handling DISPLAY variable.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Out of memory handling DISPLAY variable.\n"; + + HandleCleanup(); + } + + strcpy(display, displayHost); + + #ifdef __APPLE__ + + if (strncasecmp(display, "/tmp/launch", 11) == 0) + { + #ifdef TEST + *logofs << "Loop: Using launchd service on socket '" + << display << "'.\n" << logofs_flush; + #endif + + useLaunchdSocket = 1; + } + + #endif + + char *separator = rindex(display, ':'); + + if ((separator == NULL) || !isdigit(*(separator + 1))) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid display '" << display << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid display '" << display << "'.\n"; + + HandleCleanup(); + } + + *separator = '\0'; + + xPort = atoi(separator + 1); + + #ifdef TEST + *logofs << "Loop: Using local X display '" << displayHost + << "' with host '" << display << "' and port '" + << xPort << "'.\n" << logofs_flush; + #endif + + #ifdef __APPLE__ + + if (separator == display || strcmp(display, "unix") == 0 || + useLaunchdSocket == 1) + + #else + + if (separator == display || strcmp(display, "unix") == 0) + + #endif + { + // + // UNIX domain port. + // + + #ifdef TEST + *logofs << "Loop: Using real X server on UNIX domain socket.\n" + << logofs_flush; + #endif + + sockaddr_un *xServerAddrUNIX = new sockaddr_un; + + xServerAddrFamily = AF_UNIX; + xServerAddrUNIX -> sun_family = AF_UNIX; + + // + // The scope of this function is to fill either the sockaddr_un + // (when the display is set to the Unix Domain socket) or the + // sockaddr_in structure (when connecting by TCP) only once, so + // that the structure will be later used at the time the server + // proxy will try to forward the connection to the X server. We + // don't need to verify that the socket does exist at the pre- + // sent moment. The method that forwards the connection will + // perform the required checks and will retry, if needed. Anyway + // we need to select the name of the socket, so we check if the + // well-known directory exists and take that as an indication of + // where the socket will be created. + // + + struct stat statInfo; + + char unixSocketDir[DEFAULT_STRING_LENGTH]; + + snprintf(unixSocketDir, DEFAULT_STRING_LENGTH - 1, "%s/.X11-unix", + control -> TempPath); + + #ifdef __APPLE__ + + if (useLaunchdSocket == 1) + { + char *slash = rindex(display, '/'); + + if (slash != NULL) + { + *slash = '\0'; + } + + snprintf(unixSocketDir, DEFAULT_STRING_LENGTH - 1, "%s", display); + } + + #endif + + *(unixSocketDir + DEFAULT_STRING_LENGTH - 1) = '\0'; + + #ifdef TEST + *logofs << "Loop: Assuming X socket in directory '" + << unixSocketDir << "'.\n" << logofs_flush; + #endif + + if (stat(unixSocketDir, &statInfo) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't determine the location of " + << "the X display socket.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't determine the location of " + << "the X display socket.\n"; + + #ifdef PANIC + *logofs << "Loop: PANIC! Error " << EGET() << " '" << ESTR() + << "' checking '" << unixSocketDir << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error " << EGET() << " '" << ESTR() + << "' checking '" << unixSocketDir << "'.\n"; + + HandleCleanup(); + } + + sprintf(unixSocketName, "%s/X%d", unixSocketDir, xPort); + + #ifdef __APPLE__ + + if (useLaunchdSocket == 1) + { + strncpy(unixSocketName, displayHost, DEFAULT_STRING_LENGTH - 1); + } + + #endif + + #ifdef TEST + *logofs << "Loop: Assuming X socket name '" << unixSocketName + << "'.\n" << logofs_flush; + #endif + + strcpy(xServerAddrUNIX -> sun_path, unixSocketName); + + xServerAddr = (sockaddr *) xServerAddrUNIX; + xServerAddrLength = sizeof(sockaddr_un); + } + else + { + // + // TCP port. + // + + #ifdef TEST + *logofs << "Loop: Using real X server on TCP port.\n" + << logofs_flush; + #endif + + xServerAddrFamily = AF_INET; + + int xServerIPAddr = GetHostAddress(display); + + if (xServerIPAddr == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Unknown display host '" << display + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unknown display host '" << display + << "'.\n"; + + HandleCleanup(); + } + + sockaddr_in *xServerAddrTCP = new sockaddr_in; + + xServerAddrTCP -> sin_family = AF_INET; + xServerAddrTCP -> sin_port = htons(X_TCP_PORT + xPort); + xServerAddrTCP -> sin_addr.s_addr = xServerIPAddr; + + xServerAddr = (sockaddr *) xServerAddrTCP; + xServerAddrLength = sizeof(sockaddr_in); + } + + delete [] display; + + return 1; +} + +int SetupServiceSockets() +{ + if (control -> ProxyMode == proxy_client) + { + if (useCupsSocket) + { + if ((cupsFD = ListenConnection(cupsPort, "CUPS")) < 0) + { + useCupsSocket = 0; + } + } + + if (useAuxSocket) + { + if ((auxFD = ListenConnection(auxPort, "auxiliary X11")) < 0) + { + useAuxSocket = 0; + } + } + + if (useSmbSocket) + { + if ((smbFD = ListenConnection(smbPort, "SMB")) < 0) + { + useSmbSocket = 0; + } + } + + if (useMediaSocket) + { + if ((mediaFD = ListenConnection(mediaPort, "media")) < 0) + { + useMediaSocket = 0; + } + } + + if (useHttpSocket) + { + if ((httpFD = ListenConnection(httpPort, "http")) < 0) + { + useHttpSocket = 0; + } + } + + useFontSocket = 0; + } + else + { + // + // Disable the font server connections if + // they are not supported by the remote + // proxy. + // + + if (useFontSocket) + { + if (control -> isProtoStep7() == 1) + { + int port = atoi(fontPort); + + if ((fontFD = ListenConnection(port, "font")) < 0) + { + useFontSocket = 0; + } + } + else + { + #ifdef WARNING + *logofs << "Loop: WARNING! Font server connections not supported " + << "by the remote proxy.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Font server connections not supported " + << "by the remote proxy.\n"; + + useFontSocket = 0; + } + } + + useCupsSocket = 0; + useAuxSocket = 0; + useSmbSocket = 0; + useMediaSocket = 0; + useHttpSocket = 0; + } + + // + // Slave channels can be originated + // by both sides. + // + + if (useSlaveSocket) + { + if (control -> isProtoStep7() == 1) + { + if ((slaveFD = ListenConnection(slavePort, "slave")) < 0) + { + useSlaveSocket = 0; + } + } + else + { + #ifdef WARNING + *logofs << "Loop: WARNING! Slave connections not supported " + << "by the remote proxy.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Slave connections not supported " + << "by the remote proxy.\n"; + + useSlaveSocket = 0; + } + } + + return 1; +} + +int ListenConnection(int port, const char *label) +{ + sockaddr_in tcpAddr; + + unsigned int portTCP = port; + + int newFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); + + if (newFD == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed for " << label + << " TCP socket. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed for " << label + << " TCP socket. Error is " << EGET() << " '" + << ESTR() << "'.\n"; + + goto SetupSocketError; + } + else if (SetReuseAddress(newFD) < 0) + { + goto SetupSocketError; + } + + tcpAddr.sin_family = AF_INET; + tcpAddr.sin_port = htons(portTCP); + tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(newFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to bind failed for " << label + << " TCP port " << port << ". Error is " << EGET() + << " '" << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to bind failed for " << label + << " TCP port " << port << ". Error is " << EGET() + << " '" << ESTR() << "'.\n"; + + goto SetupSocketError; + } + + if (listen(newFD, 8) == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to bind failed for " << label + << " TCP port " << port << ". Error is " << EGET() + << " '" << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to bind failed for " << label + << " TCP port " << port << ". Error is " << EGET() + << " '" << ESTR() << "'.\n"; + + goto SetupSocketError; + } + + return newFD; + +SetupSocketError: + + if (newFD != -1) + { + close(newFD); + } + + // + // May optionally return. The session would + // continue without the service. The problem + // with this approach is that it would make + // harder to track problems with allocation + // of ports in clients and server. + // + + HandleCleanup(); +} + +static int AcceptConnection(int fd, int domain, const char *label) +{ + struct sockaddr newAddr; + + socklen_t addrLen = sizeof(newAddr); + + #ifdef TEST + + if (domain == AF_UNIX) + { + *logofs << "Loop: Going to accept new Unix " << label + << " connection on FD#" << fd << ".\n" + << logofs_flush; + } + else + { + *logofs << "Loop: Going to accept new TCP " << label + << " connection on FD#" << fd << ".\n" + << logofs_flush; + } + + #endif + + int newFD = accept(fd, &newAddr, &addrLen); + + if (newFD < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to accept failed for " + << label << " connection. Error is " << EGET() + << " '" << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to accept failed for " + << label << " connection. Error is " << EGET() + << " '" << ESTR() << "'.\n"; + } + + return newFD; +} + +void HandleShutdown() +{ + if (proxy -> getShutdown() == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! No shutdown of proxy link " + << "performed by remote proxy.\n" + << logofs_flush; + #endif + + // + // Close the socket before showing the alert. + // It seems that the closure of the socket can + // sometimes take several seconds, even after + // the connection is broken. The result is that + // the dialog can be shown long after the user + // has gone after the failed session. Note that + // disabling the linger timeout does not seem + // to make any difference. + // + + CleanupSockets(); + + cerr << "Error" << ": Connection with remote peer broken.\n"; + + #ifdef TEST + *logofs << "Loop: Bytes received so far are " + << (unsigned long long) statistics -> getBytesIn() + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Please check the state of your " + << "network and retry.\n"; + + handleTerminatingInLoop(); + + if (control -> ProxyMode == proxy_server) + { + #ifdef TEST + *logofs << "Loop: Showing the proxy abort dialog.\n" + << logofs_flush; + #endif + + HandleAlert(ABORT_PROXY_CONNECTION_ALERT, 1); + + handleAlertInLoop(); + } + } + #ifdef TEST + else + { + *logofs << "Loop: Finalized the remote proxy shutdown.\n" + << logofs_flush; + } + #endif + + HandleCleanup(); +} + + +void WaitCleanup() +{ + T_timestamp selectTs; + + while (NXTransRunning(NX_FD_ANY)) + { + setTimestamp(selectTs, control -> PingTimeout); + + NXTransContinue(&selectTs); + } +} + +int KillProcess(int pid, const char *label, int signal, int wait) +{ + if (pid > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Killing the " << label << " process '" + << pid << "' from process with pid '" << getpid() + << "' with signal '" << DumpSignal(signal) + << "'.\n" << logofs_flush; + #endif + + signal = (signal == 0 ? SIGTERM : signal); + + if (kill(pid, signal) < 0 && EGET() != ESRCH) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Couldn't kill the " << label + << " process with pid '" << pid << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Couldn't kill the " << label + << " process with pid '" << pid << "'.\n"; + } + + if (wait == 1) + { + WaitChild(pid, label, 1); + } + + return 1; + } + else + { + #ifdef TEST + *logofs << "Loop: No " << label << " process " + << "to kill with pid '" << pid + << "'.\n" << logofs_flush; + #endif + + return 0; + } +} + +int CheckProcess(int pid, const char *label) +{ + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Checking the " << label << " process '" + << pid << "' from process with pid '" << getpid() + << "'.\n" << logofs_flush; + #endif + + if (kill(pid, SIGCONT) < 0 && EGET() == ESRCH) + { + #ifdef WARNING + *logofs << "Loop: WARNING! The " << label << " process " + << "with pid '" << pid << "' has exited.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": The " << label << " process " + << "with pid '" << pid << "' has exited.\n"; + + return 0; + } + + return 1; +} + +int StartKeeper() +{ + #if defined(TEST) || defined(INFO) + + if (IsRunning(lastKeeper) == 1 || + IsRestarting(lastKeeper) == 1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! The house-keeping process is " + << "alreay running with pid '" << lastKeeper + << "'.\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + #endif + + // + // Don't care harvesting the persistent caches if + // the memory cache is not enabled. If the memory + // cache is not enabled neither we produced per- + // sistent caches. The user can still delete any + // persistent cache produced by the previous runs + // by using the client GUI. + // + // TODO: At the moment the user doesn't have a way + // to specify the amount of disk space to use for + // the persistent caches, but only the amount of + // space to use for images. + // + + if (control -> LocalTotalStorageSize > 0) + { + #ifdef TEST + *logofs << "Loop: Starting the house-keeping process with " + << "storage size " << control -> PersistentCacheDiskLimit + << ".\n" << logofs_flush; + #endif + + lastKeeper = NXTransKeeper(control -> PersistentCacheDiskLimit, + 0, control -> RootPath); + + if (IsFailed(lastKeeper)) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Failed to start the NX keeper process.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Failed to start the NX keeper process.\n"; + + SetNotRunning(lastKeeper); + } + #ifdef TEST + else + { + *logofs << "Loop: Keeper started with pid '" + << lastKeeper << "'.\n" << logofs_flush; + } + #endif + } + #ifdef TEST + else + { + *logofs << "Loop: Nothing to do for the keeper process " + << "with persistent cache not enabled.\n" + << logofs_flush; + } + #endif + + return 1; +} + +void HandleCleanup(int code) +{ + #ifdef TEST + *logofs << "Loop: Going to clean up system resources " + << "in process '" << getpid() << "'.\n" + << logofs_flush; + #endif + + handleTerminatedInLoop(); + + // + // Suspend any signal while cleaning up. + // + + DisableSignals(); + + if (getpid() == lastProxy) + { + // + // Terminate all the children. + // + + CleanupChildren(); + + // + // Close all listeners. + // + + CleanupListeners(); + + // + // Close all sockets. + // + + CleanupSockets(); + + // + // Release the global objects. + // + + CleanupGlobal(); + + // + // Restore the original signal handlers. + // + + RestoreSignals(); + } + + // + // This is our last chance to print a message. If this + // is the process which created the transport we will + // jump back into the loop, letting the caller find out + // that the connection is broken, otherwise we assume + // that this is a child of the proxy and so we will + // safely exit. + // + + #ifdef TEST + + if (getpid() == lastProxy) + { + *logofs << "Loop: Reverting to loop context in process with " + << "pid '" << getpid() << "' at " << strMsTimestamp() + << ".\n" << logofs_flush; + } + else + { + *logofs << "Loop: Exiting from child process with pid '" + << getpid() << "' at " << strMsTimestamp() + << ".\n" << logofs_flush; + } + + #endif + + if (getpid() == lastProxy) + { + // + // Reset all values to their default. + // + + CleanupLocal(); + + CleanupStreams(); + + longjmp(context, 1); + } + else + { + // + // Give a last chance to the process + // to cleanup the ancillary classes. + // + + CleanupKeeper(); + + CleanupStreams(); + + exit(code); + } +} + +void CleanupKeeper() +{ + if (keeper != NULL) + { + #ifdef TEST + *logofs << "Loop: Freeing up keeper in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + delete keeper; + + keeper = NULL; + } +} + +void CleanupStreams() +{ + // + // TODO: The cleanup procedure skips deletion of + // the I/O streams under Windows. This is intended + // to avoid a strange segfault occurring randomly, + // at the time the proxy is being shut down. + // + + #ifndef __CYGWIN32__ + + #ifdef TEST + *logofs << "Loop: Freeing up streams in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + if (logofs != NULL && logofs != &cerr && + *errorsFileName != '\0') + { + *logofs << flush; + + delete logofs; + + // + // Let the log go again to the standard + // error. + // + + logofs = &cerr; + } + + if (statofs != NULL && statofs != &cerr && + *statsFileName != '\0') + { + *statofs << flush; + + delete statofs; + + statofs = NULL; + } + + if (errofs != NULL) + { + *errofs << flush; + + if (errofs == &cerr) + { + errofs = NULL; + } + else if (errsbuf != NULL) + { + cerr.rdbuf(errsbuf); + + errsbuf = NULL; + + delete errofs; + } + + errofs = NULL; + } + + #endif /* #ifndef __CYGWIN32__ */ + + // + // Reset these as they can't be reset + // in CleanupLocal(). + // + + *sessionFileName = '\0'; + *errorsFileName = '\0'; + *optionsFileName = '\0'; + *statsFileName = '\0'; +} + +void CleanupChildren() +{ + // + // Remove any watchdog. + // + + if (IsRunning(lastWatchdog)) + { + KillProcess(lastWatchdog, "watchdog", SIGTERM, 1); + + SetNotRunning(lastWatchdog); + + lastSignal = 0; + } + + // + // Kill the cache house-keeping process. + // + + if (IsRunning(lastKeeper)) + { + KillProcess(lastKeeper, "house-keeping", SIGTERM, 1); + + SetNotRunning(lastKeeper); + } + + // + // Let any running dialog to continue until it is + // closed by the user. In general this is the exp- + // ected behaviour, as for example when we are + // exiting because the link was abrouptedly shut + // down. + // + + if (IsRunning(lastDialog)) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: WARNING! Leaving the dialog process '" + << lastDialog << "' running in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + SetNotRunning(lastDialog); + } + + // + // Give user a chance to start a new session. + // + + if (control -> EnableRestartOnShutdown == 1) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Respawning the NX client " + << "on display '" << displayHost << "'.\n" + << logofs_flush; + #endif + + NXTransClient(displayHost); + } + + for (int i = 0; i < control -> KillDaemonOnShutdownNumber; i++) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Killing the NX daemon with " + << "pid '" << control -> KillDaemonOnShutdown[i] + << "'.\n" << logofs_flush; + #endif + + KillProcess(control -> KillDaemonOnShutdown[i], "daemon", SIGTERM, 0); + } +} + +void CleanupGlobal() +{ + if (proxy != NULL) + { + #ifdef TEST + *logofs << "Loop: Freeing up proxy in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + delete proxy; + + proxy = NULL; + } + + if (agent != NULL) + { + #ifdef TEST + *logofs << "Loop: Freeing up agent in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + delete agent; + + agent = NULL; + } + + if (auth != NULL) + { + #ifdef TEST + *logofs << "Loop: Freeing up auth data in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + delete auth; + + auth = NULL; + } + + if (statistics != NULL) + { + #ifdef TEST + *logofs << "Loop: Freeing up statistics in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + delete statistics; + + statistics = NULL; + } + + if (control != NULL) + { + #ifdef TEST + *logofs << "Loop: Freeing up control in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + delete control; + + control = NULL; + } +} + +void CleanupConnections() +{ + if (proxy -> getChannels(channel_x11) != 0) + { + #ifdef TEST + *logofs << "Loop: Closing any remaining X connections.\n" + << logofs_flush; + #endif + + proxy -> handleCloseAllXConnections(); + + #ifdef TEST + *logofs << "Loop: Closing any remaining listener.\n" + << logofs_flush; + #endif + + proxy -> handleCloseAllListeners(); + } + + proxy -> handleFinish(); +} + +void CleanupListeners() +{ + if (useTcpSocket == 1) + { + if (tcpFD != -1) + { + #ifdef TEST + *logofs << "Loop: Closing TCP listener in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + close(tcpFD); + + tcpFD = -1; + } + + useTcpSocket = 0; + } + + if (useUnixSocket == 1) + { + if (unixFD != -1) + { + #ifdef TEST + *logofs << "Loop: Closing UNIX listener in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + close(unixFD); + + unixFD = -1; + } + + if (*unixSocketName != '\0') + { + #ifdef TEST + *logofs << "Loop: Going to remove the Unix domain socket '" + << unixSocketName << "' in process " << "with pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + unlink(unixSocketName); + } + + useUnixSocket = 0; + } + + if (useAgentSocket == 1) + { + // + // There is no listener for the + // agent descriptor. + // + + useAgentSocket = 0; + } + + if (useCupsSocket == 1) + { + if (cupsFD != -1) + { + #ifdef TEST + *logofs << "Loop: Closing CUPS listener in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + close(cupsFD); + + cupsFD = -1; + } + + useCupsSocket = 0; + } + + if (useAuxSocket == 1) + { + if (auxFD != -1) + { + #ifdef TEST + *logofs << "Loop: Closing auxiliary X11 listener " + << "in process " << "with pid '" << getpid() + << "'.\n" << logofs_flush; + #endif + + close(auxFD); + + auxFD = -1; + } + + useAuxSocket = 0; + } + + if (useSmbSocket == 1) + { + if (smbFD != -1) + { + #ifdef TEST + *logofs << "Loop: Closing SMB listener in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + close(smbFD); + + smbFD = -1; + } + + useSmbSocket = 0; + } + + if (useMediaSocket == 1) + { + if (mediaFD != -1) + { + #ifdef TEST + *logofs << "Loop: Closing multimedia listener in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + close(mediaFD); + + mediaFD = -1; + } + + useMediaSocket = 0; + } + + if (useHttpSocket == 1) + { + if (httpFD != -1) + { + #ifdef TEST + *logofs << "Loop: Closing http listener in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + close(httpFD); + + httpFD = -1; + } + + useHttpSocket = 0; + } + + if (useFontSocket == 1) + { + if (fontFD != -1) + { + #ifdef TEST + *logofs << "Loop: Closing font server listener in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + close(fontFD); + + fontFD = -1; + } + + useFontSocket = 0; + } + + if (useSlaveSocket == 1) + { + if (slaveFD != -1) + { + #ifdef TEST + *logofs << "Loop: Closing slave listener in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + close(slaveFD); + + slaveFD = -1; + } + + useSlaveSocket = 0; + } +} + +void CleanupSockets() +{ + if (proxyFD != -1) + { + #ifdef TEST + *logofs << "Loop: Closing proxy FD in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + close(proxyFD); + + proxyFD = -1; + } + + if (agentFD[1] != -1) + { + #ifdef TEST + *logofs << "Loop: Closing agent FD in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + close(agentFD[1]); + + agentFD[0] = -1; + agentFD[1] = -1; + } +} + +void CleanupLocal() +{ + *homeDir = '\0'; + *rootDir = '\0'; + *tempDir = '\0'; + *systemDir = '\0'; + *sessionDir = '\0'; + + *linkSpeedName = '\0'; + *cacheSizeName = '\0'; + *shsegSizeName = '\0'; + *imagesSizeName = '\0'; + *bitrateLimitName = '\0'; + *packMethodName = '\0'; + *productName = '\0'; + + packMethod = -1; + packQuality = -1; + + *sessionType = '\0'; + *sessionId = '\0'; + + parsedOptions = 0; + parsedCommand = 0; + + *remoteData = '\0'; + remotePosition = 0; + + tcpFD = -1; + unixFD = -1; + cupsFD = -1; + auxFD = -1; + smbFD = -1; + mediaFD = -1; + httpFD = -1; + fontFD = -1; + slaveFD = -1; + proxyFD = -1; + + agentFD[0] = -1; + agentFD[1] = -1; + + useUnixSocket = 1; + useTcpSocket = 1; + useCupsSocket = 0; + useAuxSocket = 0; + useSmbSocket = 0; + useMediaSocket = 0; + useHttpSocket = 0; + useFontSocket = 0; + useSlaveSocket = 0; + useAgentSocket = 0; + + useNoDelay = -1; + usePolicy = -1; + useRender = -1; + useTaint = -1; + + *unixSocketName = '\0'; + + *connectHost = '\0'; + *acceptHost = '\0'; + *listenHost = '\0'; + *displayHost = '\0'; + *authCookie = '\0'; + + proxyPort = DEFAULT_NX_PROXY_PORT; + xPort = DEFAULT_NX_X_PORT; + + xServerAddrFamily = -1; + xServerAddrLength = 0; + + delete xServerAddr; + + xServerAddr = NULL; + + listenPort = -1; + connectPort = -1; + + cupsPort = -1; + auxPort = -1; + smbPort = -1; + mediaPort = -1; + httpPort = -1; + slavePort = -1; + + *fontPort = '\0'; + + *bindHost = '\0'; + bindPort = -1; + + initTs = nullTimestamp(); + startTs = nullTimestamp(); + logsTs = nullTimestamp(); + nowTs = nullTimestamp(); + + diffTs = 0; + + lastProxy = 0; + lastDialog = 0; + lastWatchdog = 0; + lastKeeper = 0; + lastStatus = 0; + lastKill = 0; + lastDestroy = 0; + + lastReadableTs = nullTimestamp(); + + lastAlert.code = 0; + lastAlert.local = 0; + + lastMasks.blocked = 0; + lastMasks.installed = 0; + + memset(&lastMasks.saved, 0, sizeof(sigset_t)); + + for (int i = 0; i < 32; i++) + { + lastMasks.enabled[i] = 0; + lastMasks.forward[i] = 0; + + memset(&lastMasks.action[i], 0, sizeof(struct sigaction)); + } + + lastSignal = 0; + + memset(&lastTimer.action, 0, sizeof(struct sigaction)); + memset(&lastTimer.value, 0, sizeof(struct itimerval)); + + lastTimer.start = nullTimestamp(); + lastTimer.next = nullTimestamp(); +} + +int CheckAbort() +{ + if (lastSignal != 0) + { + #ifdef TEST + *logofs << "Loop: Aborting the procedure due to signal '" + << lastSignal << "', '" << DumpSignal(lastSignal) + << "'.\n" << logofs_flush; + #endif + + cerr << "Info" << ": Aborting the procedure due to signal '" + << lastSignal << "'.\n"; + + lastSignal = 0; + + return 1; + } + + return 0; +} + +void HandleAbort() +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + *logofs << flush; + + handleTerminatingInLoop(); + + if (lastSignal == SIGHUP) + { + lastSignal = 0; + } + + // + // The current default is to just quit the program. + // Code has not been updated to deal with the new + // NX transport loop. + // + + if (control -> EnableCoreDumpOnAbort == 1) + { + if (agent != NULL) + { + cerr << "Session" << ": Terminating session at '" + << strTimestamp() << "'.\n"; + } + + cerr << "Error" << ": Generating a core file to help " + << "the investigations.\n"; + + cerr << "Session" << ": Session terminated at '" + << strTimestamp() << "'.\n"; + + cerr << flush; + + signal(SIGABRT, SIG_DFL); + + raise(SIGABRT); + } + + #ifdef TEST + *logofs << "Loop: Showing the proxy abort dialog.\n" + << logofs_flush; + #endif + + if (control -> ProxyMode == proxy_server) + { + // + // Close the socket before showing the alert. + // It seems that the closure of the socket can + // sometimes take several seconds, even after + // the connection is broken. + // + + CleanupSockets(); + + if (lastKill == 0) + { + HandleAlert(ABORT_PROXY_CONNECTION_ALERT, 1); + } + else + { + HandleAlert(ABORT_PROXY_SHUTDOWN_ALERT, 1); + } + + handleAlertInLoop(); + } + + HandleCleanup(); +} + +void HandleAlert(int code, int local) +{ + if (lastAlert.code == 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Requesting an alert dialog with code " + << code << " and local " << local << ".\n" + << logofs_flush; + #endif + + lastAlert.code = code; + lastAlert.local = local; + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "Loop: WARNING! Alert dialog already requested " + << "with code " << lastAlert.code << ".\n" + << logofs_flush; + } + #endif + + return; +} + +void FlushCallback(int length) +{ + if (flushCallback != NULL) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Reporting a flush request at " + << strMsTimestamp() << " with " << length + << " bytes written.\n" << logofs_flush; + #endif + + (*flushCallback)(flushParameter, length); + } + #if defined(TEST) || defined(INFO) + else if (control -> ProxyMode == proxy_client) + { + *logofs << "Loop: WARNING! Can't find a flush " + << "callback in process with pid '" << getpid() + << "'.\n" << logofs_flush; + } + #endif +} + +void KeeperCallback() +{ + if (IsRunning(lastKeeper) == 0) + { + // + // Let the house-keeping process take care + // of the persistent image cache. + // + + if (control -> ImageCacheEnableLoad == 1 || + control -> ImageCacheEnableSave == 1) + { + #ifdef TEST + *logofs << "Loop: Starting the house-keeping process with " + << "image storage size " << control -> ImageCacheDiskLimit + << ".\n" << logofs_flush; + #endif + + lastKeeper = NXTransKeeper(0, control -> ImageCacheDiskLimit, + control -> RootPath); + + if (IsFailed(lastKeeper)) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Can't start the NX keeper process.\n" + << logofs_flush; + #endif + + SetNotRunning(lastKeeper); + } + #ifdef TEST + else + { + *logofs << "Loop: Keeper started with pid '" + << lastKeeper << "'.\n" << logofs_flush; + } + #endif + } + #ifdef TEST + else + { + *logofs << "Loop: Nothing to do for the keeper process " + << "with image cache not enabled.\n" + << logofs_flush; + } + #endif + } + #ifdef TEST + else + { + *logofs << "Loop: Nothing to do with the keeper process " + << "already running.\n" << logofs_flush; + } + #endif +} + +void InstallSignals() +{ + #ifdef TEST + *logofs << "Loop: Installing signals in process with pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + for (int i = 0; i < 32; i++) + { + if (CheckSignal(i) == 1 && + lastMasks.enabled[i] == 0) + { + InstallSignal(i, NX_SIGNAL_ENABLE); + } + } + + lastMasks.installed = 1; +} + +void RestoreSignals() +{ + #ifdef TEST + *logofs << "Loop: Restoring signals in process with pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + if (lastMasks.installed == 1) + { + // + // Need to keep monitoring the children. + // + + for (int i = 0; i < 32; i++) + { + if (lastMasks.enabled[i] == 1) + { + RestoreSignal(i); + } + } + } + + lastMasks.installed = 0; + + if (lastMasks.blocked == 1) + { + EnableSignals(); + } +} + +void DisableSignals() +{ + if (lastMasks.blocked == 0) + { + sigset_t newMask; + + sigemptyset(&newMask); + + // + // Block also the other signals that may be + // installed by the agent, that are those + // signals for which the function returns 2. + // + + for (int i = 0; i < 32; i++) + { + if (CheckSignal(i) > 0) + { + #ifdef DUMP + *logofs << "Loop: Disabling signal " << i << " '" + << DumpSignal(i) << "' in process with pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + sigaddset(&newMask, i); + } + } + + sigprocmask(SIG_BLOCK, &newMask, &lastMasks.saved); + + lastMasks.blocked++; + } + #ifdef TEST + else + { + *logofs << "Loop: WARNING! Signals were already blocked in " + << "process with pid '" << getpid() << "'.\n" + << logofs_flush; + } + #endif +} + +void EnableSignals() +{ + if (lastMasks.blocked == 1) + { + #ifdef TEST + *logofs << "Loop: Enabling signals in process with pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + sigprocmask(SIG_SETMASK, &lastMasks.saved, NULL); + + lastMasks.blocked = 0; + } + else + { + #ifdef WARNING + *logofs << "Loop: WARNING! Signals were not blocked in " + << "process with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Signals were not blocked in " + << "process with pid '" << getpid() << "'.\n"; + } +} + +void InstallSignal(int signal, int action) +{ + if (lastMasks.enabled[signal] == 1) + { + if (action == NX_SIGNAL_FORWARD) + { + #ifdef TEST + *logofs << "Loop: Forwarding handler for signal " << signal + << " '" << DumpSignal(signal) << "' in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + lastMasks.forward[signal] = 1; + + return; + } + #ifdef TEST + else + { + *logofs << "Loop: Reinstalling handler for signal " << signal + << " '" << DumpSignal(signal) << "' in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + } + #endif + } + #ifdef TEST + else + { + *logofs << "Loop: Installing handler for signal " << signal + << " '" << DumpSignal(signal) << "' in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + } + #endif + + if (signal == SIGALRM && isTimestamp(lastTimer.start)) + { + ResetTimer(); + } + + struct sigaction newAction; + + newAction.sa_handler = HandleSignal; + + // + // This field doesn't exist on most OSes except + // Linux. We keep setting the field to NULL to + // avoid side-effects in the case the field is + // a value return. + // + + #if defined(__linux__) + + newAction.sa_restorer = NULL; + + #endif + + sigemptyset(&(newAction.sa_mask)); + + if (signal == SIGCHLD) + { + newAction.sa_flags = SA_NOCLDSTOP; + } + else + { + newAction.sa_flags = 0; + } + + sigaction(signal, &newAction, &lastMasks.action[signal]); + + lastMasks.enabled[signal] = 1; + + if (action == NX_SIGNAL_FORWARD) + { + lastMasks.forward[signal] = 1; + } +} + +void RestoreSignal(int signal) +{ + if (lastMasks.enabled[signal] == 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Signal '" << DumpSignal(signal) + << " not installed in process with pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Signal '" << DumpSignal(signal) + << " not installed in process with pid '" + << getpid() << "'.\n"; + + return; + } + + #ifdef TEST + *logofs << "Loop: Restoring handler for signal " << signal + << " '" << DumpSignal(signal) << "' in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + if (signal == SIGALRM && isTimestamp(lastTimer.start)) + { + ResetTimer(); + } + + sigaction(signal, &lastMasks.action[signal], NULL); + + lastMasks.enabled[signal] = 0; + lastMasks.forward[signal] = 0; +} + +void HandleSignal(int signal) +{ + if (logofs == NULL) + { + logofs = &cerr; + } + + #if defined(UNSAFE) && (defined(TEST) || defined(INFO)) + + if (lastSignal != 0) + { + *logofs << "Loop: WARNING! Last signal is '" << lastSignal + << "', '" << DumpSignal(signal) << "' and not zero " + << "in process with pid '" << getpid() << "'.\n" + << logofs_flush; + } + + *logofs << "Loop: Signal '" << signal << "', '" + << DumpSignal(signal) << "' received in process " + << "with pid '" << getpid() << "'.\n" << logofs_flush; + + #endif + + if (getpid() != lastProxy && handler != NULL) + { + #if defined(UNSAFE) && (defined(TEST) || defined(INFO)) + *logofs << "Loop: Calling slave handler in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + if ((*handler)(signal) == 0) + { + return; + } + } + + switch (signal) + { + case SIGUSR1: + { + if (proxy != NULL && lastSignal == 0) + { + lastSignal = SIGUSR1; + } + + break; + } + case SIGUSR2: + { + if (proxy != NULL && lastSignal == 0) + { + lastSignal = SIGUSR2; + } + + break; + } + case SIGPIPE: + { + // + // It can happen that SIGPIPE is delivered + // to the proxy even in the case some other + // descriptor is unexpectedly closed. + // + // if (agentFD[1] != -1) + // { + // cerr << "Info" << ": Received signal 'SIGPIPE'. " + // << "Closing agent conection.\n"; + // + // shutdown(agentFD[1], SHUT_RDWR); + // } + // + + break; + } + case SIGALRM: + { + // + // Nothing to do. Just wake up the + // process on blocking operations. + // + + break; + } + case SIGCHLD: + { + // + // Check if any of our children has exited. + // + + if (HandleChildren() != 0) + { + signal = 0; + } + + // + // Don't save this signal or it will override + // any previous signal sent by child before + // exiting. + // + + break; + } + + #ifdef __CYGWIN32__ + + case 12: + { + // + // Nothing to do. This signal is what is delivered + // by the Cygwin library when trying use a shared + // memory function if the daemon is not running. + // + + #ifdef TEST + *logofs << "Loop: WARNING! Received signal '12' in " + << "process with pid '" << getpid() << "'.\n" + << logofs_flush; + + *logofs << "Loop: WARNING! Please check that the " + << "cygserver daemon is running.\n" + << logofs_flush; + #endif + + break; + } + + #endif + + default: + { + // + // Register the signal so we can handle it + // inside the main loop. We will probably + // dispose any resource and exit. + // + + if (getpid() == lastProxy) + { + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Registering end of session request " + << "due to signal '" << signal << "', '" + << DumpSignal(signal) << "'.\n" + << logofs_flush; + #endif + + lastSignal = signal; + } + else + { + // + // This is a child, so exit immediately. + // + + HandleCleanup(); + } + } + } + + if (signal != 0 && lastMasks.forward[signal] == 1) + { + if (lastMasks.action[signal].sa_handler != NULL && + lastMasks.action[signal].sa_handler != HandleSignal) + { + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Forwarding signal '" << signal << "', '" + << DumpSignal(signal) << "' to previous handler.\n" + << logofs_flush; + #endif + + lastMasks.action[signal].sa_handler(signal); + } + #ifdef WARNING + else if (lastMasks.action[signal].sa_handler == NULL) + { + *logofs << "Loop: WARNING! Parent requested to forward " + << "signal '" << signal << "', '" << DumpSignal(signal) + << "' but didn't set a handler.\n" << logofs_flush; + } + #endif + } +} + +int HandleChildren() +{ + // + // Try to waitpid() for each child because the + // call might have return ECHILD and so we may + // have lost any of the processes. + // + + if (IsRunning(lastDialog) && HandleChild(lastDialog) == 1) + { + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Resetting pid of last dialog process " + << "in handler.\n" << logofs_flush; + #endif + + SetNotRunning(lastDialog); + + if (proxy != NULL) + { + proxy -> handleResetAlert(); + } + + return 1; + } + + if (IsRunning(lastWatchdog) && HandleChild(lastWatchdog) == 1) + { + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Watchdog is gone. Setting the last " + << "signal to SIGHUP.\n" << logofs_flush; + #endif + + lastSignal = SIGHUP; + + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Resetting pid of last watchdog process " + << "in handler.\n" << logofs_flush; + #endif + + SetNotRunning(lastWatchdog); + + return 1; + } + + // + // The house-keeping process exits after a + // number of iterations to keep the memory + // pollution low. It is restarted on demand + // by the lower layers, using the callback + // function. + // + + if (IsRunning(lastKeeper) && HandleChild(lastKeeper) == 1) + { + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Resetting pid of last house-keeping " + << "process in handler.\n" << logofs_flush; + #endif + + SetNotRunning(lastKeeper); + + return 1; + } + + // + // The pid will be checked by the code + // that registered the child. + // + + if (IsRunning(lastChild)) + { + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Resetting pid of last child process " + << "in handler.\n" << logofs_flush; + #endif + + SetNotRunning(lastChild); + + return 1; + } + + // + // This can actually happen either because we + // reset the pid of the child process as soon + // as we kill it, or because of a child process + // of our parent. + // + + #if defined(UNSAFE) && (defined(TEST) || defined(INFO)) + *logofs << "Loop: Ignoring signal received for the " + << "unregistered child.\n" << logofs_flush; + #endif + + return 0; +} + +int HandleChild(int child) +{ + int pid; + + int status = 0; + int options = WNOHANG | WUNTRACED; + + while ((pid = waitpid(child, &status, options)) && + pid == -1 && EGET() == EINTR); + + return CheckChild(pid, status); +} + +int WaitChild(int child, const char* label, int force) +{ + int pid; + + int status = 0; + int options = WUNTRACED; + + for (;;) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Waiting for the " << label + << " process '" << child << "' to die.\n" + << logofs_flush; + #endif + + pid = waitpid(child, &status, options); + + if (pid == -1 && EGET() == EINTR) + { + if (force == 0) + { + return 0; + } + + #ifdef WARNING + *logofs << "Loop: WARNING! Ignoring signal while " + << "waiting for the " << label << " process '" + << child << "' to die.\n" + << logofs_flush; + #endif + + continue; + } + + break; + } + + return (EGET() == ECHILD ? 1 : CheckChild(pid, status)); +} + +int CheckChild(int pid, int status) +{ + lastStatus = 0; + + if (pid > 0) + { + if (WIFSTOPPED(status)) + { + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Child process '" << pid << "' was stopped " + << "with signal " << (WSTOPSIG(status)) << ".\n" + << logofs_flush; + #endif + + return 0; + } + else + { + if (WIFEXITED(status)) + { + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Child process '" << pid << "' exited " + << "with status '" << (WEXITSTATUS(status)) + << "'.\n" << logofs_flush; + #endif + + lastStatus = WEXITSTATUS(status); + } + else if (WIFSIGNALED(status)) + { + if (CheckSignal(WTERMSIG(status)) != 1) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Child process '" << pid + << "' died because of signal " << (WTERMSIG(status)) + << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Child process '" << pid + << "' died because of signal " << (WTERMSIG(status)) + << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n"; + } + #if defined(UNSAFE) && defined(TEST) + else + { + *logofs << "Loop: Child process '" << pid + << "' died because of signal " << (WTERMSIG(status)) + << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n" + << logofs_flush; + } + #endif + + lastStatus = 1; + } + + return 1; + } + } + else if (pid < 0) + { + if (EGET() != ECHILD) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to waitpid failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to waitpid failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + HandleCleanup(); + } + + // + // This can happen when the waitpid() is + // blocking, as the SIGCHLD is received + // within the call. + // + + #ifdef TEST + *logofs << "Loop: No more children processes running.\n" + << logofs_flush; + #endif + + return 1; + } + + return 0; +} + +void RegisterChild(int child) +{ + #if defined(TEST) || defined(INFO) + + if (IsNotRunning(lastChild)) + { + *logofs << "Loop: Registering child process '" << child + << "' in process with pid '" << getpid() + << "'.\n" << logofs_flush; + } + else + { + *logofs << "Loop: WARNING! Overriding registered child '" + << lastChild << "' with new child '" << child + << "' in process with pid '" << getpid() + << "'.\n" << logofs_flush; + } + + #endif + + lastChild = child; +} + +int CheckParent(const char *name, const char *type, int parent) +{ + if (parent != getppid() || parent == 1) + { + #ifdef WARNING + *logofs << name << ": WARNING! Parent process appears " + << "to be dead. Exiting " << type << ".\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Parent process appears " + << "to be dead. Exiting " << type << ".\n"; + + return 0; + } + + return 1; +} + +void HandleTimer(int signal) +{ + if (signal == SIGALRM) + { + if (isTimestamp(lastTimer.start)) + { + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Timer expired at " << strMsTimestamp() + << " in process with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + if (proxy != NULL) + { + proxy -> handleTimer(); + } + + ResetTimer(); + } + else + { + #ifdef PANIC + *logofs << "Loop: PANIC! Inconsistent timer state " + << " in process with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Inconsistent timer state " + << " in process with pid '" << getpid() << "'.\n"; + } + } + else + { + #ifdef PANIC + *logofs << "Loop: PANIC! Inconsistent signal '" + << signal << "', '" << DumpSignal(signal) + << "' received in process with pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Inconsistent signal '" + << signal << "', '" << DumpSignal(signal) + << "' received in process with pid '" + << getpid() << "'.\n"; + } +} + +void SetTimer(int value) +{ + getNewTimestamp(); + + if (isTimestamp(lastTimer.start)) + { + int diffTs = diffTimestamp(lastTimer.start, getTimestamp()); + + if (diffTs > lastTimer.next.tv_usec / 1000 * 2) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Timer missed to expire at " + << strMsTimestamp() << " in process with pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Timer missed to expire at " + << strMsTimestamp() << " in process with pid '" + << getpid() << "'.\n"; + + HandleTimer(SIGALRM); + } + else + { + #ifdef TEST + *logofs << "Loop: Timer already running at " + << strMsTimestamp() << " in process with pid '" + << getpid() << "'.\n" << logofs_flush; + #endif + + return; + } + } + + // + // Save the former handler. + // + + struct sigaction action; + + action.sa_handler = HandleTimer; + + #if defined(__linux__) + + action.sa_restorer = NULL; + + #endif + + sigemptyset(&action.sa_mask); + + action.sa_flags = 0; + + sigaction(SIGALRM, &action, &lastTimer.action); + + // + // Start the timer. + // + + lastTimer.next = getTimestamp(value); + + struct itimerval timer; + + timer.it_interval = lastTimer.next; + timer.it_value = lastTimer.next; + + #ifdef TEST + *logofs << "Loop: Timer set to " << lastTimer.next.tv_sec + << " S and " << lastTimer.next.tv_usec / 1000 + << " Ms at " << strMsTimestamp() << " in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + if (setitimer(ITIMER_REAL, &timer, &lastTimer.value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to setitimer failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to setitimer failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + lastTimer.next = nullTimestamp(); + + return; + } + + lastTimer.start = getTimestamp(); +} + +void ResetTimer() +{ + if (isTimestamp(lastTimer.start) == 0) + { + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Timer not running in process " + << "with pid '" << getpid() << "'.\n" + << logofs_flush; + #endif + + return; + } + + #if defined(UNSAFE) && defined(TEST) + *logofs << "Loop: Timer reset at " << strMsTimestamp() + << " in process with pid '" << getpid() + << "'.\n" << logofs_flush; + #endif + + // + // Restore the old signal mask and timer. + // + + if (setitimer(ITIMER_REAL, &lastTimer.value, NULL) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to setitimer failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to setitimer failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + } + + if (sigaction(SIGALRM, &lastTimer.action, NULL) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to sigaction failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to sigaction failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + } + + lastTimer.start = lastTimer.next = nullTimestamp(); +} + +// +// Open TCP socket to listen for remote proxy and +// block until remote connects. If successful close +// the listening socket and return FD on which the +// other party is connected. +// + +int WaitForRemote(int portNum) +{ + char hostLabel[DEFAULT_STRING_LENGTH] = { 0 }; + + int retryAccept = -1; + int listenIPAddr = -1; + + int proxyFD = -1; + int newFD = -1; + + // + // Get IP address of host to be awaited. + // + + int acceptIPAddr = 0; + + if (*acceptHost != '\0') + { + acceptIPAddr = GetHostAddress(acceptHost); + + if (acceptIPAddr == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Cannot accept connections from unknown host '" + << acceptHost << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot accept connections from unknown host '" + << acceptHost << "'.\n"; + + goto WaitForRemoteError; + } + } + + proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); + + if (proxyFD == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed for TCP socket. " + << "Error is " << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed for TCP socket. " + << "Error is " << EGET() << " '" << ESTR() << "'.\n"; + + goto WaitForRemoteError; + } + else if (SetReuseAddress(proxyFD) < 0) + { + goto WaitForRemoteError; + } + + listenIPAddr = 0; + + ParseListenOption(listenIPAddr); + + sockaddr_in tcpAddr; + + tcpAddr.sin_family = AF_INET; + tcpAddr.sin_port = htons(portNum); + + // + // Quick patch to run on MacOS/X where inet_addr("127.0.0.1") + // alone seems to fail to return a valid interface. It probably + // just needs a htonl() or something like that. + // + // TODO: We have to give another look at inet_addr("127.0.0.1") + // on the Mac. + // + + #ifdef __APPLE__ + + tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY); + + #else + + tcpAddr.sin_addr.s_addr = listenIPAddr; + + #endif + + if (bind(proxyFD, (sockaddr *) &tcpAddr, sizeof(tcpAddr)) == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to bind failed for TCP port " + << portNum << ". Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to bind failed for TCP port " + << portNum << ". Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + goto WaitForRemoteError; + } + + if (listen(proxyFD, 4) == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to listen failed for TCP port " + << portNum << ". Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to listen failed for TCP port " + << portNum << ". Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + goto WaitForRemoteError; + } + + if (*acceptHost != '\0') + { + strcat(hostLabel, "'"); + strcat(hostLabel, acceptHost); + strcat(hostLabel, "'"); + } + else + { + strcpy(hostLabel, "any host"); + } + + #ifdef TEST + *logofs << "Loop: Waiting for connection from " + << hostLabel << " on port '" << portNum + << "'.\n" << logofs_flush; + #endif + + cerr << "Info" << ": Waiting for connection from " + << hostLabel << " on port '" << portNum + << "'.\n"; + + // + // How many times to loop waiting for connections + // from the selected host? Each loop wait for at + // most 20 seconds so a default value of 3 gives + // a timeout of 1 minute. + // + // TODO: Handling of timeouts and retry attempts + // must be rewritten. + // + + retryAccept = control -> OptionProxyRetryAccept; + + for (;;) + { + fd_set readSet; + + FD_ZERO(&readSet); + FD_SET(proxyFD, &readSet); + + T_timestamp selectTs; + + selectTs.tv_sec = 20; + selectTs.tv_usec = 0; + + int result = select(proxyFD + 1, &readSet, NULL, NULL, &selectTs); + + getNewTimestamp(); + + if (result == -1) + { + if (EGET() == EINTR) + { + if (CheckAbort() != 0) + { + goto WaitForRemoteError; + } + + continue; + } + + #ifdef PANIC + *logofs << "Loop: PANIC! Call to select failed. Error is " + << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Call to select failed. Error is " + << EGET() << " '" << ESTR() << "'.\n"; + + goto WaitForRemoteError; + } + else if (result > 0 && FD_ISSET(proxyFD, &readSet)) + { + sockaddr_in newAddr; + + size_t addrLen = sizeof(sockaddr_in); + + newFD = accept(proxyFD, (sockaddr *) &newAddr, (socklen_t *) &addrLen); + + if (newFD == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to accept failed. Error is " + << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Call to accept failed. Error is " + << EGET() << " '" << ESTR() << "'.\n"; + + goto WaitForRemoteError; + } + + char *connectedHost = inet_ntoa(newAddr.sin_addr); + + if (*acceptHost == '\0' || (int) newAddr.sin_addr.s_addr == acceptIPAddr) + { + #ifdef TEST + + unsigned int connectedPort = ntohs(newAddr.sin_port); + + *logofs << "Loop: Accepted connection from '" << connectedHost + << "' with port '" << connectedPort << "'.\n" + << logofs_flush; + #endif + + cerr << "Info" << ": Accepted connection from '" + << connectedHost << "'.\n"; + + break; + } + else + { + #ifdef PANIC + *logofs << "Loop: WARNING! Refusing connection from '" << connectedHost + << "' on port '" << portNum << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Refusing connection from '" + << connectedHost << "'.\n"; + } + + // + // Not the best way to elude a DOS attack... + // + + sleep(5); + + close(newFD); + } + + if (--retryAccept == 0) + { + if (*acceptHost == '\0') + { + #ifdef PANIC + *logofs << "Loop: PANIC! Connection with remote host " + << "could not be established.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Connection with remote host " + << "could not be established.\n"; + } + else + { + #ifdef PANIC + *logofs << "Loop: PANIC! Connection with remote host '" + << acceptHost << "' could not be established.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Connection with remote host '" + << acceptHost << "' could not be established.\n"; + } + + goto WaitForRemoteError; + } + else + { + handleCheckSessionInConnect(); + } + } + + close(proxyFD); + + return newFD; + +WaitForRemoteError: + + close(proxyFD); + + HandleCleanup(); +} + +// +// Connect to remote proxy. If successful +// return FD of connection, else return -1. +// + +int ConnectToRemote(const char *const hostName, int portNum) +{ + int proxyFD = -1; + + int remoteIPAddr = GetHostAddress(hostName); + + if (remoteIPAddr == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Unknown remote host '" + << hostName << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unknown remote host '" + << hostName << "'.\n"; + + HandleCleanup(); + } + + #ifdef TEST + *logofs << "Loop: Connecting to remote host '" + << hostName << ":" << portNum << "'.\n" + << logofs_flush; + #endif + + cerr << "Info" << ": Connecting to remote host '" + << hostName << ":" << portNum << "'.\n" + << logofs_flush; + + // + // How many times we retry to connect to remote + // host in case of failure? + // + + int retryConnect = control -> OptionProxyRetryConnect; + + // + // Show an alert after 20 seconds and use the + // same timeout to interrupt the connect. The + // retry timeout is incremental, starting from + // 100 miliseconds up to 1 second. + // + + int alertTimeout = 20000; + int connectTimeout = 20000; + int retryTimeout = 100; + + T_timestamp lastRetry = getNewTimestamp(); + + sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_port = htons(portNum); + addr.sin_addr.s_addr = remoteIPAddr; + + for (;;) + { + proxyFD = socket(AF_INET, SOCK_STREAM, PF_UNSPEC); + + if (proxyFD == -1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Call to socket failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + goto ConnectToRemoteError; + } + else if (SetReuseAddress(proxyFD) < 0) + { + goto ConnectToRemoteError; + } + + // + // Ensure operation is timed out + // if there is a network problem. + // + + #ifdef DEBUG + *logofs << "Loop: Timer set to " << connectTimeout / 1000 + << " S " << "with retry set to " << retryConnect + << " in process with pid '" << getpid() + << "'.\n" << logofs_flush; + #endif + + SetTimer(connectTimeout); + + int result = connect(proxyFD, (sockaddr *) &addr, sizeof(sockaddr_in)); + + int reason = EGET(); + + ResetTimer(); + + if (result < 0) + { + close(proxyFD); + + if (CheckAbort() != 0) + { + goto ConnectToRemoteError; + } + else if (--retryConnect == 0) + { + ESET(reason); + + #ifdef PANIC + *logofs << "Loop: PANIC! Connection to '" << hostName + << ":" << portNum << "' failed. Error is " + << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Connection to '" << hostName + << ":" << portNum << "' failed. Error is " + << EGET() << " '" << ESTR() << "'.\n"; + + goto ConnectToRemoteError; + } + else + { + #ifdef TEST + *logofs << "Loop: Sleeping " << retryTimeout + << " Ms before retrying.\n" + << logofs_flush; + #endif + + usleep(retryTimeout * 1000); + + retryTimeout <<= 1; + + if (retryTimeout > 1000 * 1000) + { + retryTimeout = 1000 * 1000; + } + } + + // + // Check if it is time to show an alert dialog. + // + + if (diffTimestamp(lastRetry, getNewTimestamp()) >= + (alertTimeout - control -> LatencyTimeout)) + { + if (IsNotRunning(lastDialog)) + { + handleCheckSessionInConnect(); + + // + // Wait for the dialog process to die + // unless a signal is received. + // + + while (IsRunning(lastDialog)) + { + WaitChild(lastDialog, "dialog", 0); + + if (CheckAbort() != 0) + { + // + // The client ignores the TERM signal + // on Windows. + // + + #ifdef __CYGWIN32__ + + KillProcess(lastDialog, "dialog", SIGKILL, 1); + + #else + + KillProcess(lastDialog, "dialog", SIGTERM, 1); + + #endif + + goto ConnectToRemoteError; + } + } + + lastRetry = getTimestamp(); + } + } + #ifdef TEST + { + *logofs << "Loop: Not showing the dialog with " + << (diffTimestamp(lastRetry, getTimestamp()) / 1000) + << " seconds elapsed.\n" << logofs_flush; + } + #endif + + ESET(reason); + + #ifdef TEST + *logofs << "Loop: Connection to '" << hostName + << ":" << portNum << "' failed with error '" + << ESTR() << "'. Retrying.\n" + << logofs_flush; + #endif + } + else + { + // + // Connection was successful. + // + + break; + } + } + + return proxyFD; + +ConnectToRemoteError: + + if (proxyFD != -1) + { + close(proxyFD); + } + + HandleCleanup(); +} + +// +// Make a string of options for the remote +// proxy and write it to the descriptor. +// The string includes the local version. +// + +int SendProxyOptions(int fd) +{ + char options[DEFAULT_REMOTE_OPTIONS_LENGTH]; + + // + // Send the "compatibility" version first, then our + // actual version. Old proxies will take the first + // value and ignore the second. + // + + sprintf(options, "NXPROXY-1.5.0-%i.%i.%i", control -> LocalVersionMajor, + control -> LocalVersionMinor, control -> LocalVersionPatch); + + // + // If you want to send options from proxy + // initiating the connection use something + // like this: + // + // if (WE_PROVIDE_CREDENTIALS) + // { + // sprintf(options + strlen(options), "%s=%s", option, value); + // } + // + // If you want to send options according to + // local proxy mode use something like this: + // + // if (control -> ProxyMode == proxy_client) + // { + // sprintf(options + strlen(options), "%s=%s", option, value); + // } + // + + // + // Send the authorization cookie if any. We assume + // user can choose to not provide any auth cookie + // and allow any connection to be accepted. + // + + if (WE_PROVIDE_CREDENTIALS && *authCookie != '\0') + { + sprintf(options + strlen(options), " cookie=%s,", authCookie); + } + else + { + sprintf(options + strlen(options), " "); + } + + // + // Now link characteristics and compression + // options. Delta compression, as well as + // preferred pack method, are imposed by + // client proxy. + // + + if (control -> ProxyMode == proxy_client) + { + sprintf(options + strlen(options), "link=%s,pack=%s,cache=%s,", + linkSpeedName, packMethodName, cacheSizeName); + + if (*bitrateLimitName != '\0') + { + sprintf(options + strlen(options), "limit=%s,", + bitrateLimitName); + } + + // + // Let the user disable the render extension + // and let the X client proxy know if it can + // short-circuit the X replies. Also pass + // along the session type to ensure that the + // remote proxy gets the right value. + // + + sprintf(options + strlen(options), "render=%d,taint=%d,", + (control -> HideRender == 0), + control -> TaintReplies); + + if (*sessionType != '\0') + { + sprintf(options + strlen(options), "type=%s,", sessionType); + } + else + { + sprintf(options + strlen(options), "type=default,"); + } + + // + // Add the 'strict' option, if needed. + // + + if (control -> isProtoStep7() == 1 && + useStrict != -1) + { + sprintf(options + strlen(options), "strict=%d,", useStrict); + } + + // + // Tell the remote the size of the shared + // memory segment. + // + + if (control -> isProtoStep7() == 1 && + *shsegSizeName != '\0') + { + sprintf(options + strlen(options), "shseg=%s,", shsegSizeName); + } + + // + // Send image cache parameters. + // + + sprintf(options + strlen(options), "images=%s,", imagesSizeName); + + sprintf(options + strlen(options), "delta=%d,stream=%d,data=%d ", + control -> LocalDeltaCompression, + control -> LocalStreamCompressionLevel, + control -> LocalDataCompressionLevel); + } + else + { + // + // If no special compression level was selected, + // server side will use compression levels set + // by client. + // + + if (control -> LocalStreamCompressionLevel < 0) + { + sprintf(options + strlen(options), "stream=default,"); + } + else + { + sprintf(options + strlen(options), "stream=%d,", + control -> LocalStreamCompressionLevel); + } + + if (control -> LocalDataCompressionLevel < 0) + { + sprintf(options + strlen(options), "data=default "); + } + else + { + sprintf(options + strlen(options), "data=%d ", + control -> LocalDataCompressionLevel); + } + } + + #ifdef TEST + *logofs << "Loop: Sending remote options '" + << options << "'.\n" << logofs_flush; + #endif + + return WriteLocalData(fd, options, strlen(options)); +} + +int ReadProxyVersion(int fd) +{ + #ifdef TEST + *logofs << "Loop: Going to read the remote proxy version " + << "from FD#" << fd << ".\n" << logofs_flush; + #endif + + // + // Read until the first space in string. + // We expect the remote version number. + // + + char options[DEFAULT_REMOTE_OPTIONS_LENGTH]; + + int result = ReadRemoteData(fd, options, sizeof(options), ' '); + + if (result <= 0) + { + if (result < 0) + { + if (control -> ProxyMode == proxy_server) + { + HandleAlert(ABORT_PROXY_NEGOTIATION_ALERT, 1); + } + + handleAlertInLoop(); + } + + return result; + } + + #ifdef TEST + *logofs << "Loop: Received remote version string '" + << options << "' from FD#" << fd << ".\n" + << logofs_flush; + #endif + + if (strncmp(options, "NXPROXY-", strlen("NXPROXY-")) != 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Parse error in remote options string '" + << options << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Parse error in remote options string '" + << options << "'.\n"; + + return -1; + } + + // + // Try to determine if this is a pre-2.0.0 + // version advertising itself as compatible + // with the 1.2.2. + // + + int major = -1; + int minor = -1; + int patch = -1; + + sscanf(options, "NXPROXY-%i.%i.%i-%i.%i.%i", &(control -> RemoteVersionMajor), + &(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch), + &major, &minor, &patch); + + if (control -> RemoteVersionMajor == 1 && + control -> RemoteVersionMinor == 2 && + control -> RemoteVersionPatch == 2 && + major != -1 && minor != -1 && patch != -1) + { + #ifdef TEST + *logofs << "Loop: Read trailing remote version '" << major + << "." << minor << "." << patch << "'.\n" + << logofs_flush; + #endif + + control -> CompatVersionMajor = major; + control -> CompatVersionMinor = minor; + control -> CompatVersionPatch = patch; + + control -> RemoteVersionMajor = major; + control -> RemoteVersionMinor = minor; + control -> RemoteVersionPatch = patch; + } + else + { + // + // We read the remote version at the first + // round. If the second version is missing, + // we will retain the values read before. + // + + sscanf(options, "NXPROXY-%i.%i.%i-%i.%i.%i", &(control -> CompatVersionMajor), + &(control -> CompatVersionMinor), &(control -> CompatVersionPatch), + &(control -> RemoteVersionMajor), &(control -> RemoteVersionMinor), + &(control -> RemoteVersionPatch)); + } + + #ifdef TEST + *logofs << "Loop: Identified remote version '" << control -> RemoteVersionMajor + << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch + << "'.\n" << logofs_flush; + + *logofs << "Loop: Remote compatibility version '" << control -> CompatVersionMajor + << "." << control -> CompatVersionMinor << "." << control -> CompatVersionPatch + << "'.\n" << logofs_flush; + + *logofs << "Loop: Local version '" << control -> LocalVersionMajor + << "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch + << "'.\n" << logofs_flush; + #endif + + if (SetVersion() < 0) + { + if (control -> ProxyMode == proxy_server) + { + HandleAlert(WRONG_PROXY_VERSION_ALERT, 1); + } + + handleAlertInLoop(); + + return -1; + } + + return 1; +} + +int ReadProxyOptions(int fd) +{ + #ifdef TEST + *logofs << "Loop: Going to read the remote proxy options " + << "from FD#" << fd << ".\n" << logofs_flush; + #endif + + char options[DEFAULT_REMOTE_OPTIONS_LENGTH]; + + int result = ReadRemoteData(fd, options, sizeof(options), ' '); + + if (result <= 0) + { + return result; + } + + #ifdef TEST + *logofs << "Loop: Received remote options string '" + << options << "' from FD#" << fd << ".\n" + << logofs_flush; + #endif + + // + // Get the remote options, delimited by a space character. + // Note that there will be a further initialization phase + // at the time proxies negotiate cache file to restore. + // + + if (ParseRemoteOptions(options) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Couldn't negotiate a valid " + << "session with remote NX proxy.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Couldn't negotiate a valid " + << "session with remote NX proxy.\n"; + + return -1; + } + + return 1; +} + +int SendProxyCaches(int fd) +{ + #ifdef TEST + *logofs << "Loop: Synchronizing local and remote caches.\n" + << logofs_flush; + #endif + + if (control -> ProxyMode == proxy_client) + { + // + // Prepare a list of caches matching this + // session type and send it to the remote. + // + + #ifdef TEST + *logofs << "Loop: Going to send the list of local caches.\n" + << logofs_flush; + #endif + + SetCaches(); + + int entries = DEFAULT_REMOTE_CACHE_ENTRIES; + + const char prefix = 'C'; + + if (control -> LocalDeltaCompression == 0 || + control -> PersistentCacheEnableLoad == 0) + { + #ifdef TEST + *logofs << "Loop: Writing an empty list to FD#" << fd + << ".\n" << logofs_flush; + #endif + + return WriteLocalData(fd, "cachelist=none ", strlen("cachelist=none ")); + } + + int count = 0; + + #ifdef TEST + *logofs << "Loop: Looking for cache files in directory '" + << control -> PersistentCachePath << "'.\n" << logofs_flush; + #endif + + DIR *cacheDir = opendir(control -> PersistentCachePath); + + if (cacheDir != NULL) + { + dirent *dirEntry; + + int prologue = 0; + + while (((dirEntry = readdir(cacheDir)) != NULL) && (count < entries)) + { + if (*dirEntry -> d_name == prefix && + strlen(dirEntry -> d_name) == (MD5_LENGTH * 2 + 2)) + { + if (prologue == 0) + { + WriteLocalData(fd, "cachelist=", strlen("cachelist=")); + + prologue = 1; + } + else + { + WriteLocalData(fd, ",", strlen(",")); + } + + #ifdef TEST + *logofs << "Loop: Writing entry '" << control -> PersistentCachePath + << "/" << dirEntry -> d_name << "' to FD#" << fd + << ".\n" << logofs_flush; + #endif + + // + // Write cache file name to the socket, + // including leading 'C-' or 'S-'. + // + + WriteLocalData(fd, dirEntry -> d_name, MD5_LENGTH * 2 + 2); + + count++; + } + } + + closedir(cacheDir); + } + + if (count == 0) + { + #ifdef TEST + *logofs << "Loop: Writing an empty list to FD#" << fd + << ".\n" << logofs_flush; + #endif + + return WriteLocalData(fd, "cachelist=none ", strlen("cachelist=none ")); + } + else + { + return WriteLocalData(fd, " ", 1); + } + } + else + { + // + // Send back the selected cache name. + // + + #ifdef TEST + *logofs << "Loop: Going to send the selected cache.\n" + << logofs_flush; + #endif + + char buffer[DEFAULT_STRING_LENGTH]; + + if (control -> PersistentCacheName != NULL) + { + #ifdef TEST + *logofs << "Loop: Name of selected cache file is '" + << control -> PersistentCacheName << "'.\n" + << logofs_flush; + #endif + + sprintf(buffer, "cachefile=%s%s ", + *(control -> PersistentCacheName) == 'C' ? "S-" : "C-", + control -> PersistentCacheName + 2); + } + else + { + #ifdef TEST + *logofs << "Loop: No valid cache file was selected.\n" + << logofs_flush; + #endif + + sprintf(buffer, "cachefile=none "); + } + + #ifdef TEST + *logofs << "Loop: Sending string '" << buffer + << "' as selected cache file.\n" + << logofs_flush; + #endif + + return WriteLocalData(fd, buffer, strlen(buffer)); + } +} + +int ReadProxyCaches(int fd) +{ + if (control -> ProxyMode == proxy_client) + { + #ifdef TEST + *logofs << "Loop: Going to receive the selected proxy cache.\n" + << logofs_flush; + #endif + + // + // We will read the name of cache plus the stop character. + // + + char buffer[DEFAULT_STRING_LENGTH]; + + // + // Leave space for a trailing null. + // + + int result = ReadRemoteData(fd, buffer, sizeof("cachefile=") + MD5_LENGTH * 2 + 3, ' '); + + if (result <= 0) + { + return result; + } + + char *cacheName = strstr(buffer, "cachefile="); + + if (cacheName == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid cache file option '" + << buffer << "' provided by remote proxy.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid cache file option '" + << buffer << "' provided by remote proxy.\n"; + + HandleCleanup(); + } + + cacheName += strlen("cachefile="); + + if (control -> PersistentCacheName != NULL) + { + delete [] control -> PersistentCacheName; + } + + control -> PersistentCacheName = NULL; + + if (strncasecmp(cacheName, "none", strlen("none")) == 0) + { + #ifdef TEST + *logofs << "Loop: No cache file selected by remote proxy.\n" + << logofs_flush; + #endif + } + else if (strlen(cacheName) != MD5_LENGTH * 2 + 3 || + *(cacheName + MD5_LENGTH * 2 + 2) != ' ') + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid cache file name '" + << cacheName << "' provided by remote proxy.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid cache file name '" + << cacheName << "' provided by remote proxy.\n"; + + HandleCleanup(); + } + else + { + // + // It is "C-" + 32 + "\0". + // + + control -> PersistentCacheName = new char[MD5_LENGTH * 2 + 3]; + + *(cacheName + MD5_LENGTH * 2 + 2) = '\0'; + + strcpy(control -> PersistentCacheName, cacheName); + + #ifdef TEST + *logofs << "Loop: Cache file '" << control -> PersistentCacheName + << "' selected by remote proxy.\n" << logofs_flush; + #endif + } + } + else + { + #ifdef TEST + *logofs << "Loop: Going to receive the list of remote caches.\n" + << logofs_flush; + #endif + + SetCaches(); + + int size = ((MD5_LENGTH * 2 + 2) + strlen(",")) * DEFAULT_REMOTE_CACHE_ENTRIES + + strlen("cachelist=") + strlen(" ") + 1; + + char *buffer = new char[size]; + + int result = ReadRemoteData(fd, buffer, size - 1, ' '); + + if (result <= 0) + { + delete [] buffer; + + return result; + } + + #ifdef TEST + *logofs << "Loop: Read list of caches from remote side as '" + << buffer << "'.\n" << logofs_flush; + #endif + + // + // Prepare the buffer. What we want is a list + // like "cache1,cache2,cache2" terminated by + // null. + // + + *(buffer + strlen(buffer) - 1) = '\0'; + + if (strncasecmp(buffer, "cachelist=", strlen("cachelist=")) != 0) + { + #ifdef PANIC + *logofs << "Loop: Wrong format for list of cache files " + << "read from FD#" << fd << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Wrong format for list of cache files.\n"; + + delete [] buffer; + + return -1; + } + + control -> PersistentCacheName = GetLastCache(buffer, control -> PersistentCachePath); + + // + // Get rid of list of caches. + // + + delete [] buffer; + } + + return 1; +} + +int ReadForwarderVersion(int fd) +{ + #ifdef TEST + *logofs << "Loop: Going to negotiate the forwarder version.\n" + << logofs_flush; + #endif + + // + // Check if we actually expect the session cookie. + // + + if (*authCookie == '\0') + { + #ifdef TEST + *logofs << "Loop: No authentication cookie required " + << "from FD#" << fd << ".\n" << logofs_flush; + #endif + + return 1; + } + + char options[DEFAULT_REMOTE_OPTIONS_LENGTH]; + + int result = ReadRemoteData(fd, options, sizeof(options), ' '); + + if (result <= 0) + { + return result; + } + + #ifdef TEST + *logofs << "Loop: Received forwarder version string '" << options + << "' from FD#" << fd << ".\n" << logofs_flush; + #endif + + if (strncmp(options, "NXSSH-", strlen("NXSSH-")) != 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Parse error in forwarder options string '" + << options << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Parse error in forwarder options string '" + << options << "'.\n"; + + return -1; + } + + // + // Accept whatever forwarder version. + // + + sscanf(options, "NXSSH-%i.%i.%i", &(control -> RemoteVersionMajor), + &(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch)); + + #ifdef TEST + *logofs << "Loop: Read forwarder version '" << control -> RemoteVersionMajor + << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch + << "'.\n" << logofs_flush; + #endif + + return 1; +} + +int ReadForwarderOptions(int fd) +{ + // + // Get the forwarder cookie. + // + + if (*authCookie == '\0') + { + #ifdef TEST + *logofs << "Loop: No authentication cookie required " + << "from FD#" << fd << ".\n" << logofs_flush; + #endif + + return 1; + } + + char options[DEFAULT_REMOTE_OPTIONS_LENGTH]; + + int result = ReadRemoteData(fd, options, sizeof(options), ' '); + + if (result <= 0) + { + return result; + } + + #ifdef TEST + *logofs << "Loop: Received forwarder options string '" + << options << "' from FD#" << fd << ".\n" + << logofs_flush; + #endif + + if (ParseForwarderOptions(options) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Couldn't negotiate a valid " + << "cookie with the NX forwarder.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Couldn't negotiate a valid " + << "cookie with the NX forwarder.\n"; + + return -1; + } + + return 1; +} + +int ReadRemoteData(int fd, char *buffer, int size, char stop) +{ + #ifdef TEST + *logofs << "Loop: Going to read remote data from FD#" + << fd << ".\n" << logofs_flush; + #endif + + if (size >= MAXIMUM_REMOTE_OPTIONS_LENGTH) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Maximum remote options buffer " + << "limit exceeded.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Maximum remote options buffer " + << "limit exceeded.\n"; + + HandleCleanup(); + } + + while (remotePosition < (size - 1)) + { + int result = read(fd, remoteData + remotePosition, 1); + + getNewTimestamp(); + + if (result <= 0) + { + if (result == -1) + { + if (EGET() == EAGAIN) + { + #ifdef TEST + *logofs << "Loop: Reading data from FD#" << fd + << " would block.\n" << logofs_flush; + #endif + + return 0; + } + else if (EGET() == EINTR) + { + if (CheckAbort() != 0) + { + return -1; + } + + continue; + } + } + + #ifdef PANIC + *logofs << "Loop: PANIC! The remote NX proxy closed " + << "the connection.\n" << logofs_flush; + #endif + + cerr << "Error" << ": The remote NX proxy closed " + << "the connection.\n"; + + return -1; + } + else if (*(remoteData + remotePosition) == stop) + { + #ifdef TEST + *logofs << "Loop: Read stop character from FD#" + << fd << ".\n" << logofs_flush; + #endif + + remotePosition++; + + // + // Copy the fake terminating null + // in the buffer. + // + + *(remoteData + remotePosition) = '\0'; + + memcpy(buffer, remoteData, remotePosition + 1); + + #ifdef TEST + *logofs << "Loop: Remote string '" << remoteData + << "' read from FD#" << fd << ".\n" + << logofs_flush; + #endif + + int t = remotePosition; + + remotePosition = 0; + + return t; + } + else + { + // + // Make sure string received + // from far end is printable. + // + + if (isgraph(*(remoteData + remotePosition)) == 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Non printable character decimal '" + << (unsigned int) *(remoteData + remotePosition) + << "' received in remote data from FD#" + << fd << ".\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Non printable character decimal '" + << (unsigned int) *(remoteData + remotePosition) + << "' received in remote data from FD#" + << fd << ".\n" << logofs_flush; + + *(remoteData + remotePosition) = ' '; + } + + #ifdef DEBUG + *logofs << "Loop: Read a further character " + << "from FD#" << fd << ".\n" + << logofs_flush; + #endif + + remotePosition++; + } + } + + *(remoteData + remotePosition) = '\0'; + + #ifdef PANIC + *logofs << "Loop: PANIC! Stop character missing " + << "from FD#" << fd << " after " << remotePosition + << " characters read in string '" << remoteData + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Stop character missing " + << "from FD#" << fd << " after " << remotePosition + << " characters read in string '" << remoteData + << "'.\n"; + + memcpy(buffer, remoteData, remotePosition); + + remotePosition = 0; + + return -1; +} + +int WriteLocalData(int fd, const char *buffer, int size) +{ + int position = 0; + + while (position < size) + { + int result = write(fd, buffer + position, size - position); + + getNewTimestamp(); + + if (result <= 0) + { + if (result < 0 && EGET() == EINTR) + { + continue; + } + + #ifdef TEST + *logofs << "Loop: Error writing data to FD#" + << fd << ".\n" << logofs_flush; + #endif + + return -1; + } + + position += result; + } + + return position; +} + +// +// Parse the string passed by calling process in +// the environment. This is not necessarily the +// content of DISPLAY variable, but can be the +// parameters passed when creating the process +// or thread. +// + +int ParseEnvironmentOptions(const char *env, int force) +{ + // + // Be sure log file is valid. + // + + if (logofs == NULL) + { + logofs = &cerr; + } + + // + // Be sure we have a parameters repository + // and a context to jump into because this + // can be called before creating the proxy. + // + + if (control == NULL) + { + control = new Control(); + } + + if (setjmp(context) == 1) + { + #ifdef TEST + *logofs << "Loop: Out of the long jump while parsing " + << "the environment options.\n" + << logofs_flush; + #endif + + return -1; + } + + if (force == 0 && parsedOptions == 1) + { + #ifdef TEST + *logofs << "Loop: Skipping a further parse of environment " + << "options string '" << (env != NULL ? env : "") + << "'.\n" << logofs_flush; + #endif + + return 1; + } + + if (env == NULL || *env == '\0') + { + #ifdef TEST + *logofs << "Loop: Nothing to do with empty environment " + << "options string '" << (env != NULL ? env : "") + << "'.\n" << logofs_flush; + #endif + + return 0; + } + + #ifdef TEST + *logofs << "Loop: Going to parse the environment options " + << "string '" << env << "'.\n" + << logofs_flush; + #endif + + parsedOptions = 1; + + // + // Copy the string passed as parameter + // because we need to modify it. + // + + char opts[DEFAULT_DISPLAY_OPTIONS_LENGTH]; + + #ifdef VALGRIND + + memset(opts, '\0', DEFAULT_DISPLAY_OPTIONS_LENGTH); + + #endif + + if (strlen(env) >= DEFAULT_DISPLAY_OPTIONS_LENGTH) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Environment options string '" << env + << "' exceeds length of " << DEFAULT_DISPLAY_OPTIONS_LENGTH + << " characters.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Environment options string '" << env + << "' exceeds length of " << DEFAULT_DISPLAY_OPTIONS_LENGTH + << " characters.\n"; + + return -1; + } + + strcpy(opts, env); + + char *nextOpts = opts; + + // + // Ensure that DISPLAY environment variable + // (roughly) follows the X convention for + // transport notation. + // + + if (strncasecmp(opts, "nx/nx,:", 7) == 0 || + strncasecmp(opts, "nx,:", 4) == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Parse error in options string '" + << opts << "' at 'nx,:'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Parse error in options string '" + << opts << "' at 'nx,:'.\n"; + + return -1; + } + else if (strncasecmp(opts, "nx/nx,", 6) == 0) + { + nextOpts += 6; + } + else if (strncasecmp(opts, "nx,", 3) == 0) + { + nextOpts += 3; + } + else if (strncasecmp(opts, "nx:", 3) == 0) + { + nextOpts += 3; + } + else if (force == 0) + { + #ifdef TEST + *logofs << "Loop: Ignoring host X server display string '" + << opts << "'.\n" << logofs_flush; + #endif + + return 0; + } + + // + // Save here the name of the options file and + // parse it after all the other options. + // + + char fileOptions[DEFAULT_STRING_LENGTH] = { 0 }; + + // + // The options string is intended to be a series + // of name/value tuples in the form name=value + // separated by the ',' character ended by a ':' + // followed by remote NX proxy port. + // + + char *name; + char *value; + + value = rindex(nextOpts, ':'); + + if (value != NULL) + { + char *check = value + 1; + + if (*check == '\0' || isdigit(*check) == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify NX port in string '" + << value << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify NX port in string '" + << value << "'.\n"; + + return -1; + } + + proxyPort = atoi(check); + + // + // Get rid of the port specification. + // + + *value = '\0'; + } + else if (proxyPort == DEFAULT_NX_PROXY_PORT && force == 0) + { + // + // Complain only if user didn't specify + // the port on the command line. + // + + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify NX port in string '" + << opts << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify NX port in string '" + << opts << "'.\n"; + + return -1; + } + + #ifdef TEST + *logofs << "Loop: Parsing options string '" + << nextOpts << "'.\n" << logofs_flush; + #endif + + // + // Now all the other optional parameters. + // + + name = strtok(nextOpts, "="); + + while (name) + { + value = strtok(NULL, ","); + + if (CheckArg("environment", name, value) < 0) + { + return -1; + } + + if (strcasecmp(name, "options") == 0) + { + strncpy(fileOptions, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "display") == 0) + { + strncpy(displayHost, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "link") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else if (ParseLinkOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify 'link' option in string '" + << value << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify 'link' option in string '" + << value << "'.\n"; + + return -1; + } + } + else if (strcasecmp(name, "limit") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else if (ParseBitrateOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify option 'limit' in string '" + << value << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify option 'limit' in string '" + << value << "'.\n"; + + return -1; + } + } + else if (strcasecmp(name, "type") == 0) + { + // + // Type of session, for example "desktop", + // "application", "windows", etc. + // + + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else + { + if (strcasecmp(value, "default") == 0) + { + *sessionType = '\0'; + } + else + { + strncpy(sessionType, value, DEFAULT_STRING_LENGTH - 1); + } + } + } + else if (strcasecmp(name, "listen") == 0) + { + if (*connectHost != '\0') + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't handle 'listen' and 'connect' parameters " + << "at the same time.\n" << logofs_flush; + + *logofs << "Loop: PANIC! Refusing 'listen' parameter with 'connect' being '" + << connectHost << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't handle 'listen' and 'connect' parameters " + << "at the same time.\n"; + + cerr << "Error" << ": Refusing 'listen' parameter with 'connect' being '" + << connectHost << "'.\n"; + + return -1; + } + + listenPort = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "accept") == 0) + { + if (*connectHost != '\0') + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't handle 'accept' and 'connect' parameters " + << "at the same time.\n" << logofs_flush; + + *logofs << "Loop: PANIC! Refusing 'accept' parameter with 'connect' being '" + << connectHost << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't handle 'accept' and 'connect' parameters " + << "at the same time.\n"; + + cerr << "Error" << ": Refusing 'accept' parameter with 'connect' being '" + << connectHost << "'.\n"; + + return -1; + } + + strncpy(acceptHost, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "connect") == 0) + { + if (*acceptHost != '\0') + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't handle 'connect' and 'accept' parameters " + << "at the same time.\n" << logofs_flush; + + *logofs << "Loop: PANIC! Refusing 'connect' parameter with 'accept' being '" + << acceptHost << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't handle 'connect' and 'accept' parameters " + << "at the same time.\n"; + + cerr << "Error" << ": Refusing 'connect' parameter with 'accept' being '" + << acceptHost << "'.\n"; + + return -1; + } + + strncpy(connectHost, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "port") == 0) + { + connectPort = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "retry") == 0) + { + control -> OptionProxyRetryConnect = ValidateArg("local", name, value); + control -> OptionServerRetryConnect = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "session") == 0) + { + strncpy(sessionFileName, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "errors") == 0) + { + // + // The old name of the parameter was 'log' + // but the default name for the file is + // 'errors' so it is more logical to use + // the same name. + // + + strncpy(errorsFileName, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "root") == 0) + { + strncpy(rootDir, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "id") == 0) + { + strncpy(sessionId, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "stats") == 0) + { + control -> EnableStatistics = 1; + + strncpy(statsFileName, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "cookie") == 0) + { + LowercaseArg("local", name, value); + + strncpy(authCookie, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "nodelay") == 0) + { + useNoDelay = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "policy") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else + { + usePolicy = ValidateArg("local", name, value); + } + } + else if (strcasecmp(name, "render") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else + { + useRender = ValidateArg("local", name, value); + } + } + else if (strcasecmp(name, "taint") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else + { + useTaint = ValidateArg("local", name, value); + } + } + else if (strcasecmp(name, "delta") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else + { + control -> LocalDeltaCompression = ValidateArg("local", name, value); + } + } + else if (strcasecmp(name, "data") == 0) + { + control -> LocalDataCompressionLevel = ValidateArg("local", name, value); + + if (control -> LocalDataCompressionLevel == 0) + { + control -> LocalDataCompression = 0; + } + else + { + control -> LocalDataCompression = 1; + } + } + else if (strcasecmp(name, "stream") == 0) + { + control -> LocalStreamCompressionLevel = ValidateArg("local", name, value); + + if (control -> LocalStreamCompressionLevel == 0) + { + control -> LocalStreamCompression = 0; + } + else + { + control -> LocalStreamCompression = 1; + } + } + else if (strcasecmp(name, "memory") == 0) + { + control -> LocalMemoryLevel = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "cache") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else if (ParseCacheOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify cache size for string '" + << value << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify cache size for string '" + << value << "'.\n"; + + return -1; + } + } + else if (strcasecmp(name, "images") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else if (ParseImagesOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify images cache size for string '" + << value << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify images cache size for string '" + << value << "'.\n"; + + return -1; + } + } + else if (strcasecmp(name, "shseg") == 0) + { + // + // The 'shmem' option is used by the agent, together + // with 'shpix' literal. We make the 'shseg' option + // specific to the proxy and use it to determine the + // size of the shared memory segment, or otherwise 0, + // if the use of the shared memory extension should + // not be enabled on the real X server. + // + + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else if (ParseShmemOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify size of shared memory " + << "segment in string '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify size of shared memory " + << "segment in string '" << value << "'.\n"; + + return -1; + } + } + else if (strcasecmp(name, "load") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else + { + control -> PersistentCacheEnableLoad = ValidateArg("local", name, value); + + if (control -> PersistentCacheEnableLoad > 0) + { + control -> PersistentCacheEnableLoad = 1; + } + else + { + if (control -> PersistentCacheName != NULL) + { + delete [] control -> PersistentCacheName; + } + + control -> PersistentCacheName = NULL; + + control -> PersistentCacheEnableLoad = 0; + } + } + } + else if (strcasecmp(name, "save") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else + { + control -> PersistentCacheEnableSave = ValidateArg("local", name, value); + + if (control -> PersistentCacheEnableSave > 0) + { + control -> PersistentCacheEnableSave = 1; + } + else + { + if (control -> PersistentCacheName != NULL) + { + delete [] control -> PersistentCacheName; + } + + control -> PersistentCacheName = NULL; + + control -> PersistentCacheEnableSave = 0; + } + } + } + else if (strcasecmp(name, "cups") == 0) + { + cupsPort = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "sync") == 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! No 'sync' channel in current version. " + << "Assuming 'cups' channel.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": No 'sync' channel in current version. " + << "Assuming 'cups' channel.\n"; + + cupsPort = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "keybd") == 0 || + strcasecmp(name, "aux") == 0) + { + auxPort = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "samba") == 0 || + strcasecmp(name, "smb") == 0) + { + smbPort = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "media") == 0) + { + mediaPort = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "http") == 0) + { + httpPort = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "font") == 0) + { + strncpy(fontPort, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "slave") == 0) + { + slavePort = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "mask") == 0) + { + control -> ChannelMask = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "timeout") == 0) + { + int timeout = ValidateArg("local", name, value); + + if (timeout == 0) + { + #ifdef TEST + *logofs << "Loop: Disabling timeout on broken " + << "proxy connection.\n" << logofs_flush; + #endif + + control -> ProxyTimeout = 0; + } + else + { + control -> ProxyTimeout = timeout * 1000; + } + } + else if (strcasecmp(name, "cleanup") == 0) + { + int cleanup = ValidateArg("local", name, value); + + if (cleanup == 0) + { + #ifdef TEST + *logofs << "Loop: Disabling grace timeout on " + << "proxy shutdown.\n" << logofs_flush; + #endif + + control -> CleanupTimeout = 0; + } + else + { + control -> CleanupTimeout = cleanup * 1000; + } + } + else if (strcasecmp(name, "pack") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else if (ParsePackOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify pack method for string '" + << value << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify pack method for string '" + << value << "'.\n"; + + return -1; + } + } + else if (strcasecmp(name, "core") == 0) + { + control -> EnableCoreDumpOnAbort = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "kill") == 0) + { + if (control -> KillDaemonOnShutdownNumber < + control -> KillDaemonOnShutdownLimit) + { + #ifdef TEST + *logofs << "Loop: WARNING! Adding process with pid '" + << ValidateArg("local", name, value) << " to the " + << "daemons to kill at shutdown.\n" + << logofs_flush; + #endif + + control -> KillDaemonOnShutdown[control -> + KillDaemonOnShutdownNumber] = + ValidateArg("local", name, value); + + control -> KillDaemonOnShutdownNumber++; + } + else + { + #ifdef WARNING + *logofs << "Loop: WARNING! Number of daemons to kill " + << "at shutdown exceeded.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Number of daemons to kill " + << "at shutdown exceeded.\n"; + } + } + else if (strcasecmp(name, "strict") == 0) + { + if (control -> ProxyMode == proxy_server) + { + PrintOptionIgnored("local", name, value); + } + else + { + useStrict = ValidateArg("local", name, value); + } + } + else if (strcasecmp(name, "encryption") == 0) + { + useEncryption = ValidateArg("local", name, value); + } + else if (strcasecmp(name, "product") == 0) + { + strncpy(productName, value, DEFAULT_STRING_LENGTH - 1); + } + else if (strcasecmp(name, "rootless") == 0 || + strcasecmp(name, "geometry") == 0 || + strcasecmp(name, "resize") == 0 || + strcasecmp(name, "fullscreen") == 0 || + strcasecmp(name, "keyboard") == 0 || + strcasecmp(name, "clipboard") == 0 || + strcasecmp(name, "streaming") == 0 || + strcasecmp(name, "backingstore") == 0) + { + #ifdef DEBUG + *logofs << "Loop: Ignoring agent option '" << name + << "' with value '" << value << "'.\n" + << logofs_flush; + #endif + } + else if (strcasecmp(name, "composite") == 0 || + strcasecmp(name, "shmem") == 0 || + strcasecmp(name, "shpix") == 0 || + strcasecmp(name, "kbtype") == 0 || + strcasecmp(name, "client") == 0 || + strcasecmp(name, "shadow") == 0 || + strcasecmp(name, "shadowuid") == 0 || + strcasecmp(name, "shadowmode") == 0 || + strcasecmp(name, "clients") == 0) + { + #ifdef DEBUG + *logofs << "Loop: Ignoring agent option '" << name + << "' with value '" << value << "'.\n" + << logofs_flush; + #endif + } + else if (strcasecmp(name, "defer") == 0 || + strcasecmp(name, "tile") == 0 || + strcasecmp(name, "menu") == 0) + { + #ifdef DEBUG + *logofs << "Loop: Ignoring agent option '" << name + << "' with value '" << value << "'.\n" + << logofs_flush; + #endif + } + else + { + #ifdef WARNING + *logofs << "Loop: WARNING! Ignoring unknown option '" + << name << "' with value '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Ignoring unknown option '" + << name << "' with value '" << value << "'.\n"; + } + + name = strtok(NULL, "="); + + } // End of while (name) ... + + #ifdef TEST + *logofs << "Loop: Completed parsing of string '" + << env << "'.\n" << logofs_flush; + #endif + + if (*fileOptions != '\0') + { + if (strcmp(fileOptions, optionsFileName) != 0) + { + #ifdef TEST + *logofs << "Loop: Reading options from '" << fileOptions + << "'.\n" << logofs_flush; + #endif + + if (ParseFileOptions(fileOptions) < 0) + { + return -1; + } + } + #ifdef WARNING + else + { + *logofs << "Loop: WARNING! Name of the options file " + << "specified multiple times. Not parsing " + << "again.\n" << logofs_flush; + } + #endif + + if (*optionsFileName == '\0') + { + strncpy(optionsFileName, value, DEFAULT_STRING_LENGTH - 1); + + #ifdef TEST + *logofs << "Loop: Assuming name of options file '" + << optionsFileName << "'.\n" + << logofs_flush; + #endif + } + } + + // + // If port where proxy is acting as an X server + // was not specified assume the same port where + // proxy is listening for the remote peer. + // + + if (xPort == DEFAULT_NX_X_PORT) + { + xPort = proxyPort; + } + + return 1; +} + +// +// Parse the command line options passed by user when +// running proxy in stand alone mode. Note that passing +// parameters this way is strongly discouraged. These +// command line switch can change (and they do often). +// Please, use the form "option=value" instead and set +// the DISPLAY environment variable. +// + +int ParseCommandLineOptions(int argc, const char **argv) +{ + // + // Be sure log file is valid. + // + + if (logofs == NULL) + { + logofs = &cerr; + } + + if (setjmp(context) == 1) + { + #ifdef TEST + *logofs << "Loop: Out of the long jump while parsing " + << "the command line options.\n" + << logofs_flush; + #endif + + return -1; + } + + // + // Be sure we have a parameters repository + // + + if (control == NULL) + { + control = new Control(); + } + + if (parsedCommand == 1) + { + #ifdef TEST + *logofs << "Loop: Skipping a further parse of command line options.\n" + << logofs_flush; + #endif + + return 1; + } + + #ifdef TEST + *logofs << "Loop: Going to parse the command line options.\n" + << logofs_flush; + #endif + + parsedCommand = 1; + + // + // Print out arguments. + // + + #ifdef TEST + + *logofs << "Loop: Argc is " << argc << ".\n" << logofs_flush; + + for (int argi = 0; argi < argc; argi++) + { + *logofs << "Loop: Argv[" << argi << "] is " << argv[argi] + << ".\n" << logofs_flush; + } + + #endif + + // + // Shall use getopt here. + // + + for (int argi = 1; argi < argc; argi++) + { + const char *nextArg = argv[argi]; + + if (*nextArg == '-') + { + switch (*(nextArg + 1)) + { + case 'h': + { + PrintUsageInfo(nextArg, 0); + + return -1; + } + case 'C': + { + // + // Start proxy in CLIENT mode. + // + + if (WE_SET_PROXY_MODE == 0) + { + #ifdef TEST + *logofs << "Loop: Setting local proxy mode to proxy_client.\n" + << logofs_flush; + #endif + + control -> ProxyMode = proxy_client; + } + else if (control -> ProxyMode != proxy_client) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't redefine local proxy to " + << "client mode.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't redefine local proxy to " + << "client mode.\n"; + + return -1; + } + + break; + } + case 'S': + { + // + // Start proxy in SERVER mode. + // + + if (WE_SET_PROXY_MODE == 0) + { + #ifdef TEST + *logofs << "Loop: Setting local proxy mode to proxy_server.\n" + << logofs_flush; + #endif + + control -> ProxyMode = proxy_server; + } + else if (control -> ProxyMode != proxy_server) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't redefine local proxy to " + << "server mode.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't redefine local proxy to " + << "server mode.\n"; + + return -1; + } + + break; + } + case 'v': + { + PrintVersionInfo(); + + return -1; + } + default: + { + PrintUsageInfo(nextArg, 1); + + // + // Function GetArg() is not used anymore. + // Add a dummy call to avoid the warning. + // + + if (0) + { + GetArg(argi, argc, argv); + } + + return -1; + } + } + } + else + { + if (nextArg) + { + // + // Try to parse the option as a remote host:port + // specification as in 'localhost:8'. Such a + // parameter can be specified at the end of the + // command line at the connecting side. + // + + if (ParseHostOption(nextArg, connectHost, connectPort) > 0) + { + // + // Assume port is at a proxied display offset. + // + + proxyPort = connectPort; + + connectPort += DEFAULT_NX_PROXY_PORT_OFFSET; + } + else if (ParseEnvironmentOptions(nextArg, 1) < 0) + { + return -1; + } + } + } + } + + return 1; +} + +// +// Set the variable to the values of host and +// port where this proxy is going to hook to +// an existing proxy. +// + +int ParseBindOptions(char **host, int *port) +{ + if (*bindHost != '\0') + { + *host = bindHost; + *port = bindPort; + + return 1; + } + else + { + return 0; + } +} + +// +// Read options from file and merge with environment. +// + +int ParseFileOptions(const char *file) +{ + char *fileName; + + if (*file != '/' && *file != '.') + { + char *filePath = GetSessionPath(); + + if (filePath == NULL) + { + cerr << "Error" << ": Cannot determine directory for NX option file.\n"; + + HandleCleanup(); + } + + fileName = new char[strlen(filePath) + strlen("/") + + strlen(file) + 1]; + + strcpy(fileName, filePath); + + strcat(fileName, "/"); + strcat(fileName, file); + + delete [] filePath; + } + else + { + fileName = new char[strlen(file) + 1]; + + strcpy(fileName, file); + } + + #ifdef TEST + *logofs << "Loop: Going to read options from file '" + << fileName << "'.\n" << logofs_flush; + #endif + + FILE *filePtr = fopen(fileName, "r"); + + if (filePtr == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't open options file '" << fileName + << "'. Error is " << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't open options file '" << fileName + << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"; + + delete [] fileName; + + return -1; + } + + char options[DEFAULT_DISPLAY_OPTIONS_LENGTH]; + + #ifdef VALGRIND + + memset(options, '\0', DEFAULT_DISPLAY_OPTIONS_LENGTH); + + #endif + + if (fgets(options, DEFAULT_DISPLAY_OPTIONS_LENGTH, filePtr) == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't read options from file '" << fileName + << "'. Error is " << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't read options from file '" << fileName + << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"; + + fclose(filePtr); + + delete [] fileName; + + return -1; + } + + fclose(filePtr); + + // + // Purge the newline and the other non- + // printable characters in the string. + // + + char *next = options; + + while (*next != '\0') + { + if (isprint(*next) == 0) + { + *next = '\0'; + } + + next++; + } + + #ifdef TEST + *logofs << "Loop: Read options '" << options << "' from file '" + << fileName << "'.\n" << logofs_flush; + #endif + + if (ParseEnvironmentOptions(options, 1) < 0) + { + delete [] fileName; + + return -1; + } + + delete [] fileName; + + return 1; +} + +// +// Parse the option string passed from the +// remote proxy at startup. +// + +int ParseRemoteOptions(char *opts) +{ + #ifdef TEST + *logofs << "Loop: Going to parse the remote options " + << "string '" << opts << "'.\n" + << logofs_flush; + #endif + + char *name; + char *value; + + // + // The options string is intended to be a series + // of name/value tuples in the form name=value + // separated by the ',' character. + // + + int hasCookie = 0; + int hasLink = 0; + int hasPack = 0; + int hasCache = 0; + int hasImages = 0; + int hasDelta = 0; + int hasStream = 0; + int hasData = 0; + int hasLimit = 0; + int hasRender = 0; + int hasTaint = 0; + int hasType = 0; + int hasStrict = 0; + int hasShseg = 0; + + // + // Get rid of the terminating space. + // + + if (*(opts + strlen(opts) - 1) == ' ') + { + *(opts + strlen(opts) - 1) = '\0'; + } + + name = strtok(opts, "="); + + while (name) + { + value = strtok(NULL, ","); + + if (CheckArg("remote", name, value) < 0) + { + return -1; + } + + if (strcasecmp(name, "cookie") == 0) + { + if (WE_PROVIDE_CREDENTIALS) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Ignoring remote option 'cookie' " + << "with value '" << value << "' when initiating " + << "connection.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Ignoring remote option 'cookie' " + << "with value '" << value << "' when initiating " + << "connection.\n"; + } + else if (strncasecmp(authCookie, value, strlen(authCookie)) != 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Authentication cookie '" << value + << "' doesn't match '" << authCookie << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Authentication cookie '" << value + << "' doesn't match '" << authCookie << "'.\n"; + + return -1; + } + + hasCookie = 1; + } + else if (strcasecmp(name, "link") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + if (*linkSpeedName != '\0' && strcasecmp(linkSpeedName, value) != 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Overriding option 'link' " + << "with new value '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Overriding option 'link' " + << "with new value '" << value << "'.\n"; + } + + if (ParseLinkOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify remote 'link' " + << "option in string '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify remote 'link' " + << "option in string '" << value << "'.\n"; + + return -1; + } + } + + hasLink = 1; + } + else if (strcasecmp(name, "pack") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + if (*packMethodName != '\0' && strcasecmp(packMethodName, value) != 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Overriding option 'pack' " + << "with remote value '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Overriding option 'pack' " + << "with remote value '" << value << "'.\n"; + } + + if (ParsePackOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid pack option '" + << value << "' requested by remote.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid pack option '" + << value << "' requested by remote.\n"; + + return -1; + } + } + + hasPack = 1; + } + else if (strcasecmp(name, "cache") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + // + // Cache size is sent as a hint of how much memory + // the remote proxy is going to consume. A very low + // powered thin client could choose to refuse the + // connection. + // + + if (ParseCacheOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify remote 'cache' " + << "option in string '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify remote 'cache' " + << "option in string '" << value << "'.\n"; + + return -1; + } + } + + hasCache = 1; + } + else if (strcasecmp(name, "images") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + // + // Images cache size is sent as a hint. + // There is no obbligation for the local + // proxy to use the persistent cache. + // + + if (ParseImagesOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify remote 'images' " + << "option in string '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify remote 'images' " + << "option in string '" << value << "'.\n"; + + return -1; + } + } + + hasImages = 1; + } + else if (strcasecmp(name, "limit") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + if (*bitrateLimitName != '\0' && + strcasecmp(bitrateLimitName, value) != 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Overriding option 'limit' " + << "with new value '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Overriding option 'limit' " + << "with new value '" << value << "'.\n"; + } + + if (ParseBitrateOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify 'limit' " + << "option in string '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify 'limit' " + << "option in string '" << value << "'.\n"; + + return -1; + } + } + + hasLimit = 1; + } + else if (strcasecmp(name, "render") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + useRender = ValidateArg("remote", name, value); + } + + hasRender = 1; + } + else if (strcasecmp(name, "taint") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + useTaint = ValidateArg("remote", name, value); + } + + hasTaint = 1; + } + else if (strcasecmp(name, "type") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + if (strcasecmp(value, "default") == 0) + { + *sessionType = '\0'; + } + else + { + strncpy(sessionType, value, DEFAULT_STRING_LENGTH - 1); + } + } + + hasType = 1; + } + else if (strcasecmp(name, "strict") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + useStrict = ValidateArg("remote", name, value); + } + + hasStrict = 1; + } + else if (strcasecmp(name, "shseg") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else if (ParseShmemOption(value) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't identify size of shared memory " + << "segment in string '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify size of shared memory " + << "segment in string '" << value << "'.\n"; + + return -1; + } + + hasShseg = 1; + } + else if (strcasecmp(name, "delta") == 0) + { + if (control -> ProxyMode == proxy_client) + { + PrintOptionIgnored("remote", name, value); + } + else + { + control -> RemoteDeltaCompression = ValidateArg("remote", name, value); + + // + // Follow for delta compression the + // same settings as the client proxy. + // + + control -> LocalDeltaCompression = control -> RemoteDeltaCompression; + } + + hasDelta = 1; + } + else if (strcasecmp(name, "stream") == 0) + { + // + // If remote side didn't choose its own + // stream compression level then assume + // local settings. + // + + if (strcasecmp(value, "default") == 0) + { + // + // This applies only at client side. + // + + control -> RemoteStreamCompression = + control -> LocalStreamCompression; + + control -> RemoteStreamCompressionLevel = + control -> LocalStreamCompressionLevel; + } + else + { + control -> RemoteStreamCompressionLevel = ValidateArg("remote", name, value); + + if (control -> RemoteStreamCompressionLevel > 0) + { + control -> RemoteStreamCompression = 1; + } + else + { + control -> RemoteStreamCompression = 0; + } + + if (control -> LocalStreamCompressionLevel < 0) + { + control -> LocalStreamCompressionLevel = ValidateArg("remote", name, value); + + if (control -> LocalStreamCompressionLevel > 0) + { + control -> LocalStreamCompression = 1; + } + else + { + control -> LocalStreamCompression = 0; + } + } + } + + hasStream = 1; + } + else if (strcasecmp(name, "data") == 0) + { + // + // Apply the same to data compression level. + // + + if (strcasecmp(value, "default") == 0) + { + control -> RemoteDataCompression = + control -> LocalDataCompression; + + control -> RemoteDataCompressionLevel = + control -> LocalDataCompressionLevel; + } + else + { + control -> RemoteDataCompressionLevel = ValidateArg("remote", name, value); + + if (control -> RemoteDataCompressionLevel > 0) + { + control -> RemoteDataCompression = 1; + } + else + { + control -> RemoteDataCompression = 0; + } + + if (control -> LocalDataCompressionLevel < 0) + { + control -> LocalDataCompressionLevel = ValidateArg("remote", name, value); + + if (control -> LocalDataCompressionLevel > 0) + { + control -> LocalDataCompression = 1; + } + else + { + control -> LocalDataCompression = 0; + } + } + } + + hasData = 1; + } + else if (strcasecmp(name, "flush") == 0) + { + // + // This option has no effect in recent + // versions. + // + + #ifdef DEBUG + *logofs << "Loop: Ignoring obsolete remote option '" + << name << "' with value '" << value + << "'.\n" << logofs_flush; + #endif + } + else + { + #ifdef WARNING + *logofs << "Loop: WARNING! Ignoring unknown remote option '" + << name << "' with value '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Ignoring unknown remote option '" + << name << "' with value '" << value << "'.\n"; + } + + name = strtok(NULL, "="); + + } // End of while (name) ... + + // + // If we are client side, we need remote 'stream' + // and 'data' options. If we are server, we need + // all the above plus 'link' and some others. + // + + char missing[DEFAULT_STRING_LENGTH]; + + *missing = '\0'; + + if (control -> ProxyMode == proxy_client) + { + if (hasStream == 0) + { + strcpy(missing, "stream"); + } + else if (hasData == 0) + { + strcpy(missing, "data"); + } + } + else + { + // + // Don't complain if the optional 'flush', + // 'render' and 'taint' options are not + // provided. + // + + if (hasLink == 0) + { + strcpy(missing, "link"); + } + else if (hasCache == 0) + { + strcpy(missing, "cache"); + } + else if (hasPack == 0) + { + strcpy(missing, "pack"); + } + else if (hasDelta == 0) + { + strcpy(missing, "delta"); + } + else if (hasStream == 0) + { + strcpy(missing, "stream"); + } + else if (hasData == 0) + { + strcpy(missing, "data"); + } + else if (hasType == 0) + { + strcpy(missing, "type"); + } + else if (hasImages == 0) + { + strcpy(missing, "images"); + } + } + + if (WE_PROVIDE_CREDENTIALS == 0) + { + // + // Can be that user doesn't have requested to + // check the authorization cookie provided by + // the connecting peer. + // + + if (hasCookie == 0 && *authCookie != '\0') + { + strcpy(missing, "cookie"); + } + } + + if (*missing != '\0') + { + #ifdef PANIC + *logofs << "Loop: PANIC! The remote peer didn't specify the option '" + << missing << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": The remote peer didn't specify the option '" + << missing << "'.\n"; + + return -1; + } + + return 1; +} + +// +// Parse the cookie provided by the NX proxy +// connection forwarder. +// + +int ParseForwarderOptions(char *opts) +{ + #ifdef TEST + *logofs << "Loop: Going to parse the forwarder options " + << "string '" << opts << "'.\n" + << logofs_flush; + #endif + + char *name; + char *value; + + int hasCookie = 0; + + // + // Get rid of the terminating space. + // + + if (*(opts + strlen(opts) - 1) == ' ') + { + *(opts + strlen(opts) - 1) = '\0'; + } + + name = strtok(opts, "="); + + while (name) + { + value = strtok(NULL, ","); + + if (CheckArg("forwarder", name, value) < 0) + { + return -1; + } + + if (strcasecmp(name, "cookie") == 0) + { + if (strncasecmp(authCookie, value, strlen(authCookie)) != 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! The NX forwarder cookie '" << value + << "' doesn't match '" << authCookie << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": The NX forwarder cookie '" << value + << "' doesn't match '" << authCookie << "'.\n"; + + return -1; + } + + hasCookie = 1; + } + else + { + #ifdef WARNING + *logofs << "Loop: WARNING! Ignoring unknown forwarder option '" + << name << "' with value '" << value << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Ignoring unknown forwarder option '" + << name << "' with value '" << value << "'.\n"; + } + + name = strtok(NULL, "="); + + } // End of while (name) ... + + if (hasCookie == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! The NX forwarder didn't provide " + << "the authentication cookie.\n" << logofs_flush; + #endif + + cerr << "Error" << ": The NX forwarder didn't provide " + << "the authentication cookie.\n"; + + return -1; + } + + return 1; +} + +int SetCore() +{ + #ifdef COREDUMPS + + rlimit rlim; + + if (getrlimit(RLIMIT_CORE, &rlim)) + { + #ifdef TEST + *logofs << "Cannot read RLIMIT_CORE. Error is '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + return -1; + } + + if (rlim.rlim_cur < rlim.rlim_max) + { + rlim.rlim_cur = rlim.rlim_max; + + if (setrlimit(RLIMIT_CORE, &rlim)) + { + #ifdef TEST + *logofs << "Loop: Cannot read RLIMIT_CORE. Error is '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + return -2; + } + } + + #ifdef TEST + *logofs << "Loop: Set RLIMIT_CORE to "<< rlim.rlim_max + << ".\n" << logofs_flush; + #endif + + #endif // #ifdef COREDUMPS + + return 1; +} + +char *GetLastCache(char *listBuffer, const char *searchPath) +{ + if (listBuffer == NULL || searchPath == NULL || + strncmp(listBuffer, "cachelist=", strlen("cachelist=")) != 0) + { + #ifdef TEST + *logofs << "Loop: Invalid parameters '" << listBuffer << "' and '" + << (searchPath != NULL ? searchPath : "") + << "'. Can't select any cache.\n" << logofs_flush; + #endif + + return NULL; + } + + char *selectedName = new char[MD5_LENGTH * 2 + 3]; + + *selectedName = '\0'; + + const char *localPrefix; + const char *remotePrefix; + + if (control -> ProxyMode == proxy_client) + { + localPrefix = "C-"; + remotePrefix = "S-"; + } + else + { + localPrefix = "S-"; + remotePrefix = "C-"; + } + + // + // Get rid of prefix. + // + + listBuffer += strlen("cachelist="); + + char *fileName; + + fileName = strtok(listBuffer, ","); + + // + // It is "/path/to/file" + "/" + "C-" + 32 + "\0". + // + + char fullPath[strlen(searchPath) + MD5_LENGTH * 2 + 4]; + + time_t selectedTime = 0; + + struct stat fileStat; + + while (fileName) + { + if (strncmp(fileName, "none", strlen("none")) == 0) + { + #ifdef TEST + *logofs << "Loop: No cache files seem to be available.\n" + << logofs_flush; + #endif + + delete [] selectedName; + + return NULL; + } + else if (strlen(fileName) != MD5_LENGTH * 2 + 2 || + strncmp(fileName, remotePrefix, 2) != 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Bad cache file name '" + << fileName << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Bad cache file name '" + << fileName << "'.\n"; + + delete [] selectedName; + + HandleCleanup(); + } + + #ifdef TEST + *logofs << "Loop: Parsing remote cache name '" + << fileName << "'.\n" << logofs_flush; + #endif + + // + // Prefix, received as "S-", becomes + // "C-" and viceversa. + // + + *fileName = *localPrefix; + + strcpy(fullPath, searchPath); + strcat(fullPath, "/"); + strcat(fullPath, fileName); + + if (stat(fullPath, &fileStat) == 0) + { + #ifdef TEST + *logofs << "Loop: Found a matching cache '" + << fullPath << "'.\n" << logofs_flush; + #endif + + if (fileStat.st_mtime >= selectedTime) + { + strcpy(selectedName, fileName); + + selectedTime = fileStat.st_mtime; + } + } + #ifdef TEST + else + { + *logofs << "Loop: Can't get stats of file '" + << fullPath << "'.\n" << logofs_flush; + } + #endif + + fileName = strtok(NULL, ","); + } + + if (*selectedName != '\0') + { + return selectedName; + } + else + { + delete [] selectedName; + + return NULL; + } +} + +char *GetTempPath() +{ + if (*tempDir == '\0') + { + // + // Check the NX_TEMP environment, first, + // then the TEMP variable. + // + + const char *tempEnv = getenv("NX_TEMP"); + + if (tempEnv == NULL || *tempEnv == '\0') + { + #ifdef TEST + *logofs << "Loop: WARNING! No environment for NX_TEMP.\n" + << logofs_flush; + #endif + + tempEnv = getenv("TEMP"); + + if (tempEnv == NULL || *tempEnv == '\0') + { + #ifdef TEST + *logofs << "Loop: WARNING! No environment for TEMP.\n" + << logofs_flush; + #endif + + tempEnv = "/tmp"; + } + } + + if (strlen(tempEnv) > DEFAULT_STRING_LENGTH - 1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid value for the NX " + << "temporary directory '" << tempEnv + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid value for the NX " + << "temporary directory '" << tempEnv + << "'.\n"; + + HandleCleanup(); + } + + strcpy(tempDir, tempEnv); + + #ifdef TEST + *logofs << "Loop: Assuming temporary NX directory '" + << tempDir << "'.\n" << logofs_flush; + #endif + } + + char *tempPath = new char[strlen(tempDir) + 1]; + + if (tempPath == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't allocate memory " + << "for the temp path.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory " + << "for the temp path.\n"; + + HandleCleanup(); + } + + strcpy(tempPath, tempDir); + + return tempPath; +} + +char *GetClientPath() +{ + if (*clientDir == '\0') + { + // + // Check the NX_CLIENT environment. + // + + const char *clientEnv = getenv("NX_CLIENT"); + + if (clientEnv == NULL || *clientEnv == '\0') + { + #ifdef TEST + *logofs << "Loop: WARNING! No environment for NX_CLIENT.\n" + << logofs_flush; + #endif + + // + // Try to guess the location of the client. + // + + clientEnv = "/usr/NX/bin/nxclient"; + + #ifdef __APPLE__ + + clientEnv = "/Applications/NX Client for OSX.app/Contents/MacOS/nxclient"; + + #endif + + #ifdef __CYGWIN32__ + + clientEnv = "C:\\Program Files\\NX Client for Windows\\nxclient"; + + #endif + } + + if (strlen(clientEnv) > DEFAULT_STRING_LENGTH - 1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid value for the NX " + << "client directory '" << clientEnv + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid value for the NX " + << "client directory '" << clientEnv + << "'.\n"; + + HandleCleanup(); + } + + strcpy(clientDir, clientEnv); + + #ifdef TEST + *logofs << "Loop: Assuming NX client location '" + << clientDir << "'.\n" << logofs_flush; + #endif + } + + char *clientPath = new char[strlen(clientDir) + 1]; + + if (clientPath == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't allocate memory " + << "for the client path.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory " + << "for the client path.\n"; + + HandleCleanup(); + } + + strcpy(clientPath, clientDir); + + return clientPath; +} + +char *GetSystemPath() +{ + if (*systemDir == '\0') + { + // + // Check the NX_SYSTEM environment. + // + + const char *systemEnv = getenv("NX_SYSTEM"); + + if (systemEnv == NULL || *systemEnv == '\0') + { + #ifdef TEST + *logofs << "Loop: WARNING! No environment for NX_SYSTEM.\n" + << logofs_flush; + #endif + + systemEnv = "/usr/NX"; + } + + if (strlen(systemEnv) > DEFAULT_STRING_LENGTH - 1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid value for the NX " + << "system directory '" << systemEnv + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid value for the NX " + << "system directory '" << systemEnv + << "'.\n"; + + HandleCleanup(); + } + + strcpy(systemDir, systemEnv); + + #ifdef TEST + *logofs << "Loop: Assuming system NX directory '" + << systemDir << "'.\n" << logofs_flush; + #endif + } + + char *systemPath = new char[strlen(systemDir) + 1]; + + if (systemPath == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't allocate memory " + << "for the system path.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory " + << "for the system path.\n"; + + HandleCleanup(); + } + + strcpy(systemPath, systemDir); + + return systemPath; +} + +char *GetHomePath() +{ + if (*homeDir == '\0') + { + // + // Check the NX_HOME environment. + // + + const char *homeEnv = getenv("NX_HOME"); + + if (homeEnv == NULL || *homeEnv == '\0') + { + #ifdef TEST + *logofs << "Loop: WARNING! No environment for NX_HOME.\n" + << logofs_flush; + #endif + + homeEnv = getenv("HOME"); + + if (homeEnv == NULL || *homeEnv == '\0') + { + #ifdef PANIC + *logofs << "Loop: PANIC! No environment for HOME.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": No environment for HOME.\n"; + + HandleCleanup(); + } + } + + if (strlen(homeEnv) > DEFAULT_STRING_LENGTH - 1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid value for the NX " + << "home directory '" << homeEnv + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid value for the NX " + << "home directory '" << homeEnv + << "'.\n"; + + HandleCleanup(); + } + + strcpy(homeDir, homeEnv); + + #ifdef TEST + *logofs << "Loop: Assuming NX user's home directory '" + << homeDir << "'.\n" << logofs_flush; + #endif + } + + char *homePath = new char[strlen(homeDir) + 1]; + + if (homePath == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't allocate memory " + << "for the home path.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory " + << "for the home path.\n"; + + HandleCleanup(); + } + + strcpy(homePath, homeDir); + + return homePath; +} + +char *GetRootPath() +{ + if (*rootDir == '\0') + { + // + // Check the NX_ROOT environment. + // + + const char *rootEnv = getenv("NX_ROOT"); + + if (rootEnv == NULL || *rootEnv == '\0') + { + #ifdef TEST + *logofs << "Loop: WARNING! No environment for NX_ROOT.\n" + << logofs_flush; + #endif + + // + // We will determine the root NX directory + // based on the NX_HOME or HOME directory + // settings. + // + + const char *homeEnv = GetHomePath(); + + if (strlen(homeEnv) > DEFAULT_STRING_LENGTH - + strlen("/.nx") - 1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid value for the NX " + << "home directory '" << homeEnv + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid value for the NX " + << "home directory '" << homeEnv + << "'.\n"; + + HandleCleanup(); + } + + #ifdef TEST + *logofs << "Loop: Assuming NX root directory in " + << "the user's home '" << homeEnv + << "'.\n" << logofs_flush; + #endif + + strcpy(rootDir, homeEnv); + strcat(rootDir, "/.nx"); + + delete [] homeEnv; + + // + // Create the NX root directory. + // + + struct stat dirStat; + + if ((stat(rootDir, &dirStat) == -1) && (EGET() == ENOENT)) + { + if (mkdir(rootDir, 0700) < 0 && (EGET() != EEXIST)) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't create directory '" + << rootDir << ". Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't create directory '" + << rootDir << ". Error is " << EGET() << " '" + << ESTR() << "'.\n"; + + HandleCleanup(); + } + } + } + else + { + if (strlen(rootEnv) > DEFAULT_STRING_LENGTH - 1) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid value for the NX " + << "root directory '" << rootEnv + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid value for the NX " + << "root directory '" << rootEnv + << "'.\n"; + + HandleCleanup(); + } + + strcpy(rootDir, rootEnv); + } + + #ifdef TEST + *logofs << "Loop: Assuming NX root directory '" + << rootDir << "'.\n" << logofs_flush; + #endif + } + + char *rootPath = new char[strlen(rootDir) + 1]; + + if (rootPath == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't allocate memory " + << "for the root path.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory " + << "for the root path.\n"; + + HandleCleanup(); + } + + strcpy(rootPath, rootDir); + + return rootPath; +} + +char *GetCachePath() +{ + char *rootPath = GetRootPath(); + + char *cachePath; + + if (*sessionType != '\0') + { + cachePath = new char[strlen(rootPath) + strlen("/cache-") + + strlen(sessionType) + 1]; + } + else + { + cachePath = new char[strlen(rootPath) + strlen("/cache") + 1]; + } + + strcpy(cachePath, rootPath); + + if (*sessionType != '\0') + { + strcat(cachePath, "/cache-"); + + strcat(cachePath, sessionType); + } + else + { + strcat(cachePath, "/cache"); + } + + // + // Create the cache directory if needed. + // + + struct stat dirStat; + + if ((stat(cachePath, &dirStat) == -1) && (EGET() == ENOENT)) + { + if (mkdir(cachePath, 0700) < 0 && (EGET() != EEXIST)) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't create directory '" << cachePath + << ". Error is " << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't create directory '" << cachePath + << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; + + delete [] rootPath; + delete [] cachePath; + + return NULL; + } + } + + delete [] rootPath; + + return cachePath; +} + +char *GetImagesPath() +{ + char *rootPath = GetRootPath(); + + char *imagesPath = new char[strlen(rootPath) + strlen("/images") + 1]; + + strcpy(imagesPath, rootPath); + + strcat(imagesPath, "/images"); + + // + // Create the cache directory if needed. + // + + struct stat dirStat; + + if ((stat(imagesPath, &dirStat) == -1) && (EGET() == ENOENT)) + { + if (mkdir(imagesPath, 0700) < 0 && (EGET() != EEXIST)) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't create directory '" << imagesPath + << ". Error is " << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't create directory '" << imagesPath + << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; + + delete [] rootPath; + delete [] imagesPath; + + return NULL; + } + } + + // + // Create 16 directories in the path to + // hold the images whose name begins with + // the corresponding hexadecimal digit. + // + + char *digitPath = new char[strlen(imagesPath) + 5]; + + strcpy(digitPath, imagesPath); + + // + // Image paths have format "[path][/I-c][\0]", + // where c is the first digit of the checksum. + // + + for (char digit = 0; digit < 16; digit++) + { + sprintf(digitPath + strlen(imagesPath), "/I-%01X", digit); + + if ((stat(digitPath, &dirStat) == -1) && (EGET() == ENOENT)) + { + if (mkdir(digitPath, 0700) < 0 && (EGET() != EEXIST)) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't create directory '" << digitPath + << ". Error is " << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't create directory '" << digitPath + << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; + + delete [] rootPath; + delete [] imagesPath; + delete [] digitPath; + + return NULL; + } + } + } + + delete [] rootPath; + delete [] digitPath; + + return imagesPath; +} + +char *GetSessionPath() +{ + if (*sessionDir == '\0') + { + char *rootPath = GetRootPath(); + + strcpy(sessionDir, rootPath); + + if (control -> ProxyMode == proxy_client) + { + strcat(sessionDir, "/C-"); + } + else + { + strcat(sessionDir, "/S-"); + } + + if (*sessionId == '\0') + { + char port[DEFAULT_STRING_LENGTH]; + + sprintf(port, "%d", proxyPort); + + strcpy(sessionId, port); + } + + strcat(sessionDir, sessionId); + + struct stat dirStat; + + if ((stat(sessionDir, &dirStat) == -1) && (EGET() == ENOENT)) + { + if (mkdir(sessionDir, 0700) < 0 && (EGET() != EEXIST)) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't create directory '" << sessionDir + << ". Error is " << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't create directory '" << sessionDir + << ". Error is " << EGET() << " '" << ESTR() << "'.\n"; + + delete [] rootPath; + + return NULL; + } + } + + #ifdef TEST + *logofs << "Loop: Root of NX session is '" << sessionDir + << "'.\n" << logofs_flush; + #endif + + delete [] rootPath; + } + + char *sessionPath = new char[strlen(sessionDir) + 1]; + + strcpy(sessionPath, sessionDir); + + return sessionPath; +} + +// +// Identify requested link characteristics +// and set control parameters accordingly. +// + +int ParseLinkOption(const char *opt) +{ + // + // Normalize the user input. + // + + if (strcasecmp(opt, "modem") == 0 || + strcasecmp(opt, "33k") == 0 || + strcasecmp(opt, "56k") == 0) + { + strcpy(linkSpeedName, "MODEM"); + } + else if (strcasecmp(opt, "isdn") == 0 || + strcasecmp(opt, "64k") == 0 || + strcasecmp(opt, "128k") == 0) + { + strcpy(linkSpeedName, "ISDN"); + } + else if (strcasecmp(opt, "adsl") == 0 || + strcasecmp(opt, "256k") == 0 || + strcasecmp(opt, "640k") == 0) + { + strcpy(linkSpeedName, "ADSL"); + } + else if (strcasecmp(opt, "wan") == 0 || + strcasecmp(opt, "1m") == 0 || + strcasecmp(opt, "2m") == 0 || + strcasecmp(opt, "34m") == 0) + { + strcpy(linkSpeedName, "WAN"); + } + else if (strcasecmp(opt, "lan") == 0 || + strcasecmp(opt, "10m") == 0 || + strcasecmp(opt, "100m") == 0 || + strcasecmp(opt, "local") == 0) + { + strcpy(linkSpeedName, "LAN"); + } + + if (strcasecmp(linkSpeedName, "modem") != 0 && + strcasecmp(linkSpeedName, "isdn") != 0 && + strcasecmp(linkSpeedName, "adsl") != 0 && + strcasecmp(linkSpeedName, "wan") != 0 && + strcasecmp(linkSpeedName, "lan") != 0) + { + return -1; + } + + return 1; +} + +int ParsePackOption(const char *opt) +{ + #ifdef DEBUG + *logofs << "Loop: Pack method is " << packMethod + << " quality is " << packQuality << ".\n" + << logofs_flush; + #endif + + #ifdef DEBUG + *logofs << "Loop: Parsing pack method '" << opt + << "'.\n" << logofs_flush; + #endif + + if (strcasecmp(opt, "0") == 0 || + strcasecmp(opt, "none") == 0 || + strcasecmp(opt, "nopack") == 0 || + strcasecmp(opt, "no-pack") == 0) + { + packMethod = PACK_NONE; + } + else if (strcasecmp(opt, "8") == 0) + { + packMethod = PACK_MASKED_8_COLORS; + } + else if (strcasecmp(opt, "64") == 0) + { + packMethod = PACK_MASKED_64_COLORS; + } + else if (strcasecmp(opt, "256") == 0) + { + packMethod = PACK_MASKED_256_COLORS; + } + else if (strcasecmp(opt, "512") == 0) + { + packMethod = PACK_MASKED_512_COLORS; + } + else if (strcasecmp(opt, "4k") == 0) + { + packMethod = PACK_MASKED_4K_COLORS; + } + else if (strcasecmp(opt, "32k") == 0) + { + packMethod = PACK_MASKED_32K_COLORS; + } + else if (strcasecmp(opt, "64k") == 0) + { + packMethod = PACK_MASKED_64K_COLORS; + } + else if (strcasecmp(opt, "256k") == 0) + { + packMethod = PACK_MASKED_256K_COLORS; + } + else if (strcasecmp(opt, "2m") == 0) + { + packMethod = PACK_MASKED_2M_COLORS; + } + else if (strcasecmp(opt, "16m") == 0) + { + packMethod = PACK_MASKED_16M_COLORS; + } + else if (strncasecmp(opt, "8-jpeg", strlen("8-jpeg")) == 0) + { + packMethod = PACK_JPEG_8_COLORS; + } + else if (strncasecmp(opt, "64-jpeg", strlen("64-jpeg")) == 0) + { + packMethod = PACK_JPEG_64_COLORS; + } + else if (strncasecmp(opt, "256-jpeg", strlen("256-jpeg")) == 0) + { + packMethod = PACK_JPEG_256_COLORS; + } + else if (strncasecmp(opt, "512-jpeg", strlen("512-jpeg")) == 0) + { + packMethod = PACK_JPEG_512_COLORS; + } + else if (strncasecmp(opt, "4k-jpeg", strlen("4k-jpeg")) == 0) + { + packMethod = PACK_JPEG_4K_COLORS; + } + else if (strncasecmp(opt, "32k-jpeg", strlen("32k-jpeg")) == 0) + { + packMethod = PACK_JPEG_32K_COLORS; + } + else if (strncasecmp(opt, "64k-jpeg", strlen("64k-jpeg")) == 0) + { + packMethod = PACK_JPEG_64K_COLORS; + } + else if (strncasecmp(opt, "256k-jpeg", strlen("256k-jpeg")) == 0) + { + packMethod = PACK_JPEG_256K_COLORS; + } + else if (strncasecmp(opt, "2m-jpeg", strlen("2m-jpeg")) == 0) + { + packMethod = PACK_JPEG_2M_COLORS; + } + else if (strncasecmp(opt, "16m-jpeg", strlen("16m-jpeg")) == 0) + { + packMethod = PACK_JPEG_16M_COLORS; + } + else if (strncasecmp(opt, "8-png", strlen("8-png")) == 0) + { + packMethod = PACK_PNG_8_COLORS; + } + else if (strncasecmp(opt, "64-png", strlen("64-png")) == 0) + { + packMethod = PACK_PNG_64_COLORS; + } + else if (strncasecmp(opt, "256-png", strlen("256-png")) == 0) + { + packMethod = PACK_PNG_256_COLORS; + } + else if (strncasecmp(opt, "512-png", strlen("512-png")) == 0) + { + packMethod = PACK_PNG_512_COLORS; + } + else if (strncasecmp(opt, "4k-png", strlen("4k-png")) == 0) + { + packMethod = PACK_PNG_4K_COLORS; + } + else if (strncasecmp(opt, "32k-png", strlen("32k-png")) == 0) + { + packMethod = PACK_PNG_32K_COLORS; + } + else if (strncasecmp(opt, "64k-png", strlen("64k-png")) == 0) + { + packMethod = PACK_PNG_64K_COLORS; + } + else if (strncasecmp(opt, "256k-png", strlen("256k-png")) == 0) + { + packMethod = PACK_PNG_256K_COLORS; + } + else if (strncasecmp(opt, "2m-png", strlen("2m-png")) == 0) + { + packMethod = PACK_PNG_2M_COLORS; + } + else if (strncasecmp(opt, "16m-png", strlen("16m-png")) == 0) + { + packMethod = PACK_PNG_16M_COLORS; + } + else if (strncasecmp(opt, "16m-rgb", strlen("16m-rgb")) == 0 || + strncasecmp(opt, "rgb", strlen("rgb")) == 0) + { + packMethod = PACK_RGB_16M_COLORS; + } + else if (strncasecmp(opt, "16m-rle", strlen("16m-rle")) == 0 || + strncasecmp(opt, "rle", strlen("rle")) == 0) + { + packMethod = PACK_RLE_16M_COLORS; + } + else if (strncasecmp(opt, "16m-bitmap", strlen("16m-bitmap")) == 0 || + strncasecmp(opt, "bitmap", strlen("bitmap")) == 0) + { + packMethod = PACK_BITMAP_16M_COLORS; + } + else if (strncasecmp(opt, "lossy", strlen("lossy")) == 0) + { + packMethod = PACK_LOSSY; + } + else if (strncasecmp(opt, "lossless", strlen("lossless")) == 0) + { + packMethod = PACK_LOSSLESS; + } + else if (strncasecmp(opt, "adaptive", strlen("adaptive")) == 0) + { + packMethod = PACK_ADAPTIVE; + } + else + { + return -1; + } + + if (packMethod == PACK_NONE) + { + strcpy(packMethodName, "none"); + } + else + { + strcpy(packMethodName, opt); + } + + if (packMethod == PACK_RGB_16M_COLORS || + packMethod == PACK_RLE_16M_COLORS || + packMethod == PACK_BITMAP_16M_COLORS || + (packMethod >= PACK_JPEG_8_COLORS && + packMethod <= PACK_JPEG_16M_COLORS) || + (packMethod >= PACK_PNG_8_COLORS && + packMethod <= PACK_PNG_16M_COLORS) || + packMethod == PACK_LOSSY || + packMethod == PACK_LOSSLESS || + packMethod == PACK_ADAPTIVE) + { + const char *dash = rindex(opt, '-'); + + if (dash != NULL && strlen(dash) == 2 && + *(dash + 1) >= '0' && *(dash + 1) <= '9') + { + packQuality = atoi(dash + 1); + + #ifdef DEBUG + *logofs << "Loop: Using pack quality '" + << packQuality << "'.\n" << logofs_flush; + #endif + } + } + else + { + packQuality = 0; + } + + return 1; +} + +int ParsePackMethod(const int method, const int quality) +{ + switch (method) + { + case PACK_NONE: + { + strcpy(packMethodName, "none"); + + break; + } + case PACK_MASKED_8_COLORS: + { + strcpy(packMethodName, "8"); + + break; + } + case PACK_MASKED_64_COLORS: + { + strcpy(packMethodName, "64"); + + break; + } + case PACK_MASKED_256_COLORS: + { + strcpy(packMethodName, "256"); + + break; + } + case PACK_MASKED_512_COLORS: + { + strcpy(packMethodName, "512"); + + break; + } + case PACK_MASKED_4K_COLORS: + { + strcpy(packMethodName, "4k"); + + break; + } + case PACK_MASKED_32K_COLORS: + { + strcpy(packMethodName, "32k"); + + break; + } + case PACK_MASKED_64K_COLORS: + { + strcpy(packMethodName, "64k"); + + break; + } + case PACK_MASKED_256K_COLORS: + { + strcpy(packMethodName, "256k"); + + break; + } + case PACK_MASKED_2M_COLORS: + { + strcpy(packMethodName, "2m"); + + break; + } + case PACK_MASKED_16M_COLORS: + { + strcpy(packMethodName, "16m"); + + break; + } + case PACK_JPEG_8_COLORS: + { + strcpy(packMethodName, "8-jpeg"); + + break; + } + case PACK_JPEG_64_COLORS: + { + strcpy(packMethodName, "64-jpeg"); + + break; + } + case PACK_JPEG_256_COLORS: + { + strcpy(packMethodName, "256-jpeg"); + + break; + } + case PACK_JPEG_512_COLORS: + { + strcpy(packMethodName, "512-jpeg"); + + break; + } + case PACK_JPEG_4K_COLORS: + { + strcpy(packMethodName, "4k-jpeg"); + + break; + } + case PACK_JPEG_32K_COLORS: + { + strcpy(packMethodName, "32k-jpeg"); + + break; + } + case PACK_JPEG_64K_COLORS: + { + strcpy(packMethodName, "64k-jpeg"); + + break; + } + case PACK_JPEG_256K_COLORS: + { + strcpy(packMethodName, "256k-jpeg"); + + break; + } + case PACK_JPEG_2M_COLORS: + { + strcpy(packMethodName, "2m-jpeg"); + + break; + } + case PACK_JPEG_16M_COLORS: + { + strcpy(packMethodName, "16m-jpeg"); + + break; + } + case PACK_PNG_8_COLORS: + { + strcpy(packMethodName, "8-png"); + + break; + } + case PACK_PNG_64_COLORS: + { + strcpy(packMethodName, "64-png"); + + break; + } + case PACK_PNG_256_COLORS: + { + strcpy(packMethodName, "256-png"); + + break; + } + case PACK_PNG_512_COLORS: + { + strcpy(packMethodName, "512-png"); + + break; + } + case PACK_PNG_4K_COLORS: + { + strcpy(packMethodName, "4k-png"); + + break; + } + case PACK_PNG_32K_COLORS: + { + strcpy(packMethodName, "32k-png"); + + break; + } + case PACK_PNG_64K_COLORS: + { + strcpy(packMethodName, "64k-png"); + + break; + } + case PACK_PNG_256K_COLORS: + { + strcpy(packMethodName, "256k-png"); + + break; + } + case PACK_PNG_2M_COLORS: + { + strcpy(packMethodName, "2m-png"); + + break; + } + case PACK_PNG_16M_COLORS: + { + strcpy(packMethodName, "16m-png"); + + break; + } + case PACK_RGB_16M_COLORS: + { + strcpy(packMethodName, "16m-rgb"); + + break; + } + case PACK_RLE_16M_COLORS: + { + strcpy(packMethodName, "16m-rle"); + + break; + } + case PACK_BITMAP_16M_COLORS: + { + strcpy(packMethodName, "16m-bitmap"); + + break; + } + case PACK_LOSSY: + { + strcpy(packMethodName, "lossy"); + + break; + } + case PACK_LOSSLESS: + { + strcpy(packMethodName, "lossless"); + + break; + } + case PACK_ADAPTIVE: + { + strcpy(packMethodName, "adaptive"); + + break; + } + default: + { + return -1; + } + } + + if (quality < 0 || quality > 9) + { + return -1; + } + + if (packMethod == PACK_RGB_16M_COLORS || + packMethod == PACK_RLE_16M_COLORS || + packMethod == PACK_BITMAP_16M_COLORS || + (packMethod >= PACK_JPEG_8_COLORS && + packMethod <= PACK_JPEG_16M_COLORS) || + (packMethod >= PACK_PNG_8_COLORS && + packMethod <= PACK_PNG_16M_COLORS) || + packMethod == PACK_LOSSY || + packMethod == PACK_LOSSLESS || + packMethod == PACK_ADAPTIVE) + { + sprintf(packMethodName + strlen(packMethodName), + "-%d", quality); + } + + packMethod = method; + packQuality = quality; + + control -> PackMethod = packMethod; + control -> PackQuality = packQuality; + + return 1; +} + +int SetDirectories() +{ + // + // Determine the location of the user's NX + // directory and the other relevant paths. + // The functions below will check the pa- + // rameters passed to the program and will + // query the environment, if needed. + // + + control -> HomePath = GetHomePath(); + control -> RootPath = GetRootPath(); + control -> SystemPath = GetSystemPath(); + control -> TempPath = GetTempPath(); + control -> ClientPath = GetClientPath(); + + return 1; +} + +int SetLogs() +{ + // + // So far we used stderr (or stdout under + // WIN32). Now use the files selected by + // the user. + // + + if (*statsFileName == '\0') + { + strcpy(statsFileName, "stats"); + + #ifdef TEST + *logofs << "Loop: Assuming default statistics file '" + << statsFileName << "'.\n" << logofs_flush; + #endif + } + #ifdef TEST + else + { + *logofs << "Loop: Name selected for statistics is '" + << statsFileName << "'.\n" << logofs_flush; + } + #endif + + if (OpenLogFile(statsFileName, statofs) < 0) + { + HandleCleanup(); + } + + #ifndef MIXED + + if (*errorsFileName == '\0') + { + strcpy(errorsFileName, "errors"); + + #ifdef TEST + *logofs << "Loop: Assuming default log file name '" + << errorsFileName << "'.\n" << logofs_flush; + #endif + } + #ifdef TEST + else + { + *logofs << "Loop: Name selected for log file is '" + << errorsFileName << "'.\n" << logofs_flush; + } + #endif + + // + // Share the bebug output with the nxssh binder + // process. The file must be made writable by + // everybody because the nxssh process is run by + // nxserver as the nx user. + // + + #ifdef BINDER + + strcpy(errorsFileName, "/tmp/errors"); + + ostream *tmpfs = new ofstream(errorsFileName, ios::out); + + delete tmpfs; + + chmod(errorsFileName, S_IRUSR | S_IWUSR | S_IRGRP | + S_IWGRP | S_IROTH | S_IWOTH); + + #endif + + if (OpenLogFile(errorsFileName, logofs) < 0) + { + HandleCleanup(); + } + + // + // By default the session log is the standard error + // of the process. It is anyway required to set the + // option when running inside SSH, otherwise the + // output will go to the same file as the SSH log, + // depending where the NX client has redirected the + // output. + // + + if (*sessionFileName != '\0') + { + #ifdef TEST + *logofs << "Loop: Name selected for session file is '" + << sessionFileName << "'.\n" << logofs_flush; + #endif + + if (errofs != NULL) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Unexpected value for stream errofs.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Unexpected value for stream errofs.\n"; + } + + if (errsbuf != NULL) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Unexpected value for buffer errsbuf.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Unexpected value for buffer errsbuf.\n"; + } + + errofs = NULL; + errsbuf = NULL; + + if (OpenLogFile(sessionFileName, errofs) < 0) + { + HandleCleanup(); + } + + // + // Redirect the standard error to the file. + // + + errsbuf = cerr.rdbuf(errofs -> rdbuf()); + } + + #endif + + return 1; +} + +int SetPorts() +{ + // + // Depending on the proxy side, we need to determine on which + // port to listen for the given protocol or to which port we + // will have to forward the connection. Three possibilities + // are given for each supported protocol: + // + // Port <= 0: Disable port forwarding. + // Port == 1: Use the default port. + // Port > 1: Use the specified port. + // + // At the connectiong side the user should always explicitly + // set the ports where the connections will be forwarded. This + // is both for security reasons and because, when running both + // proxies on the same host, there is a concrete possibility + // that, by using the default ports, the connection will be + // forwarded to the same port where the peer proxy is listen- + // ing, causing a loop. + // + + if (cupsPort <= 0) + { + #ifdef TEST + *logofs << "Loop: Disabling cups connections.\n" + << logofs_flush; + #endif + + cupsPort = 0; + + useCupsSocket = 0; + } + else + { + if (control -> ProxyMode == proxy_client) + { + if (cupsPort == 1) + { + cupsPort = DEFAULT_NX_CUPS_PORT_OFFSET + proxyPort; + } + + useCupsSocket = 1; + } + else + { + if (cupsPort == 1) + { + // + // Use the well-known 631/tcp port of the + // Internet Printing Protocol. + // + + cupsPort = 631; + } + + useCupsSocket = 0; + } + + #ifdef TEST + *logofs << "Loop: Using cups port '" << cupsPort + << "'.\n" << logofs_flush; + #endif + } + + if (auxPort <= 0) + { + #ifdef TEST + *logofs << "Loop: Disabling auxiliary X11 connections.\n" + << logofs_flush; + #endif + + auxPort = 0; + + useAuxSocket = 0; + } + else + { + if (control -> ProxyMode == proxy_client) + { + if (auxPort == 1) + { + auxPort = DEFAULT_NX_AUX_PORT_OFFSET + proxyPort; + } + + useAuxSocket = 1; + } + else + { + // + // Auxiliary X connections are always forwarded + // to the display where the session is running. + // The only value accepted is 1. + // + + if (auxPort != 1) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Overriding auxiliary X11 " + << "port with new value '" << 1 << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Overriding auxiliary X11 " + << "port with new value '" << 1 << "'.\n"; + + auxPort = 1; + } + + useAuxSocket = 0; + } + + #ifdef TEST + *logofs << "Loop: Using auxiliary X11 port '" << auxPort + << "'.\n" << logofs_flush; + #endif + } + + if (smbPort <= 0) + { + #ifdef TEST + *logofs << "Loop: Disabling SMB connections.\n" + << logofs_flush; + #endif + + smbPort = 0; + + useSmbSocket = 0; + } + else + { + if (control -> ProxyMode == proxy_client) + { + if (smbPort == 1) + { + smbPort = DEFAULT_NX_SMB_PORT_OFFSET + proxyPort; + } + + useSmbSocket = 1; + } + else + { + if (smbPort == 1) + { + // + // Assume the 139/tcp port used for SMB + // over NetBIOS over TCP. + // + + smbPort = 139; + } + + useSmbSocket = 0; + } + + #ifdef TEST + *logofs << "Loop: Using SMB port '" << smbPort + << "'.\n" << logofs_flush; + #endif + } + + if (mediaPort <= 0) + { + #ifdef TEST + *logofs << "Loop: Disabling multimedia connections.\n" + << logofs_flush; + #endif + + mediaPort = 0; + + useMediaSocket = 0; + } + else + { + if (control -> ProxyMode == proxy_client) + { + if (mediaPort == 1) + { + mediaPort = DEFAULT_NX_MEDIA_PORT_OFFSET + proxyPort; + } + + useMediaSocket = 1; + } + else + { + if (mediaPort == 1) + { + // + // We don't have a well-known port to + // be used for media connections. + // + + #ifdef PANIC + *logofs << "Loop: PANIC! No port specified for multimedia connections.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": No port specified for multimedia connections.\n"; + + HandleCleanup(); + } + + useMediaSocket = 0; + } + + #ifdef TEST + *logofs << "Loop: Using multimedia port '" << mediaPort + << "'.\n" << logofs_flush; + #endif + } + + if (httpPort <= 0) + { + #ifdef TEST + *logofs << "Loop: Disabling HTTP connections.\n" + << logofs_flush; + #endif + + httpPort = 0; + + useHttpSocket = 0; + } + else + { + if (control -> ProxyMode == proxy_client) + { + if (httpPort == 1) + { + httpPort = DEFAULT_NX_HTTP_PORT_OFFSET + proxyPort; + } + + useHttpSocket = 1; + } + else + { + if (httpPort == 1) + { + // + // Use the well-known 80/tcp port. + // + + httpPort = 80; + } + + useHttpSocket = 0; + } + + #ifdef TEST + *logofs << "Loop: Using HTTP port '" << httpPort + << "'.\n" << logofs_flush; + #endif + } + + if (ParseFontPath(fontPort) <= 0) + { + #ifdef TEST + *logofs << "Loop: Disabling font server connections.\n" + << logofs_flush; + #endif + + *fontPort = '\0'; + + useFontSocket = 0; + } + else + { + // + // We don't know yet if the remote proxy supports + // the font server connections. If needed, we will + // disable the font server connections at later + // time. + // + + if (control -> ProxyMode == proxy_server) + { + useFontSocket = 1; + } + else + { + useFontSocket = 0; + } + + #ifdef TEST + *logofs << "Loop: Using font server port '" << fontPort + << "'.\n" << logofs_flush; + #endif + } + + if (slavePort <= 0) + { + #ifdef TEST + *logofs << "Loop: Disabling slave connections.\n" + << logofs_flush; + #endif + + slavePort = 0; + + useSlaveSocket = 0; + } + else + { + // + // File transfer connections can + // be originated by both sides. + // + + if (slavePort == 1) + { + if (control -> ProxyMode == proxy_client) + { + slavePort = DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET + proxyPort; + } + else + { + slavePort = DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET + proxyPort; + } + } + + useSlaveSocket = 1; + + #ifdef TEST + *logofs << "Loop: Using slave port '" << slavePort + << "'.\n" << logofs_flush; + #endif + } + + return 1; +} + +int SetDescriptors() +{ + unsigned int limit = 0; + + #ifdef RLIMIT_NOFILE + + rlimit limits; + + if (getrlimit(RLIMIT_NOFILE, &limits) == 0) + { + if (limits.rlim_max == RLIM_INFINITY) + { + limit = 0; + } + else + { + limit = (unsigned int) limits.rlim_max; + } + } + + #endif + + #ifdef _SC_OPEN_MAX + + if (limit == 0) + { + limit = sysconf(_SC_OPEN_MAX); + } + + #endif + + #ifdef FD_SETSIZE + + if (limit > FD_SETSIZE) + { + limit = FD_SETSIZE; + } + + #endif + + #ifdef RLIMIT_NOFILE + + if (limits.rlim_cur < limit) + { + limits.rlim_cur = limit; + + setrlimit(RLIMIT_NOFILE, &limits); + } + + #endif + + if (limit == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Cannot determine number of available " + << "file descriptors.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot determine number of available " + << "file descriptors.\n"; + + return -1; + } + + return 1; +} + +// +// Find the directory containing the caches +// matching the session type. +// + +int SetCaches() +{ + if ((control -> PersistentCachePath = GetCachePath()) == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error getting or creating the cache path.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error getting or creating the cache path.\n"; + + HandleCleanup(); + } + + #ifdef TEST + *logofs << "Loop: Path of cache files is '" << control -> PersistentCachePath + << "'.\n" << logofs_flush; + #endif + + return 1; +} + +// +// Initialize all configuration parameters. +// + +int SetParameters() +{ + // + // Find out the type of session. + // + + SetSession(); + + // + // Initialize the network and compression + // parameters according to the settings + // suggested by the user. + // + + SetLink(); + + // + // Set compression according to link speed. + // + + SetCompression(); + + // + // Be sure that we have a literal for current + // cache size. Value will reflect control's + // default unless we already parsed a 'cache' + // option. Server side has no control on size + // of cache but is informed at session nego- + // tiation about how much memory is going to + // be used. + // + + SetStorage(); + + // + // Set size of shared memory segments. + // + + SetShmem(); + + // + // Make adjustments to cache based + // on the pack method. + // + + SetPack(); + + // + // Set disk-based image cache. + // + + SetImages(); + + // + // Set CPU and bandwidth limits. + // + + SetLimits(); + + return 1; +} + +// +// According to session literal determine +// the type of traffic that is going to be +// transported. Literals should be better +// standardized in future NX versions. +// + +int SetSession() +{ + if (strncmp(sessionType, "agent", strlen("agent")) == 0 || + strncmp(sessionType, "desktop", strlen("desktop")) == 0 || + strncmp(sessionType, "rootless", strlen("rootless")) == 0 || + strncmp(sessionType, "console", strlen("console")) == 0 || + strncmp(sessionType, "default", strlen("default")) == 0 || + strncmp(sessionType, "gnome", strlen("gnome")) == 0 || + strncmp(sessionType, "kde", strlen("kde")) == 0 || + strncmp(sessionType, "cde", strlen("cde")) == 0 || + strncmp(sessionType, "xdm", strlen("xdm")) == 0) + { + control -> SessionMode = session_agent; + } + else if (strncmp(sessionType, "win", strlen("win")) == 0 || + strncmp(sessionType, "vnc", strlen("vnc")) == 0) + { + control -> SessionMode = session_agent; + } + else if (strncmp(sessionType, "shadow", strlen("shadow")) == 0) + { + control -> SessionMode = session_shadow; + } + else if (strncmp(sessionType, "proxy", strlen("proxy")) == 0 || + strncmp(sessionType, "application", strlen("application")) == 0 || + strncmp(sessionType, "raw", strlen("raw")) == 0) + { + control -> SessionMode = session_proxy; + } + else + { + // + // If the session type is not passed or + // it is not among the recognized strings, + // we assume that the proxy is connected + // to the agent. + // + + if (*sessionType != '\0' && + (control -> isProtoStep8() == 1 || + strncmp(sessionType, "unix-", strlen("unix-")) != 0)) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Unrecognized session type '" + << sessionType << "'. Assuming agent session.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Unrecognized session type '" + << sessionType << "'. Assuming agent session.\n"; + } + + control -> SessionMode = session_agent; + } + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Assuming session type '" + << DumpSession(control -> SessionMode) << "' with " + << "string '" << sessionType << "'.\n" + << logofs_flush; + #endif + + // + // By default the policy is immediate. Agents + // will set a different policy, if they like. + // Anyway we need to check if the user has + // provided a custom flush policy. + // + + if (usePolicy != -1) + { + if (usePolicy > 0) + { + control -> FlushPolicy = policy_deferred; + } + else + { + control -> FlushPolicy = policy_immediate; + } + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: WARNING! Forcing flush policy to '" + << DumpPolicy(control -> FlushPolicy) + << ".\n" << logofs_flush; + #endif + } + else + { + control -> FlushPolicy = policy_immediate; + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Setting initial flush policy to '" + << DumpPolicy(control -> FlushPolicy) + << "'.\n" << logofs_flush; + #endif + } + + // + // Check if the proxy library is run inside + // another program providing encryption, as + // it is the case of the SSH client. + // + + if (useEncryption != -1) + { + if (useEncryption > 0) + { + control -> LinkEncrypted = 1; + } + else + { + control -> LinkEncrypted = 0; + } + } + + if (control -> LinkEncrypted == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Proxy running as part of an " + << "encrypting client.\n" + << logofs_flush; + #endif + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Assuming proxy running as a " + << "standalone program.\n" + << logofs_flush; + #endif + } + + // + // Check if the system administrator has + // enabled the respawn of the client at + // the end of session. + // + + if (control -> ProxyMode == proxy_server) + { + struct stat fileStat; + + char fileName[DEFAULT_STRING_LENGTH]; + + snprintf(fileName, DEFAULT_STRING_LENGTH - 1, + "%s/share/noexit", control -> SystemPath); + + *(fileName + DEFAULT_STRING_LENGTH - 1) = '\0'; + + if (stat(fileName, &fileStat) == 0) + { + #ifdef TEST + *logofs << "Loop: Enabling respawn of client at session shutdown.\n" + << logofs_flush; + #endif + + control -> EnableRestartOnShutdown = 1; + } + } + + return 1; +} + +int SetStorage() +{ + // + // If differential compression is disabled + // we don't need a cache at all. + // + + if (control -> LocalDeltaCompression == 0) + { + control -> ClientTotalStorageSize = 0; + control -> ServerTotalStorageSize = 0; + } + + // + // Set a a cache size literal. + // + + int size = control -> getUpperStorageSize(); + + if (size / 1024 > 0) + { + sprintf(cacheSizeName, "%dk", size / 1024); + } + else + { + sprintf(cacheSizeName, "%d", size); + } + + if (control -> ProxyMode == proxy_client) + { + control -> LocalTotalStorageSize = + control -> ClientTotalStorageSize; + + control -> RemoteTotalStorageSize = + control -> ServerTotalStorageSize; + } + else + { + control -> LocalTotalStorageSize = + control -> ServerTotalStorageSize; + + control -> RemoteTotalStorageSize = + control -> ClientTotalStorageSize; + } + + #ifdef DEBUG + *logofs << "Loop: Storage size limit is " + << control -> ClientTotalStorageSize + << " at client and " + << control -> ServerTotalStorageSize + << " at server.\n" + << logofs_flush; + #endif + + #ifdef DEBUG + *logofs << "Loop: Storage local limit set to " + << control -> LocalTotalStorageSize + << " remote limit set to " + << control -> RemoteTotalStorageSize + << ".\n" << logofs_flush; + #endif + + // + // Never reserve for split store more than + // half the memory available for messages. + // + + if (size > 0 && control -> + SplitTotalStorageSize > size / 2) + { + #ifdef TEST + *logofs << "Loop: Reducing size of split store to " + << size / 2 << " bytes.\n" + << logofs_flush; + #endif + + control -> SplitTotalStorageSize = size / 2; + } + + // + // Don't load render from persistent + // cache if extension is hidden or + // not supported by agent. + // + + if (control -> HideRender == 1) + { + #ifdef TEST + *logofs << "Loop: Not loading render extension " + << "from persistent cache.\n" + << logofs_flush; + #endif + + control -> PersistentCacheLoadRender = 0; + } + + return 1; +} + +int SetShmem() +{ + // + // If not set, adjust the size of the shared + // memory segment according to size of the + // message cache. + // + + if (*shsegSizeName == '\0') + { + int size = control -> getUpperStorageSize(); + + const int mega = 1048576; + + if (size > 0) + { + if (size <= 1 * mega) + { + size = 0; + } + else if (size <= 2 * mega) + { + size = 524288; + } + else if (size < 4 * mega) + { + size = 1048576; + } + else + { + size = size / 4; + } + + if (size > 4194304) + { + size = 4194304; + } + + control -> ShmemClientSize = size; + control -> ShmemServerSize = size; + } + else + { + // + // The delta compression is disabled. + // Use a default segment size of 2 MB. + // + + control -> ShmemServerSize = 2 * mega; + } + } + + // + // Client side shared memory support is + // not useful and not implemented. + // + + if (control -> ShmemServerSize >= 524288) + { + control -> ShmemServer = 1; + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Set initial shared memory size " + << "to " << control -> ShmemServerSize + << " bytes.\n" << logofs_flush; + #endif + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Disabled use of the shared memory " + << "extension.\n" << logofs_flush; + #endif + + control -> ShmemServer = 0; + } + + return 1; +} + +// +// Adjust the pack method according to the +// type of the session. +// + +int SetPack() +{ + #ifdef TEST + *logofs << "Loop: Setting pack with initial method " + << packMethod << " and quality " << packQuality + << ".\n" << logofs_flush; + #endif + + // + // Check if this is a proxy session and, in + // this case, set the pack method to none. + // Packed images are not supported by plain + // X applications. + // + + if (control -> SessionMode == session_proxy) + { + #ifdef TEST + *logofs << "Loop: WARNING! Disabling pack with proxy session.\n" + << logofs_flush; + #endif + + packMethod = PACK_NONE; + } + + // + // Adjust the internal settings according + // to the newly selected pack method. + // + + ParsePackMethod(packMethod, packQuality); + + // + // Don't load messages from persistent + // cache if packed images are disabled. + // + + if (control -> PackMethod == PACK_NONE) + { + control -> PersistentCacheLoadPacked = 0; + + #ifdef TEST + *logofs << "Loop: Not loading packed images " + << "from persistent cache.\n" + << logofs_flush; + #endif + } + + return 1; +} + +// +// Set the disk-based image cache parameters +// according to the user's wishes. +// + +int SetImages() +{ + // + // Be sure we disable the image cache if we + // are connecting to plain X clients. + // + + if (control -> SessionMode == session_proxy) + { + #ifdef TEST + *logofs << "Loop: Disabling image cache with " + << "session '" << DumpSession(control -> + SessionMode) << "'.\n" << logofs_flush; + #endif + + sprintf(imagesSizeName, "0"); + + control -> ImageCacheEnableLoad = 0; + control -> ImageCacheEnableSave = 0; + + return 1; + } + + int size = control -> ImageCacheDiskLimit; + + if (size / 1024 > 0) + { + sprintf(imagesSizeName, "%dk", size / 1024); + } + else + { + sprintf(imagesSizeName, "%d", size); + } + + if (size > 0) + { + control -> ImageCacheEnableLoad = 1; + control -> ImageCacheEnableSave = 1; + + if (control -> ProxyMode == proxy_server) + { + if ((control -> ImageCachePath = GetImagesPath()) == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error getting or creating image cache path.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error getting or creating image cache path.\n"; + + HandleCleanup(); + } + + #ifdef TEST + *logofs << "Loop: Path of image cache files is '" << control -> ImageCachePath + << "'.\n" << logofs_flush; + #endif + } + } + else + { + #ifdef TEST + *logofs << "Loop: Disabling the persistent image cache.\n" + << logofs_flush; + #endif + + control -> ImageCacheEnableLoad = 0; + control -> ImageCacheEnableSave = 0; + } + + return 1; +} + +int SetVersion() +{ + // + // Normalize the different proxy versions. + // + + int local = (control -> LocalVersionMajor << 24) | + (control -> LocalVersionMinor << 16) | + control -> LocalVersionPatch; + + int remote = (control -> RemoteVersionMajor << 24) | + (control -> RemoteVersionMinor << 16) | + control -> RemoteVersionPatch; + + int major = -1; + int minor = -1; + int patch = -1; + + if (control -> RemoteVersionMajor <= 1) + { + // + // The remote proxy uses a different + // logic to determine the version so + // we default to the compatibility + // version. + // + + major = control -> CompatVersionMajor; + minor = control -> CompatVersionMinor; + patch = control -> CompatVersionPatch; + + #ifdef TEST + *logofs << "Loop: Using compatibility version '" + << major << "." << minor << "." << patch + << "'.\n" << logofs_flush; + #endif + } + else if (control -> LocalVersionMajor > + control -> RemoteVersionMajor) + { + // + // We use a more recent version. Let's + // negotiate the version based on the + // version supported by the remote. + // + + major = control -> RemoteVersionMajor; + minor = control -> RemoteVersionMinor; + patch = control -> RemoteVersionPatch; + + #ifdef TEST + *logofs << "Loop: Using remote version '" + << major << "." << minor << "." << patch + << "'.\n" << logofs_flush; + #endif + } + else + { + // + // We support a major version that is + // equal or older than the remote. We + // assume the smaller version between + // the two, including the minor and + // the patch numbers. + // + + if (local > remote) + { + major = control -> RemoteVersionMajor; + minor = control -> RemoteVersionMinor; + patch = control -> RemoteVersionPatch; + + #ifdef TEST + *logofs << "Loop: Using remote version '" + << major << "." << minor << "." << patch + << "'.\n" << logofs_flush; + #endif + } + else + { + major = control -> LocalVersionMajor; + minor = control -> LocalVersionMinor; + patch = control -> LocalVersionPatch; + + #ifdef TEST + *logofs << "Loop: Using local version '" + << major << "." << minor << "." << patch + << "'.\n" << logofs_flush; + #endif + } + } + + // + // Handle the 1.5.0 versions. The protocol + // step 6 is the minimum supported version. + // + + int step = 0; + + if (major == 1) + { + if (minor == 5) + { + step = 6; + } + } + else if (major == 2) + { + step = 7; + } + else if (major == 3) + { + if (minor >= 2) + { + step = 10; + } + else if (minor > 0 || patch > 0) + { + step = 9; + } + else + { + step = 8; + } + } + else if (major > 3) + { + step = 10; + } + + if (step == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Incompatible remote version " + << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor + << "." << control -> RemoteVersionPatch << " with local version " + << control -> LocalVersionMajor << "." << control -> LocalVersionMinor + << "." << control -> LocalVersionPatch << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Incompatible remote version " + << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor + << "." << control -> RemoteVersionPatch << " with local version " + << control -> LocalVersionMajor << "." << control -> LocalVersionMinor + << "." << control -> LocalVersionPatch << ".\n"; + + return -1; + } + + #ifdef TEST + *logofs << "Loop: Using NX protocol step " + << step << ".\n" << logofs_flush; + #endif + + control -> setProtoStep(step); + + // + // Ignore the differences in patch version + // and print a warning if the local version + // is different or obsolete compared to + // the remote. + // + + local &= 0xffff0000; + remote &= 0xffff0000; + + if (local != remote) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Connected to remote version " + << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor + << "." << control -> RemoteVersionPatch << " with local version " + << control -> LocalVersionMajor << "." << control -> LocalVersionMinor + << "." << control -> LocalVersionPatch << ".\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Connected to remote version " + << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor + << "." << control -> RemoteVersionPatch << " with local version " + << control -> LocalVersionMajor << "." << control -> LocalVersionMinor + << "." << control -> LocalVersionPatch << ".\n" << logofs_flush; + } + + if (local < remote) + { + cerr << "Warning" << ": Consider checking http://www.nomachine.com/ for updates.\n"; + } + + // + // Now that we are aware of the remote + // version, let's adjust the options to + // be compatible with the remote proxy. + // + + if (control -> ProxyMode == proxy_client) + { + if (control -> isProtoStep8() == 0) + { + if (strncmp(sessionType, "shadow", strlen("shadow")) == 0 || + strncmp(sessionType, "application", strlen("application")) == 0 || + strncmp(sessionType, "console", strlen("console")) == 0 || + strncmp(sessionType, "default", strlen("default")) == 0 || + strncmp(sessionType, "gnome", strlen("gnome")) == 0 || + strncmp(sessionType, "kde", strlen("kde")) == 0 || + strncmp(sessionType, "cde", strlen("cde")) == 0 || + strncmp(sessionType, "xdm", strlen("xdm")) == 0) + + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: WARNING! Prepending 'unix-' to the " + << "name of the session.\n" << logofs_flush; + #endif + + char buffer[DEFAULT_STRING_LENGTH]; + + snprintf(buffer, DEFAULT_STRING_LENGTH - 1, "unix-%s", sessionType); + + strcpy(sessionType, buffer); + } + } + + // + // Check if the remote is able to handle + // the selected pack method. + // + + if (control -> isProtoStep8() == 0) + { + if (packMethod == PACK_ADAPTIVE || packMethod == PACK_LOSSY) + { + #ifdef TEST + *logofs << "Loop: WARNING! Assuming a lossy encoding with " + << "an old proxy version.\n" << logofs_flush; + #endif + + packMethod = PACK_JPEG_16M_COLORS; + } + else if (packMethod == PACK_LOSSLESS) + { + #ifdef TEST + *logofs << "Loop: WARNING! Assuming a lossless encoding with " + << "an old proxy version.\n" << logofs_flush; + #endif + + if (control -> isProtoStep7() == 1) + { + packMethod = PACK_RLE_16M_COLORS; + } + else + { + packMethod = PACK_PNG_16M_COLORS; + } + } + } + + // + // If the remote doesn't support the + // selected method use something that + // is compatible. + // + + if ((packMethod == PACK_RGB_16M_COLORS || + packMethod == PACK_RLE_16M_COLORS || + packMethod == PACK_BITMAP_16M_COLORS) && + control -> isProtoStep7() == 0) + { + #ifdef TEST + *logofs << "Loop: WARNING! Setting the pack method to '" + << PACK_PNG_16M_COLORS << "' with '" << packMethod + << "' unsupported.\n" << logofs_flush; + #endif + + packMethod = PACK_PNG_16M_COLORS; + packQuality = 9; + } + else if (packMethod == PACK_BITMAP_16M_COLORS && + control -> isProtoStep8() == 0) + { + #ifdef TEST + *logofs << "Loop: WARNING! Setting the pack method to '" + << PACK_RLE_16M_COLORS << "' with '" << packMethod + << "' unsupported.\n" << logofs_flush; + #endif + + packMethod = PACK_RLE_16M_COLORS; + packQuality = 9; + } + + // + // Update the pack method name. + // + + ParsePackMethod(packMethod, packQuality); + } + + // + // At the moment the image cache is not used by the + // agent but we need to take care of the compatibi- + // lity with old versions. Proxy versions older than + // the 3.0.0 assume that it is enabled and will send + // specific bits as part of the encoding. Conversely, + // it is advisable to disable the cache right now. + // By not enabling the image cache, the house-keep- + // ing process will only take care of cleaning up + // the "cache-" directories. + // + + if (control -> isProtoStep8() == 1) + { + #ifdef TEST + *logofs << "Loop: Disabling image cache with protocol " + << "step '" << control -> getProtoStep() + << "'.\n" << logofs_flush; + #endif + + sprintf(imagesSizeName, "0"); + + control -> ImageCacheEnableLoad = 0; + control -> ImageCacheEnableSave = 0; + } + + return 1; +} + +// +// Identify the requested link settings +// and update the control parameters +// accordingly. +// + +int SetLink() +{ + #ifdef TEST + *logofs << "Loop: Setting link with initial value " + << linkSpeedName << ".\n" << logofs_flush; + #endif + + if (*linkSpeedName == '\0') + { + strcpy(linkSpeedName, "lan"); + } + + #ifdef TEST + *logofs << "Loop: Link speed is " << linkSpeedName + << ".\n" << logofs_flush; + #endif + + if (strcasecmp(linkSpeedName, "modem") == 0) + { + SetLinkModem(); + } + else if (strcasecmp(linkSpeedName, "isdn") == 0) + { + SetLinkIsdn(); + } + else if (strcasecmp(linkSpeedName, "adsl") == 0) + { + SetLinkAdsl(); + } + else if (strcasecmp(linkSpeedName, "wan") == 0) + { + SetLinkWan(); + } + else if (strcasecmp(linkSpeedName, "lan") == 0) + { + SetLinkLan(); + } + else + { + return -1; + } + + // + // Set TCP_NODELAY according to the user's + // wishes. + // + + if (useNoDelay != -1) + { + control -> OptionProxyClientNoDelay = useNoDelay; + control -> OptionProxyServerNoDelay = useNoDelay; + } + + // + // Select the image compression method. + // + + if (packMethod == -1) + { + packMethod = control -> PackMethod; + } + + if (packQuality == -1) + { + packQuality = control -> PackQuality; + } + + if (ParsePackMethod(packMethod, packQuality) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Unrecognized pack method id " + << packMethod << " with quality " << packQuality + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unrecognized pack method id " + << packMethod << " with quality " << packQuality + << ".\n"; + + HandleCleanup(); + } + + // + // Check if the user disabled the ability + // to generate simple replies at the client + // side. + // + + if (control -> SessionMode == session_proxy) + { + if (useTaint != -1) + { + control -> TaintReplies = (useTaint == 1); + } + else + { + #ifdef WARNING + *logofs << "Loop: WARNING! Forcing taint of replies " + << "with a proxy session.\n" + << logofs_flush; + #endif + + control -> TaintReplies = 1; + } + } + else + { + // + // There is no need to taint the + // replies if we have an agent. + // + + control -> TaintReplies = 0; + } + + // + // Be sure that the requests needing a reply + // are flushed immediately. Normal X clients + // use so many replies to make the queuing + // completely useless. + // + + if (control -> SessionMode == session_proxy) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Forcing flush on priority " + << "with a proxy session.\n" + << logofs_flush; + #endif + + control -> FlushPriority = 1; + } + + return 1; +} + +// +// Parameters for MODEM 28.8/33.6/56 Kbps. +// + +int SetLinkModem() +{ + #ifdef TEST + *logofs << "Loop: Setting parameters for MODEM.\n" + << logofs_flush; + #endif + + control -> LinkMode = LINK_TYPE_MODEM; + + control -> TokenSize = 256; + control -> TokenLimit = 24; + + control -> SplitMode = 1; + control -> SplitTotalSize = 128; + control -> SplitTotalStorageSize = 1048576; + + control -> SplitTimeout = 50; + control -> MotionTimeout = 50; + control -> IdleTimeout = 50; + + control -> PackMethod = PACK_ADAPTIVE; + control -> PackQuality = 3; + + return 1; +} + +// +// Parameters for ISDN 64/128 Kbps. +// + +int SetLinkIsdn() +{ + #ifdef TEST + *logofs << "Loop: Setting parameters for ISDN.\n" + << logofs_flush; + #endif + + control -> LinkMode = LINK_TYPE_ISDN; + + control -> TokenSize = 384; + control -> TokenLimit = 24; + + control -> SplitMode = 1; + control -> SplitTotalSize = 128; + control -> SplitTotalStorageSize = 1048576; + + control -> SplitTimeout = 50; + control -> MotionTimeout = 20; + control -> IdleTimeout = 50; + + control -> PackMethod = PACK_ADAPTIVE; + control -> PackQuality = 5; + + return 1; +} + +// +// Parameters for ADSL 256 Kbps. +// + +int SetLinkAdsl() +{ + #ifdef TEST + *logofs << "Loop: Setting parameters for ADSL.\n" + << logofs_flush; + #endif + + control -> LinkMode = LINK_TYPE_ADSL; + + control -> TokenSize = 512; + control -> TokenLimit = 24; + + control -> SplitMode = 1; + control -> SplitTotalSize = 128; + control -> SplitTotalStorageSize = 1048576; + + control -> SplitTimeout = 50; + control -> MotionTimeout = 10; + control -> IdleTimeout = 50; + + control -> PackMethod = PACK_ADAPTIVE; + control -> PackQuality = 7; + + return 1; +} + +// +// Parameters for XDSL/FDDI/ATM 1/2/34 Mbps WAN. +// + +int SetLinkWan() +{ + #ifdef TEST + *logofs << "Loop: Setting parameters for WAN.\n" + << logofs_flush; + #endif + + control -> LinkMode = LINK_TYPE_WAN; + + control -> TokenSize = 768; + control -> TokenLimit = 24; + + control -> SplitMode = 1; + control -> SplitTotalSize = 128; + control -> SplitTotalStorageSize = 1048576; + + control -> SplitTimeout = 50; + control -> MotionTimeout = 5; + control -> IdleTimeout = 50; + + control -> PackMethod = PACK_ADAPTIVE; + control -> PackQuality = 9; + + return 1; +} + +// +// Parameters for LAN 10/100 Mbps. +// + +int SetLinkLan() +{ + #ifdef TEST + *logofs << "Loop: Setting parameters for LAN.\n" + << logofs_flush; + #endif + + control -> LinkMode = LINK_TYPE_LAN; + + control -> TokenSize = 1536; + control -> TokenLimit = 24; + + control -> SplitMode = 1; + control -> SplitTotalSize = 128; + control -> SplitTotalStorageSize = 1048576; + + control -> SplitTimeout = 50; + control -> MotionTimeout = 0; + control -> IdleTimeout = 50; + + control -> PackMethod = PACK_ADAPTIVE; + control -> PackQuality = 9; + + return 1; +} + +// +// Identify the requested link type and set +// the control parameters accordingly. +// + +int SetCompression() +{ + if (strcasecmp(linkSpeedName, "modem") == 0) + { + SetCompressionModem(); + } + else if (strcasecmp(linkSpeedName, "isdn") == 0) + { + SetCompressionIsdn(); + } + else if (strcasecmp(linkSpeedName, "adsl") == 0) + { + SetCompressionAdsl(); + } + else if (strcasecmp(linkSpeedName, "wan") == 0) + { + SetCompressionWan(); + } + else if (strcasecmp(linkSpeedName, "lan") == 0) + { + SetCompressionLan(); + } + else + { + return -1; + } + + if (control -> LocalDeltaCompression < 0) + { + control -> LocalDeltaCompression = 1; + } + + // + // If we didn't set remote delta compression + // (as it should always be the case at client + // side) assume value of local side. + // + + if (control -> RemoteDeltaCompression < 0) + { + control -> RemoteDeltaCompression = + control -> LocalDeltaCompression; + } + + // + // If we didn't set remote compression levels + // assume values of local side. + // + + if (control -> RemoteStreamCompression < 0) + { + control -> RemoteStreamCompressionLevel = + control -> LocalStreamCompressionLevel; + + if (control -> RemoteStreamCompressionLevel > 0) + { + control -> RemoteStreamCompression = 1; + } + else + { + control -> RemoteStreamCompression = 0; + } + } + + if (control -> RemoteDataCompression < 0) + { + control -> RemoteDataCompressionLevel = + control -> LocalDataCompressionLevel; + + if (control -> RemoteDataCompressionLevel > 0) + { + control -> RemoteDataCompression = 1; + } + else + { + control -> RemoteDataCompression = 0; + } + } + + return 1; +} + +// +// Compression for MODEM. +// + +int SetCompressionModem() +{ + if (control -> LocalDataCompression < 0) + { + control -> LocalDataCompression = 1; + control -> LocalDataCompressionLevel = 1; + } + + if (control -> LocalDataCompressionThreshold < 0) + { + control -> LocalDataCompressionThreshold = 32; + } + + if (control -> LocalStreamCompression < 0) + { + control -> LocalStreamCompression = 1; + control -> LocalStreamCompressionLevel = 9; + } + + return 1; +} + +// +// Compression for ISDN. +// + +int SetCompressionIsdn() +{ + if (control -> LocalDataCompression < 0) + { + control -> LocalDataCompression = 1; + control -> LocalDataCompressionLevel = 1; + } + + if (control -> LocalDataCompressionThreshold < 0) + { + control -> LocalDataCompressionThreshold = 32; + } + + if (control -> LocalStreamCompression < 0) + { + control -> LocalStreamCompression = 1; + control -> LocalStreamCompressionLevel = 6; + } + + return 1; +} + +// +// Compression for ADSL. +// + +int SetCompressionAdsl() +{ + if (control -> LocalDataCompression < 0) + { + control -> LocalDataCompression = 1; + control -> LocalDataCompressionLevel = 1; + } + + if (control -> LocalDataCompressionThreshold < 0) + { + control -> LocalDataCompressionThreshold = 32; + } + + if (control -> LocalStreamCompression < 0) + { + control -> LocalStreamCompression = 1; + control -> LocalStreamCompressionLevel = 4; + } + + return 1; +} + +// +// Compression for WAN. +// + +int SetCompressionWan() +{ + if (control -> LocalDataCompression < 0) + { + control -> LocalDataCompression = 1; + control -> LocalDataCompressionLevel = 1; + } + + if (control -> LocalDataCompressionThreshold < 0) + { + control -> LocalDataCompressionThreshold = 32; + } + + if (control -> LocalStreamCompression < 0) + { + control -> LocalStreamCompression = 1; + control -> LocalStreamCompressionLevel = 1; + } + + return 1; +} + +// +// Compression for LAN. +// + +int SetCompressionLan() +{ + // + // Disable delta compression if not + // explicitly enabled. + // + + if (control -> LocalDeltaCompression < 0) + { + control -> LocalDeltaCompression = 0; + } + + if (control -> LocalDataCompression < 0) + { + control -> LocalDataCompression = 0; + control -> LocalDataCompressionLevel = 0; + } + + if (control -> LocalDataCompressionThreshold < 0) + { + control -> LocalDataCompressionThreshold = 0; + } + + if (control -> LocalStreamCompression < 0) + { + control -> LocalStreamCompression = 0; + control -> LocalStreamCompressionLevel = 0; + } + + return 1; +} + +int SetLimits() +{ + // + // Check if the user requested strict + // control flow parameters. + // + + if (useStrict == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: LIMIT! Decreasing the token limit " + << "to " << control -> TokenLimit / 2 + << " with option 'strict'.\n" + << logofs_flush; + #endif + + control -> TokenLimit /= 2; + } + + #ifdef STRICT + + control -> TokenLimit = 1; + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: WARNING! LIMIT! Setting the token limit " + << "to " << control -> TokenLimit + << " to simulate the proxy congestion.\n" + << logofs_flush; + #endif + + #endif + + // + // Reduce the size of the log file. + // + + #ifdef QUOTA + + control -> FileSizeLimit = 8388608; + + #endif + + // + // Check the bitrate limits. + // + + if (control -> LocalBitrateLimit == -1) + { + if (control -> ProxyMode == proxy_client) + { + control -> LocalBitrateLimit = + control -> ClientBitrateLimit; + } + else + { + control -> LocalBitrateLimit = + control -> ServerBitrateLimit; + } + } + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: LIMIT! Setting client bitrate limit " + << "to " << control -> ClientBitrateLimit + << " server bitrate limit to " << control -> + ServerBitrateLimit << " with local limit " + << control -> LocalBitrateLimit << ".\n" + << logofs_flush; + #endif + + return 1; +} + +// +// These functions are used to parse literal +// values provided by the user and set the +// control parameters accordingly. +// + +int ParseCacheOption(const char *opt) +{ + int size = ParseArg("", "cache", opt); + + if (size < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid value '" + << opt << "' for option 'cache'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid value '" + << opt << "' for option 'cache'.\n"; + + return -1; + } + + #ifdef TEST + *logofs << "Loop: Setting size of cache to " + << size << " bytes.\n" << logofs_flush; + #endif + + control -> ClientTotalStorageSize = size; + control -> ServerTotalStorageSize = size; + + strcpy(cacheSizeName, opt); + + if (size == 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Disabling NX delta compression.\n" + << logofs_flush; + #endif + + control -> LocalDeltaCompression = 0; + + #ifdef WARNING + *logofs << "Loop: WARNING! Disabling use of NX persistent cache.\n" + << logofs_flush; + #endif + + control -> PersistentCacheEnableLoad = 0; + control -> PersistentCacheEnableSave = 0; + } + + return 1; +} + +int ParseImagesOption(const char *opt) +{ + int size = ParseArg("", "images", opt); + + if (size < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid value '" + << opt << "' for option 'images'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid value '" + << opt << "' for option 'images'.\n"; + + return -1; + } + + #ifdef TEST + *logofs << "Loop: Setting size of images cache to " + << size << " bytes.\n" << logofs_flush; + #endif + + control -> ImageCacheDiskLimit = size; + + strcpy(imagesSizeName, opt); + + return 1; +} + +int ParseShmemOption(const char *opt) +{ + int size = ParseArg("", "shseg", opt); + + if (size < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid value '" + << opt << "' for option 'shseg'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid value '" + << opt << "' for option 'shseg'.\n"; + + return -1; + } + + control -> ShmemClientSize = size; + control -> ShmemServerSize = size; + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Set shared memory size to " + << control -> ShmemServerSize << " bytes.\n" + << logofs_flush; + #endif + + strcpy(shsegSizeName, opt); + + return 1; +} + +int ParseBitrateOption(const char *opt) +{ + int bitrate = ParseArg("", "limit", opt); + + if (bitrate < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid value '" + << opt << "' for option 'limit'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid value '" + << opt << "' for option 'limit'.\n"; + + return -1; + } + + strcpy(bitrateLimitName, opt); + + if (bitrate == 0) + { + #ifdef TEST + *logofs << "Loop: Disabling bitrate limit on proxy link.\n" + << logofs_flush; + #endif + + control -> LocalBitrateLimit = 0; + } + else + { + #ifdef TEST + *logofs << "Loop: Setting bitrate to " << bitrate + << " bits per second.\n" << logofs_flush; + #endif + + // + // Internal representation is in bytes + // per second. + // + + control -> LocalBitrateLimit = bitrate >> 3; + } + + return 1; +} + +int ParseHostOption(const char *opt, char *host, int &port) +{ + #ifdef TEST + *logofs << "Loop: Trying to parse options string '" << opt + << "' as a remote NX host.\n" << logofs_flush; + #endif + + if (opt == NULL || *opt == '\0') + { + #ifdef PANIC + *logofs << "Loop: PANIC! No host parameter provided.\n" + << logofs_flush; + #endif + + return 0; + } + else if (strlen(opt) >= DEFAULT_STRING_LENGTH) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Host parameter exceeds length of " + << DEFAULT_STRING_LENGTH << " characters.\n" + << logofs_flush; + #endif + + return 0; + } + + // + // Look for a host name followed + // by a colon followed by port. + // + + int newPort = port; + + const char *separator = rindex(opt, ':'); + + if (separator != NULL) + { + const char *check = separator + 1; + + while (*check != '\0' && *check != ',' && + *check != '=' && isdigit(*check) != 0) + { + check++; + } + + newPort = atoi(separator + 1); + + if (newPort < 0 || *check != '\0') + { + #ifdef TEST + *logofs << "Loop: Can't identify remote NX port in string '" + << separator << "'.\n" << logofs_flush; + #endif + + return 0; + } + } + else if (newPort < 0) + { + // + // Complain if port was not passed + // by other means. + // + + #ifdef TEST + *logofs << "Loop: Can't identify remote NX port in string '" + << opt << "'.\n" << logofs_flush; + #endif + + return 0; + } + else + { + separator = opt + strlen(opt); + } + + char newHost[DEFAULT_STRING_LENGTH] = { 0 }; + + strncpy(newHost, opt, strlen(opt) - strlen(separator)); + + *(newHost + strlen(opt) - strlen(separator)) = '\0'; + + const char *check = newHost; + + while (*check != '\0' && *check != ',' && + *check != '=') + { + check++; + } + + if (*check != '\0') + { + #ifdef TEST + *logofs << "Loop: Can't identify remote NX host in string '" + << newHost << "'.\n" << logofs_flush; + #endif + + return 0; + } + else if (*acceptHost != '\0') + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't manage to connect and accept connections " + << "at the same time.\n" << logofs_flush; + + *logofs << "Loop: PANIC! Refusing remote NX host with string '" + << opt << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't manage to connect and accept connections " + << "at the same time.\n"; + + cerr << "Error" << ": Refusing remote NX host with string '" + << opt << "'.\n"; + + return -1; + } + + if (*host != '\0' && strcmp(host, newHost) != 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Overriding remote NX host '" + << host << "' with new value '" << newHost + << "'.\n" << logofs_flush; + #endif + } + + strcpy(host, newHost); + + if (port != -1 && port != newPort) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Overriding remote NX port '" + << port << "' with new value '" << newPort + << "'.\n" << logofs_flush; + #endif + } + + #ifdef TEST + *logofs << "Loop: Parsed options string '" << opt + << "' with host '" << newHost << "' and port '" + << newPort << "'.\n" << logofs_flush; + #endif + + port = newPort; + + return 1; +} + +int ParseFontPath(char *path) +{ + char oldPath[DEFAULT_STRING_LENGTH]; + + strcpy(oldPath, path); + + if (path == NULL || *path == '\0' || strcmp(path, "0") == 0) + { + return 0; + } + + #ifdef TEST + *logofs << "Loop: Parsing font server option '" << path + << "'.\n" << logofs_flush; + #endif + + // + // Convert the value to our default port. + // + + if (strcmp(fontPort, "1") == 0) + { + if (control -> ProxyMode == proxy_server) + { + snprintf(fontPort, DEFAULT_STRING_LENGTH - 1, "%d", + DEFAULT_NX_FONT_PORT_OFFSET + proxyPort); + } + else + { + // + // Let the client use the well-known + // "unix/:7100" font path. + // + + snprintf(fontPort, DEFAULT_STRING_LENGTH - 1, "unix/:7100"); + } + } + + // + // Check if a simple numaric value was given. + // + + if (atoi(path) > 0) + { + #ifdef TEST + *logofs << "Loop: Assuming numeric TCP port '" << atoi(path) + << "' for font server.\n" << logofs_flush; + #endif + + return 1; + } + + // + // Let's assume that a port specification "unix/:7100" + // corresponds to "$TEMP/.font-unix/fs7100" and a port + // "unix/:-1" corresponds to "$TEMP/.font-unix/fs-1". + // + + if (strncmp("unix/:", path, 6) == 0) + { + snprintf(path, DEFAULT_STRING_LENGTH - 1, "%s/.font-unix/fs%s", + control -> TempPath, oldPath + 6); + + *(path + DEFAULT_STRING_LENGTH - 1) = '\0'; + + #ifdef TEST + *logofs << "Loop: Assuming Unix socket '" << path + << "' for font server.\n" << logofs_flush; + #endif + } + else if (strncmp("tcp/:", path, 5) == 0) + { + snprintf(path, DEFAULT_STRING_LENGTH - 1, "%d", atoi(oldPath + 5)); + + *(path + DEFAULT_STRING_LENGTH - 1) = '\0'; + + if (atoi(path) <= 0) + { + goto ParseFontPathError; + } + + #ifdef TEST + *logofs << "Loop: Assuming TCP port '" << atoi(path) + << "' for font server.\n" << logofs_flush; + #endif + } + else + { + // + // Accept an absolute file path as + // a valid Unix socket. + // + + if (*path != '/') + { + goto ParseFontPathError; + } + + #ifdef TEST + *logofs << "Loop: Assuming Unix socket '" << path + << "' for font server.\n" << logofs_flush; + #endif + } + + return 1; + +ParseFontPathError: + + #ifdef TEST + *logofs << "Loop: Unable to determine the font server " + << "port in string '" << path << "'.\n" + << logofs_flush; + #endif + + return -1; +} + +int ParseListenOption(int &address) +{ + if (*listenHost == '\0') + { + // + // On the X client side listen on any address. + // On the X server side listen to the forwarder + // on localhost. + // + + if (control -> ProxyMode == proxy_server) + { + address = (int) inet_addr("127.0.0.1"); + } + else + { + address = htonl(INADDR_ANY); + } + } + else + { + address = inet_addr(listenHost); + } + + return 1; +} + +int OpenLogFile(char *name, ostream *&stream) +{ + if (name == NULL || *name == '\0') + { + #ifdef TEST + *logofs << "Loop: WARNING! No name provided for output. Using standard error.\n" + << logofs_flush; + #endif + + if (stream == NULL) + { + stream = &cerr; + } + + return 1; + } + + if (stream == NULL || stream == &cerr) + { + if (*name != '/' && *name != '.') + { + char *filePath = GetSessionPath(); + + if (filePath == NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Cannot determine directory of NX session file.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Cannot determine directory of NX session file.\n"; + + return -1; + } + + if (strlen(filePath) + strlen("/") + + strlen(name) + 1 > DEFAULT_STRING_LENGTH) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Full name of NX file '" << name + << " would exceed length of " << DEFAULT_STRING_LENGTH + << " characters.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Full name of NX file '" << name + << " would exceed length of " << DEFAULT_STRING_LENGTH + << " characters.\n"; + + return -1; + } + + char *file = new char[strlen(filePath) + strlen("/") + + strlen(name) + 1]; + + // + // Transform name in a fully qualified name. + // + + strcpy(file, filePath); + strcat(file, "/"); + strcat(file, name); + + strcpy(name, file); + + delete [] filePath; + delete [] file; + } + + mode_t fileMode = umask(0077); + + for (;;) + { + if ((stream = new ofstream(name, ios::app)) != NULL) + { + break; + } + + usleep(200000); + } + + umask(fileMode); + } + else + { + #ifdef PANIC + *logofs << "Loop: PANIC! Bad stream provided for output.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Bad stream provided for output.\n"; + + return -1; + } + + return 1; +} + +int ReopenLogFile(char *name, ostream *&stream, int limit) +{ + if (*name != '\0' && limit >= 0) + { + struct stat fileStat; + + if (limit > 0) + { + // + // This is used for the log file, if the + // size exceeds the limit. + // + + if (stat(name, &fileStat) != 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Can't get stats of file '" + << name << "'. Error is " << EGET() + << " '" << ESTR() << "'.\n" << logofs_flush; + #endif + + return 0; + } + else if (fileStat.st_size < (long) limit) + { + return 0; + } + } + + #ifdef TEST + *logofs << "Loop: Deleting file '" << name + << "' with size " << fileStat.st_size + << ".\n" << logofs_flush; + #endif + + // + // Create a new stream over the previous + // file. Trying to delete the file fails + // to work on recent Cygwin installs. + // + + *stream << flush; + + delete stream; + + mode_t fileMode = umask(0077); + + for (;;) + { + if ((stream = new ofstream(name, ios::out)) != NULL) + { + break; + } + + usleep(200000); + } + + umask(fileMode); + + #ifdef TEST + *logofs << "Loop: Reopened file '" << name + << "'.\n" << logofs_flush; + #endif + } + + return 1; +} + +void PrintProcessInfo() +{ + if (agent == NULL) + { + cerr << "\nNXPROXY - Version " << control -> LocalVersionMajor + << "." << control -> LocalVersionMinor << "." + << control -> LocalVersionPatch << "\n\n"; + + cerr << "Copyright (C) 2001, 2010 NoMachine.\n" + << "See http://www.nomachine.com/ for more information.\n\n"; + } + + // + // People get confused by the fact that client + // mode is running on NX server and viceversa. + // Let's adopt an user-friendly naming conven- + // tion here. + // + + cerr << "Info: Proxy running in " + << (control -> ProxyMode == proxy_client ? "server" : "client") + << " mode with pid '" << getpid() << "'.\n"; + + if (agent == NULL) + { + cerr << "Session" << ": Starting session at '" + << strTimestamp() << "'.\n"; + } + + #ifdef TEST + + if (*errorsFileName != '\0') + { + cerr << "Info" << ": Using errors file '" << errorsFileName << "'.\n"; + } + + if (*statsFileName != '\0') + { + cerr << "Info" << ": Using stats file '" << statsFileName << "'.\n"; + } + + #endif +} + +void PrintConnectionInfo() +{ + cerr << "Info" << ": Using " + << linkSpeedName << " link parameters " + << control -> TokenSize + << "/" << control -> TokenLimit + << "/" << control -> FlushPolicy + 1 + << "/" << control -> FlushPriority + << ".\n"; + + if (control -> ProxyMode == proxy_client) + { + cerr << "Info" << ": Using agent parameters " + << control -> PingTimeout + << "/" << control -> MotionTimeout + << "/" << control -> IdleTimeout + << "/" << control -> TaintReplies + << "/" << control -> HideRender + << ".\n"; + } + + if (control -> LocalDeltaCompression == 1) + { + cerr << "Info" << ": Using cache parameters " + << control -> MinimumMessageSize + << "/" << control -> MaximumMessageSize / 1024 << "KB" + << "/" << control -> ClientTotalStorageSize / 1024 << "KB" + << "/" << control -> ServerTotalStorageSize / 1024 << "KB" + << ".\n"; + } + + if (control -> ImageCacheEnableLoad == 1 || + control -> ImageCacheEnableSave == 1) + { + cerr << "Info" << ": Using image streaming parameters " + << control -> SplitTimeout + << "/" << control -> SplitTotalSize + << "/" << control -> SplitTotalStorageSize / 1024 << "KB" + << "/" << control -> SplitDataThreshold + << "/" << control -> SplitDataPacketLimit + << ".\n"; + + cerr << "Info" << ": Using image cache parameters " + << control -> ImageCacheEnableLoad + << "/" << control -> ImageCacheEnableSave + << "/" << control -> ImageCacheDiskLimit / 1024 << "KB" + << ".\n"; + } + + cerr << "Info" << ": Using pack method '" + << packMethodName << "' with session '" + << sessionType << "'.\n"; + + if (*productName != '\0') + { + cerr << "Info" << ": Using product '" << productName + << "'.\n" << logofs_flush; + } + + if (control -> LocalDeltaCompression == 0) + { + cerr << "Info" << ": Not using NX delta compression.\n"; + } + + if (control -> LocalDataCompression == 1 || + control -> RemoteDataCompression == 1) + { + cerr << "Info" << ": Using ZLIB data compression " + << control -> LocalDataCompressionLevel + << "/" << control -> RemoteDataCompressionLevel + << "/" << control -> LocalDataCompressionThreshold + << ".\n"; + } + else + { + cerr << "Info" << ": Not using ZLIB data compression.\n"; + } + + if (control -> LocalStreamCompression == 1 || + control -> RemoteStreamCompression == 1) + { + cerr << "Info" << ": Using ZLIB stream compression " + << control -> LocalStreamCompressionLevel + << "/" << control -> RemoteStreamCompressionLevel + << ".\n"; + } + else + { + cerr << "Info" << ": Not using ZLIB stream compression.\n"; + } + + if (control -> LocalBitrateLimit > 0) + { + cerr << "Info" << ": Using bandwidth limit of " + << bitrateLimitName << " bits per second.\n"; + } + + if (control -> PersistentCacheName != NULL) + { + cerr << "Info" << ": Using cache file '" + << control -> PersistentCachePath << "/" + << control -> PersistentCacheName << "'.\n"; + } + else + { + if (control -> PersistentCacheEnableLoad == 0 || + control -> LocalDeltaCompression == 0) + { + cerr << "Info" << ": Not using a persistent cache.\n"; + } + else + { + cerr << "Info" << ": No suitable cache file found.\n"; + } + } + + if (control -> ProxyMode == proxy_client && + (useUnixSocket > 0 || useTcpSocket > 0 || + useAgentSocket > 0)) + { + cerr << "Info" << ": Listening to X11 connections " + << "on display ':" << xPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server) + { + cerr << "Info" << ": Forwarding X11 connections " + << "to display '" << displayHost << "'.\n"; + } + + if (control -> ProxyMode == proxy_client && + useCupsSocket > 0 && cupsPort > 0) + { + cerr << "Info" << ": Listening to CUPS connections " + << "on port '" << cupsPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server && + cupsPort > 0) + { + cerr << "Info" << ": Forwarding CUPS connections " + << "to port '" << cupsPort << "'.\n"; + } + + if (control -> ProxyMode == proxy_client && + useAuxSocket > 0 && auxPort > 0) + { + cerr << "Info" << ": Listening to auxiliary X11 connections " + << "on port '" << auxPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server && + auxPort > 0) + { + cerr << "Info" << ": Forwarding auxiliary X11 connections " + << "to display '" << displayHost << "'.\n"; + } + + if (control -> ProxyMode == proxy_client && + useSmbSocket > 0 && smbPort > 0) + { + cerr << "Info" << ": Listening to SMB connections " + << "on port '" << smbPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server && + smbPort > 0) + { + cerr << "Info" << ": Forwarding SMB connections " + << "to port '" << smbPort << "'.\n"; + } + + if (control -> ProxyMode == proxy_client && + useMediaSocket > 0 && mediaPort > 0) + { + cerr << "Info" << ": Listening to multimedia connections " + << "on port '" << mediaPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server && + mediaPort > 0) + { + cerr << "Info" << ": Forwarding multimedia connections " + << "to port '" << mediaPort << "'.\n"; + } + + if (control -> ProxyMode == proxy_client && + useHttpSocket > 0 && httpPort > 0) + { + cerr << "Info" << ": Listening to HTTP connections " + << "on port '" << httpPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_server && + httpPort > 0) + { + cerr << "Info" << ": Forwarding HTTP connections " + << "to port '" << httpPort << "'.\n"; + } + + if (control -> ProxyMode == proxy_server && + useFontSocket > 0 && *fontPort != '\0') + { + cerr << "Info" << ": Listening to font server connections " + << "on port '" << fontPort << "'.\n"; + } + else if (control -> ProxyMode == proxy_client && + *fontPort != '\0') + { + cerr << "Info" << ": Forwarding font server connections " + << "to port '" << fontPort << "'.\n"; + } + + if (useSlaveSocket > 0 && slavePort > 0) + { + cerr << "Info" << ": Listening to slave connections " + << "on port '" << slavePort << "'.\n"; + } +} + +void PrintVersionInfo() +{ + cerr << "NXPROXY - " << "Version " + << control -> LocalVersionMajor << "." + << control -> LocalVersionMinor << "." + << control -> LocalVersionPatch; + + cerr << endl; +} + +void PrintCopyrightInfo() +{ + cerr << endl; + + PrintVersionInfo(); + + cerr << endl; + + cerr << GetCopyrightInfo(); + + // + // Print third party's copyright info. + // + + cerr << endl; + + cerr << GetOtherCopyrightInfo(); + + cerr << endl; +} + +void PrintOptionIgnored(const char *type, const char *name, const char *value) +{ + if (control -> ProxyMode == proxy_server) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Ignoring " << type + << " option '" << name << "' with value '" + << value << "' at " << "NX client side.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Ignoring " << type + << " option '" << name << "' with value '" + << value << "' at " << "NX client side.\n"; + } + else + { + #ifdef WARNING + *logofs << "Loop: WARNING! Ignoring " << type + << " option '" << name << "' with value '" + << value << "' at " << "NX server side.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Ignoring " << type + << " option '" << name << "' with value '" + << value << "' at " << "NX server side.\n"; + } +} + +const char *GetOptions(const char *options) +{ + if (options != NULL) + { + if (strncasecmp(options, "nx/nx,", 6) != 0 && + strncasecmp(options, "nx,", 3) != 0 && + strncasecmp(options, "nx:", 3) != 0) + { + #ifdef TEST + *logofs << "Loop: PANIC! Display options string '" << options + << "' must start with 'nx' or 'nx/nx' prefix.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Display options string '" << options + << "' must start with 'nx' or 'nx/nx' prefix.\n"; + + HandleCleanup(); + } + } + else + { + options = getenv("DISPLAY"); + } + + return options; +} + +const char *GetArg(int &argi, int argc, const char **argv) +{ + // + // Skip "-" and flag character. + // + + const char *arg = argv[argi] + 2; + + if (*arg == 0) + { + if (argi + 1 == argc) + { + return NULL; + } + else + { + argi++; + + return (*argv[argi] == '-' ? NULL : argv[argi]); + } + } + else + { + return (*arg == '-' ? NULL : arg); + } +} + +int CheckArg(const char *type, const char *name, const char *value) +{ + #ifdef TEST + *logofs << "Loop: Parsing " << type << " option '" << name + << "' with value '" << (value ? value : "(null)") + << "'.\n" << logofs_flush; + #endif + + if (value == NULL || strstr(value, "=") != NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error in " << type << " option '" + << name << "'. No value found.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error in " << type << " option '" + << name << "'. No value found.\n"; + + return -1; + } + else if (strstr(name, ",") != NULL) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Parse error at " << type << " option '" + << name << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Parse error at " << type << " option '" + << name << "'.\n"; + + return -1; + } + else if (strlen(value) >= DEFAULT_STRING_LENGTH) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Value '" << value << "' of " + << type << " option '" << name << "' exceeds length of " + << DEFAULT_STRING_LENGTH << " characters.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Value '" << value << "' of " + << type << " option '" << name << "' exceeds length of " + << DEFAULT_STRING_LENGTH << " characters.\n"; + + return -1; + } + + return 1; +} + +int ParseArg(const char *type, const char *name, const char *value) +{ + if (strcasecmp(value, "0") == 0) + { + return 0; + } + + // + // Find the base factor. + // + + double base; + + const char *id = value + strlen(value) - 1; + + if (strcasecmp(id, "g") == 0) + { + base = 1024 * 1024 * 1024; + } + else if (strcasecmp(id, "m") == 0) + { + base = 1024 * 1024; + } + else if (strcasecmp(id, "k") == 0) + { + base = 1024; + } + else if (strcasecmp(id, "b") == 0 || isdigit(*id) == 1) + { + base = 1; + } + else + { + return -1; + } + + char *string = new char[strlen(value)]; + + strncpy(string, value, strlen(value) - 1); + + *(string + (strlen(value) - 1)) = '\0'; + + #ifdef TEST + + *logofs << "Loop: Parsing integer option '" << name + << "' from string '" << string << "' with base set to "; + + switch (tolower(*id)) + { + case 'k': + case 'm': + case 'g': + { + *logofs << (char) toupper(*id); + } + break; + } + + *logofs << ".\n" << logofs_flush; + + #endif + + double result = atof(string) * base; + + if (result < 0 || result > (((unsigned) -1) >> 1)) + { + delete [] string; + + return -1; + } + + delete [] string; + + #ifdef TEST + *logofs << "Loop: Integer option parsed to '" + << (int) result << "'.\n" << logofs_flush; + #endif + + return (int) result; +} + +int ValidateArg(const char *type, const char *name, const char *value) +{ + int number = atoi(value); + + if (number < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Invalid " << type + << " option '" << name << "' with value '" + << value << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid " << type + << " option '" << name << "' with value '" + << value << "'.\n"; + + HandleCleanup(); + } + + return number; +} + +int LowercaseArg(const char *type, const char *name, char *value) +{ + char *next = value; + + while (*next != '\0') + { + *next = tolower(*next); + + next++; + } + + return 1; +} + +int CheckSignal(int signal) +{ + // + // Return 1 if the signal needs to be handled + // by the proxy, 2 if the signal just needs to + // be blocked to avoid interrupting a system + // call. + // + + switch (signal) + { + case SIGCHLD: + case SIGUSR1: + case SIGUSR2: + case SIGHUP: + case SIGINT: + case SIGTERM: + case SIGPIPE: + case SIGALRM: + { + return 1; + } + case SIGVTALRM: + case SIGWINCH: + case SIGIO: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + { + return 2; + } + default: + { + #ifdef __CYGWIN32__ + + // + // This signal can be raised by the Cygwin + // library. + // + + if (signal == 12) + { + return 1; + } + + #endif + + return 0; + } + } +} + +static void PrintUsageInfo(const char *option, int error) +{ + if (error == 1) + { + cerr << "Error" << ": Invalid command line option '" << option << "'.\n"; + } + + cerr << GetUsageInfo(); + + if (error == 1) + { + cerr << "Error" << ": NX transport initialization failed.\n"; + } +} + +static void handleCheckSessionInLoop() +{ + // + // Check if we completed the shutdown procedure + // and the remote confirmed the shutdown. The + // tear down should be always initiated by the + // agent, but the X server side may unilateral- + // ly shut down the link without our permission. + // + + if (proxy -> getShutdown() > 0) + { + #ifdef TEST + *logofs << "Loop: End of NX transport requested " + << "by remote.\n" << logofs_flush; + #endif + + handleTerminatingInLoop(); + + if (control -> ProxyMode == proxy_server) + { + #ifdef TEST + *logofs << "Loop: Bytes received so far are " + << (unsigned long long) statistics -> getBytesIn() + << ".\n" << logofs_flush; + #endif + + if (statistics -> getBytesIn() < 1024) + { + cerr << "Info" << ": Your session was closed before reaching " + << "a usable state.\n"; + cerr << "Info" << ": This can be due to the local X server " + << "refusing access to the client.\n"; + cerr << "Info" << ": Please check authorization provided " + << "by the remote X application.\n"; + } + } + + #ifdef TEST + *logofs << "Loop: Shutting down the NX transport.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + else if (proxy -> handlePing() < 0) + { + #ifdef TEST + *logofs << "Loop: Failure handling the ping for " + << "proxy FD#" << proxyFD << ".\n" + << logofs_flush; + #endif + + HandleShutdown(); + } + + // + // Check if the watchdog has exited and we didn't + // get the SIGCHLD. This can happen if the parent + // has overridden our signal handlers. + // + + if (IsRunning(lastWatchdog) && CheckProcess(lastWatchdog, "watchdog") == 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Watchdog is gone unnoticed. " + << "Setting the last signal to SIGTERM.\n" + << logofs_flush; + #endif + + lastSignal = SIGTERM; + + #ifdef WARNING + *logofs << "Loop: WARNING! Resetting pid of last " + << "watchdog process.\n" << logofs_flush; + #endif + + SetNotRunning(lastWatchdog); + } + + // + // Let the client proxy find out if the agent's + // channel is gone. This is the normal shutdown + // procedure in the case of an internal connect- + // ion to the agent. + // + + int cleanup = 0; + + if (control -> ProxyMode == proxy_client && + agent != NULL && proxy -> getType(agentFD[1]) == + channel_none && lastKill == 0 && lastDestroy == 1) + { + #ifdef TEST + *logofs << "Loop: End of NX transport requested " + << "by agent.\n" << logofs_flush; + #endif + + #ifdef TEST + *logofs << "Loop: Bytes sent so far are " + << (unsigned long long) statistics -> getBytesOut() + << ".\n" << logofs_flush; + #endif + + if (statistics -> getBytesOut() < 1024) + { + cerr << "Info" << ": Your session has died before reaching " + << "an usable state.\n"; + cerr << "Info" << ": This can be due to the remote X server " + << "refusing access to the client.\n"; + cerr << "Info" << ": Please check the authorization provided " + << "by your X application.\n"; + } + + cleanup = 1; + } + + // + // Check if the user requested the end of the + // session by sending a signal to the proxy. + // All signals are handled in the main loop + // so we need to reset the value to get ready + // for the next iteration. + // + + int signal = 0; + + if (lastSignal != 0) + { + switch (lastSignal) + { + case SIGCHLD: + case SIGUSR1: + case SIGUSR2: + { + break; + } + default: + { + signal = lastSignal; + + cleanup = 1; + + break; + } + } + + lastSignal = 0; + } + + if (cleanup == 1) + { + // + // The first time termination signal is received + // disable all further connections, close down any + // X channel and wait for a second signal. + // + + if (lastKill == 0) + { + // + // Don't print a message if cleanup is + // due to normal termination of agent. + // + + if (signal != 0) + { + #ifdef TEST + *logofs << "Loop: End of NX transport requested by signal '" + << signal << "' '" << DumpSignal(signal) + << "'.\n" << logofs_flush; + #endif + + handleTerminatingInLoop(); + } + + // + // Disable any further connection. + // + + CleanupListeners(); + + // + // Close all the remaining X channels and + // let proxies save their persistent cache + // on disk. + // + + CleanupConnections(); + + // + // We'll need to wait for the X channels + // to be shut down before waiting for the + // cleanup signal. + // + + lastKill = 1; + } + else if (lastKill == 2) + { + #ifdef TEST + *logofs << "Loop: Shutting down the NX transport.\n" + << logofs_flush; + #endif + + proxy -> handleShutdown(); + + HandleCleanup(); + } + } + + if (lastKill == 1 && proxy -> getChannels(channel_x11) == 0) + { + // + // Save the message stores to the + // persistent cache. + // + + proxy -> handleSave(); + + // + // Run a watchdog process so we can finally + // give up at the time the watchdog exits. + // + + if (IsNotRunning(lastWatchdog)) + { + int timeout = control -> CleanupTimeout; + + if (timeout > 0) + { + if (proxy -> getChannels() == 0) + { + timeout = 500; + } + + #ifdef TEST + *logofs << "Loop: Starting watchdog process with timeout " + << "of " << timeout << " Ms.\n" + << logofs_flush; + #endif + } + #ifdef TEST + else + { + *logofs << "Loop: Starting watchdog process without " + << "a timeout.\n" << logofs_flush; + } + #endif + + lastWatchdog = NXTransWatchdog(timeout); + + if (IsFailed(lastWatchdog)) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't start the NX watchdog " + << "process in shutdown.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't start the NX watchdog " + << "process in shutdown.\n"; + + HandleCleanup(); + } + #ifdef TEST + else + { + *logofs << "Loop: Watchdog started with pid '" + << lastWatchdog << "'.\n" << logofs_flush; + } + #endif + } + else + { + #ifdef PANIC + *logofs << "Loop: PANIC! Previous watchdog detected " + << "in shutdown with pid '" << lastWatchdog + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Previous watchdog detected " + << "in shutdown with pid '" << lastWatchdog + << "'.\n"; + + HandleCleanup(); + } + + if (control -> CleanupTimeout > 0) + { + #ifdef TEST + *logofs << "Loop: Waiting the cleanup timeout to complete.\n" + << logofs_flush; + #endif + + cerr << "Info" << ": Waiting the cleanup timeout to complete.\n"; + } + else + { + // + // The NX server will kill the watchdog + // process after having shut down the + // service channels. + // + + cerr << "Info" << ": Watchdog running with pid '" << lastWatchdog + << "'.\n"; + + #ifdef TEST + *logofs << "Loop: Waiting the watchdog process to complete.\n" + << logofs_flush; + #endif + + cerr << "Info" << ": Waiting the watchdog process to complete.\n"; + } + + lastKill = 2; + } +} + +static void handleCheckBitrateInLoop() +{ + static long int slept = 0; + + #ifdef TEST + *logofs << "Loop: Bitrate is " << statistics -> getBitrateInShortFrame() + << " B/s and " << statistics -> getBitrateInLongFrame() + << " B/s in " << control -> ShortBitrateTimeFrame / 1000 + << "/" << control -> LongBitrateTimeFrame / 1000 + << " seconds timeframes.\n" << logofs_flush; + #endif + + // + // This can be improved. We may not jump out + // of the select often enough to guarantee + // the necessary accuracy. + // + + if (control -> LocalBitrateLimit > 0) + { + #ifdef TEST + *logofs << "Loop: Calculating bandwidth usage with limit " + << control -> LocalBitrateLimit << ".\n" + << logofs_flush; + #endif + + int reference = (statistics -> getBitrateInLongFrame() + + statistics -> getBitrateInShortFrame()) / 2; + + if (reference > control -> LocalBitrateLimit) + { + double ratio = ((double) reference) / + ((double) control -> LocalBitrateLimit); + + if (ratio > 1.2) + { + ratio = 1.2; + } + + slept += (unsigned int) (pow(50000, ratio) / 1000); + + if (slept > 2000) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Sleeping due to " + << "reference bitrate of " << reference + << " B/s.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Sleeping due to " + << "reference bitrate of " << reference + << " B/s.\n"; + + slept %= 2000; + } + + T_timestamp idleTs = getNewTimestamp(); + + usleep((unsigned int) pow(50000, ratio)); + + int diffTs = diffTimestamp(idleTs, getNewTimestamp()); + + statistics -> addIdleTime(diffTs); + + statistics -> subReadTime(diffTs); + } + } +} + +#if defined(TEST) || defined(INFO) + +static void handleCheckStateInLoop(int &setFDs) +{ + int fdLength; + int fdPending; + int fdSplits; + + for (int j = 0; j < setFDs; j++) + { + if (j != proxyFD) + { + fdPending = proxy -> getPending(j); + + if (fdPending > 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Buffer for descriptor FD#" + << j << " has pending bytes to read.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + + fdLength = proxy -> getLength(j); + + if (fdLength > 0) + { + #ifdef TEST + *logofs << "Loop: WARNING! Buffer for descriptor FD#" + << j << " has " << fdLength << " bytes to write.\n" + << logofs_flush; + #endif + } + } + } + + fdPending = proxy -> getPending(proxyFD); + + if (fdPending > 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Buffer for proxy descriptor FD#" + << proxyFD << " has pending bytes to read.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + + fdLength = proxy -> getFlushable(proxyFD); + + if (fdLength > 0) + { + if (control -> FlushPolicy == policy_immediate && + proxy -> getBlocked(proxyFD) == 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Buffer for proxy descriptor FD#" + << proxyFD << " has " << fdLength << " bytes " + << "to write with policy 'immediate'.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + else + { + #ifdef TEST + *logofs << "Loop: WARNING! Buffer for proxy descriptor FD#" + << proxyFD << " has " << fdLength << " bytes " + << "to write.\n" << logofs_flush; + #endif + } + } + + fdSplits = proxy -> getSplitSize(); + + if (fdSplits > 0) + { + #ifdef WARNING + *logofs << "Loop: WARNING! Proxy descriptor FD#" << proxyFD + << " has " << fdSplits << " splits to send.\n" + << logofs_flush; + #endif + } +} + +static void handleCheckSelectInLoop(int &setFDs, fd_set &readSet, + fd_set &writeSet, T_timestamp selectTs) +{ + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Maximum descriptors is [" + << setFDs << "] at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + int i; + + if (setFDs > 0) + { + i = 0; + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Selected for read are "; + #endif + + for (int j = 0; j < setFDs; j++) + { + if (FD_ISSET(j, &readSet)) + { + #if defined(TEST) || defined(INFO) + *logofs << "[" << j << "]" << logofs_flush; + #endif + + i++; + } + } + + if (i > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << ".\n" << logofs_flush; + #endif + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "[none].\n" << logofs_flush; + #endif + } + + i = 0; + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Selected for write are "; + #endif + + for (int j = 0; j < setFDs; j++) + { + if (FD_ISSET(j, &writeSet)) + { + #if defined(TEST) || defined(INFO) + *logofs << "[" << j << "]" << logofs_flush; + #endif + + i++; + } + } + + if (i > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << ".\n" << logofs_flush; + #endif + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "[none].\n" << logofs_flush; + #endif + } + } + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Select timeout is " + << selectTs.tv_sec << " S and " + << (double) selectTs.tv_usec / 1000 + << " Ms.\n" << logofs_flush; + #endif +} + +static void handleCheckResultInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, + fd_set &writeSet, struct timeval &selectTs, + struct timeval &startTs) +{ + int diffTs = diffTimestamp(startTs, getNewTimestamp()); + + #if defined(TEST) || defined(INFO) + + if (diffTs >= (control -> PingTimeout - + (control -> LatencyTimeout * 4))) + { + *logofs << "Loop: Select result is [" << resultFDs + << "] at " << strMsTimestamp() << " with no " + << "communication within " << diffTs + << " Ms.\n" << logofs_flush; + } + else + { + *logofs << "Loop: Select result is [" << resultFDs + << "] error is [" << errorFDs << "] at " + << strMsTimestamp() << " after " << diffTs + << " Ms.\n" << logofs_flush; + } + + #endif + + int i; + + if (resultFDs > 0) + { + i = 0; + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Selected for read are "; + #endif + + for (int j = 0; j < setFDs; j++) + { + if (FD_ISSET(j, &readSet)) + { + #if defined(TEST) || defined(INFO) + *logofs << "[" << j << "]" << logofs_flush; + #endif + + i++; + } + } + + if (i > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << ".\n" << logofs_flush; + #endif + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "[none].\n" << logofs_flush; + #endif + } + + i = 0; + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Selected for write are "; + #endif + + for (int j = 0; j < setFDs; j++) + { + if (FD_ISSET(j, &writeSet)) + { + #if defined(TEST) || defined(INFO) + *logofs << "[" << j << "]" << logofs_flush; + #endif + + i++; + } + } + + if (i > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << ".\n" << logofs_flush; + #endif + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "[none].\n" << logofs_flush; + #endif + } + } +} + +#endif + +static void handleCheckSessionInConnect() +{ + #ifdef TEST + *logofs << "Loop: Going to check session in connect.\n" + << logofs_flush; + #endif + + if (control -> ProxyMode == proxy_client) + { + HandleAlert(FAILED_PROXY_CONNECTION_CLIENT_ALERT, 1); + } + else if (IsNotRunning(lastDialog)) + { + HandleAlert(FAILED_PROXY_CONNECTION_SERVER_ALERT, 1); + } + + handleAlertInLoop(); +} + +static void handleStatisticsInLoop() +{ + if (lastSignal == 0) + { + return; + } + + int mode = NO_STATS; + + if (control -> EnableStatistics == 1) + { + if (lastSignal == SIGUSR1) + { + // + // Print overall statistics. + // + + mode = TOTAL_STATS; + } + else if (lastSignal == SIGUSR2) + { + // + // Print partial statistics. + // + + mode = PARTIAL_STATS; + } + + if (mode == TOTAL_STATS || mode == PARTIAL_STATS) + { + #ifdef TEST + *logofs << "Loop: Going to request proxy statistics " + << "with signal '" << DumpSignal(lastSignal) + << "'.\n" << logofs_flush; + #endif + + if (proxy != NULL) + { + if (ReopenLogFile(statsFileName, statofs, 0) < 0) + { + HandleCleanup(); + } + + proxy -> handleStatistics(mode, statofs); + } + } + } +} + +static void handleNegotiationInLoop(int &setFDs, fd_set &readSet, + fd_set &writeSet, T_timestamp &selectTs) +{ + int yield = 0; + + while (yield == 0) + { + #ifdef TEST + *logofs << "Loop: Going to run a new negotiation loop " + << "with stage " << control -> ProxyStage + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + switch (control -> ProxyStage) + { + case stage_undefined: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_undefined" << "'.\n" + << logofs_flush; + #endif + + control -> ProxyStage = stage_initializing; + + break; + } + case stage_initializing: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_initializing" << "'.\n" + << logofs_flush; + #endif + + InitBeforeNegotiation(); + + control -> ProxyStage = stage_connecting; + + break; + } + case stage_connecting: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_connecting" << "'.\n" + << logofs_flush; + #endif + + SetupProxyConnection(); + + control -> ProxyStage = stage_connected; + + break; + } + case stage_connected: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_connected" << "'.\n" + << logofs_flush; + #endif + + // + // Server side proxy must always be the one that + // sends its version and options first, so, in + // some way, client side can be the the one that + // has the last word on the matter. + // + + if (control -> ProxyMode == proxy_server) + { + // + // Check if we have been listening for a + // forwarder. In this case it will have to + // authenticate itself. + // + + if (WE_LISTEN_FORWARDER) + { + control -> ProxyStage = stage_waiting_forwarder_version; + + break; + } + + control -> ProxyStage = stage_sending_proxy_options; + } + else + { + // + // The X client side is the side that has to wait + // for the authorization cookie and any remote + // option. + // + + control -> ProxyStage = stage_waiting_proxy_version; + } + + break; + } + case stage_sending_proxy_options: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_sending_proxy_options" << "'.\n" + << logofs_flush; + #endif + + if (SendProxyOptions(proxyFD) < 0) + { + goto handleNegotiationInLoopError; + } + + if (control -> ProxyMode == proxy_server) + { + control -> ProxyStage = stage_waiting_proxy_version; + } + else + { + control -> ProxyStage = stage_sending_proxy_caches; + } + + break; + } + case stage_waiting_forwarder_version: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_waiting_forwarder_version" << "'.\n" + << logofs_flush; + #endif + + int result = ReadForwarderVersion(proxyFD); + + if (result == 0) + { + yield = 1; + } + else if (result == 1) + { + control -> ProxyStage = stage_waiting_forwarder_options; + } + else + { + goto handleNegotiationInLoopError; + } + + break; + } + case stage_waiting_forwarder_options: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_waiting_forwarder_options" << "'.\n" + << logofs_flush; + #endif + + int result = ReadForwarderOptions(proxyFD); + + if (result == 0) + { + yield = 1; + } + else if (result == 1) + { + control -> ProxyStage = stage_sending_proxy_options; + } + else + { + goto handleNegotiationInLoopError; + } + + break; + } + case stage_waiting_proxy_version: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_waiting_proxy_version" << "'.\n" + << logofs_flush; + #endif + + int result = ReadProxyVersion(proxyFD); + + if (result == 0) + { + yield = 1; + } + else if (result == 1) + { + control -> ProxyStage = stage_waiting_proxy_options; + } + else + { + goto handleNegotiationInLoopError; + } + + break; + } + case stage_waiting_proxy_options: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_waiting_proxy_options" << "'.\n" + << logofs_flush; + #endif + + int result = ReadProxyOptions(proxyFD); + + if (result == 0) + { + yield = 1; + } + else if (result == 1) + { + if (control -> ProxyMode == proxy_server) + { + control -> ProxyStage = stage_waiting_proxy_caches; + } + else + { + control -> ProxyStage = stage_sending_proxy_options; + } + } + else + { + goto handleNegotiationInLoopError; + } + + break; + } + case stage_sending_proxy_caches: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_sending_proxy_caches" << "'.\n" + << logofs_flush; + #endif + + if (SendProxyCaches(proxyFD) < 0) + { + goto handleNegotiationInLoopError; + } + + if (control -> ProxyMode == proxy_server) + { + control -> ProxyStage = stage_operational; + } + else + { + control -> ProxyStage = stage_waiting_proxy_caches; + } + + break; + } + case stage_waiting_proxy_caches: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_waiting_proxy_caches" << "'.\n" + << logofs_flush; + #endif + + int result = ReadProxyCaches(proxyFD); + + if (result == 0) + { + yield = 1; + } + else if (result == 1) + { + if (control -> ProxyMode == proxy_server) + { + control -> ProxyStage = stage_sending_proxy_caches; + } + else + { + control -> ProxyStage = stage_operational; + } + } + else + { + goto handleNegotiationInLoopError; + } + + break; + } + case stage_operational: + { + #ifdef TEST + *logofs << "Loop: Handling negotiation with '" + << "stage_operational" << "'.\n" + << logofs_flush; + #endif + + InitAfterNegotiation(); + + yield = 1; + + break; + } + default: + { + #ifdef PANIC + *logofs << "Loop: PANIC! Unmanaged case '" << control -> ProxyStage + << "' while handling negotiation.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unmanaged case '" << control -> ProxyStage + << "' while handling negotiation.\n"; + + HandleCleanup(); + } + } + } + + // + // Check if the user requested the end of + // the session. + // + + if (CheckAbort() != 0) + { + HandleCleanup(); + } + + // + // Select the proxy descriptor so that we + // can proceed negotiating the session. + // + + FD_SET(proxyFD, &readSet); + + if (proxyFD >= setFDs) + { + setFDs = proxyFD + 1; + } + + setMinTimestamp(selectTs, control -> PingTimeout); + + #ifdef TEST + *logofs << "Loop: Selected proxy FD#" << proxyFD << " in negotiation " + << "phase with timeout of " << selectTs.tv_sec << " S and " + << selectTs.tv_usec << " Ms.\n" << logofs_flush; + #endif + + return; + +handleNegotiationInLoopError: + + #ifdef PANIC + *logofs << "Loop: PANIC! Failure negotiating the session in stage '" + << control -> ProxyStage << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Failure negotiating the session in stage '" + << control -> ProxyStage << "'.\n"; + + + if (control -> ProxyMode == proxy_server && + control -> ProxyStage == stage_waiting_proxy_version) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Wrong version or invalid session " + << "authentication cookie.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Wrong version or invalid session " + << "authentication cookie.\n"; + } + + handleTerminatingInLoop(); + + HandleCleanup(); +} + +static void handleTerminatingInLoop() +{ + if (getpid() == lastProxy) + { + if (control -> ProxyStage < stage_terminating) + { + if (agent == NULL) + { + cerr << "Session" << ": Terminating session at '" + << strTimestamp() << "'.\n"; + } + + control -> ProxyStage = stage_terminating; + } + } +} + +static void handleTerminatedInLoop() +{ + if (getpid() == lastProxy) + { + if (control -> ProxyStage < stage_terminated) + { + if (agent == NULL) + { + cerr << "Session" << ": Session terminated at '" + << strTimestamp() << "'.\n"; + } + + control -> ProxyStage = stage_terminated; + } + } +} + +static void handleAlertInLoop() +{ + if (lastAlert.code == 0) + { + return; + } + + if (lastAlert.local == 0 && + (lastAlert.code > LAST_PROTO_STEP_6_ALERT && + control -> isProtoStep7() == 0)) + { + // + // The remote proxy would be unable + // to handle the alert. + // + + #ifdef WARNING + *logofs << "Loop: WARNING! Ignoring unsupported alert " + << "with code '" << lastAlert.code << "'.\n" + << logofs_flush; + #endif + } + else if (lastAlert.local == 0) + { + if (proxy != NULL) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Requesting a remote alert with code '" + << lastAlert.code << "'.\n" << logofs_flush; + #endif + + if (proxy -> handleAlert(lastAlert.code) < 0) + { + HandleShutdown(); + } + } + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Handling a local alert with code '" + << lastAlert.code << "'.\n" << logofs_flush; + #endif + + if (control -> ProxyMode == proxy_client) + { + // + // If we are at X client side and server + // proxy is not responding, we don't have + // any possibility to interact with user. + // + + if (lastAlert.code != CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT && + lastAlert.code != RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT && + lastAlert.code != FAILED_PROXY_CONNECTION_CLIENT_ALERT) + { + // + // Let the server proxy show the dialog. + // + + if (proxy != NULL && + proxy -> handleAlert(lastAlert.code) < 0) + { + HandleShutdown(); + } + } + } + else + { + char caption[DEFAULT_STRING_LENGTH]; + + strcpy(caption, ALERT_CAPTION_PREFIX); + + int length = strlen(sessionId); + + // + // Get rid of the trailing MD5 from session id. + // + + if (length > (MD5_LENGTH * 2 + 1) && + *(sessionId + (length - (MD5_LENGTH * 2 + 1))) == '-') + { + strncat(caption, sessionId, length - (MD5_LENGTH * 2 + 1)); + } + else + { + strcat(caption, sessionId); + } + + // + // Use the display to which we are forwarding + // the remote X connections. + // + + char *display = displayHost; + + int replace = 1; + int local = 1; + + const char *message; + const char *type; + + switch (lastAlert.code) + { + case CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT: + { + message = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_STRING; + type = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_TYPE; + + break; + } + case CLOSE_DEAD_X_CONNECTION_SERVER_ALERT: + { + message = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_STRING; + type = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_TYPE; + + break; + } + case CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT: + { + message = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING; + type = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE; + + break; + } + case RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT: + { + message = RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING; + type = RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE; + + break; + } + case CLOSE_UNRESPONSIVE_X_SERVER_ALERT: + { + message = CLOSE_UNRESPONSIVE_X_SERVER_ALERT_STRING; + type = CLOSE_UNRESPONSIVE_X_SERVER_ALERT_TYPE; + + break; + } + case WRONG_PROXY_VERSION_ALERT: + { + message = WRONG_PROXY_VERSION_ALERT_STRING; + type = WRONG_PROXY_VERSION_ALERT_TYPE; + + break; + } + case FAILED_PROXY_CONNECTION_SERVER_ALERT: + { + message = FAILED_PROXY_CONNECTION_SERVER_ALERT_STRING; + type = FAILED_PROXY_CONNECTION_SERVER_ALERT_TYPE; + + break; + } + case MISSING_PROXY_CACHE_ALERT: + { + message = MISSING_PROXY_CACHE_ALERT_STRING; + type = MISSING_PROXY_CACHE_ALERT_TYPE; + + break; + } + case ABORT_PROXY_CONNECTION_ALERT: + { + message = ABORT_PROXY_CONNECTION_ALERT_STRING; + type = ABORT_PROXY_CONNECTION_ALERT_TYPE; + + break; + } + case DISPLACE_MESSAGE_ALERT: + { + message = DISPLACE_MESSAGE_ALERT_STRING; + type = DISPLACE_MESSAGE_ALERT_TYPE; + + break; + } + case GREETING_MESSAGE_ALERT: + { + message = GREETING_MESSAGE_ALERT_STRING; + type = GREETING_MESSAGE_ALERT_TYPE; + + break; + } + case START_RESUME_SESSION_ALERT: + { + message = START_RESUME_SESSION_ALERT_STRING; + type = START_RESUME_SESSION_ALERT_TYPE; + + break; + } + case FAILED_RESUME_DISPLAY_ALERT: + { + message = FAILED_RESUME_DISPLAY_ALERT_STRING; + type = FAILED_RESUME_DISPLAY_ALERT_TYPE; + + break; + } + case FAILED_RESUME_DISPLAY_BROKEN_ALERT: + { + message = FAILED_RESUME_DISPLAY_BROKEN_STRING; + type = FAILED_RESUME_DISPLAY_BROKEN_TYPE; + + break; + } + case FAILED_RESUME_VISUALS_ALERT: + { + message = FAILED_RESUME_VISUALS_ALERT_STRING; + type = FAILED_RESUME_VISUALS_ALERT_TYPE; + + break; + } + case FAILED_RESUME_COLORMAPS_ALERT: + { + message = FAILED_RESUME_COLORMAPS_ALERT_STRING; + type = FAILED_RESUME_COLORMAPS_ALERT_TYPE; + + break; + } + case FAILED_RESUME_PIXMAPS_ALERT: + { + message = FAILED_RESUME_PIXMAPS_ALERT_STRING; + type = FAILED_RESUME_PIXMAPS_ALERT_TYPE; + + break; + } + case FAILED_RESUME_DEPTHS_ALERT: + { + message = FAILED_RESUME_DEPTHS_ALERT_STRING; + type = FAILED_RESUME_DEPTHS_ALERT_TYPE; + + break; + } + case FAILED_RESUME_RENDER_ALERT: + { + message = FAILED_RESUME_RENDER_ALERT_STRING; + type = FAILED_RESUME_RENDER_ALERT_TYPE; + + break; + } + case FAILED_RESUME_FONTS_ALERT: + { + message = FAILED_RESUME_FONTS_ALERT_STRING; + type = FAILED_RESUME_FONTS_ALERT_TYPE; + + break; + } + case INTERNAL_ERROR_ALERT: + { + message = INTERNAL_ERROR_ALERT_STRING; + type = INTERNAL_ERROR_ALERT_TYPE; + + break; + } + case ABORT_PROXY_NEGOTIATION_ALERT: + { + message = ABORT_PROXY_NEGOTIATION_ALERT_STRING; + type = ABORT_PROXY_NEGOTIATION_ALERT_TYPE; + + break; + } + case ABORT_PROXY_SHUTDOWN_ALERT: + { + message = ABORT_PROXY_SHUTDOWN_ALERT_STRING; + type = ABORT_PROXY_SHUTDOWN_ALERT_TYPE; + + break; + } + case FAILED_XDMCP_CONNECTION_ALERT: + { + message = FAILED_XDMCP_CONNECTION_ALERT_STRING; + type = FAILED_XDMCP_CONNECTION_ALERT_TYPE; + + break; + } + default: + { + if (lastAlert.code > LAST_PROTO_STEP_7_ALERT) + { + #ifdef WARNING + *logofs << "Loop: WARNING! An unrecognized alert type '" + << lastAlert.code << "' was requested.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": An unrecognized alert type '" + << lastAlert.code << "' was requested.\n"; + } + #ifdef WARNING + else + { + *logofs << "Loop: WARNING! Ignoring obsolete alert type '" + << lastAlert.code << "'.\n" << logofs_flush; + } + #endif + + message = NULL; + type = NULL; + + replace = 0; + + break; + } + } + + if (replace == 1 && IsRunning(lastDialog)) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Killing the previous dialog with pid '" + << lastDialog << "'.\n" << logofs_flush; + #endif + + // + // The client ignores the TERM signal + // on Windows. + // + + #ifdef __CYGWIN32__ + + KillProcess(lastDialog, "dialog", SIGKILL, 0); + + #else + + KillProcess(lastDialog, "dialog", SIGTERM, 0); + + #endif + + SetNotRunning(lastDialog); + + if (proxy != NULL) + { + proxy -> handleResetAlert(); + } + } + + if (message != NULL && type != NULL) + { + lastDialog = NXTransDialog(caption, message, 0, type, local, display); + + if (IsFailed(lastDialog)) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Can't start the NX dialog process.\n" + << logofs_flush; + #endif + + SetNotRunning(lastDialog); + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "Loop: Dialog started with pid '" + << lastDialog << "'.\n" << logofs_flush; + } + #endif + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "Loop: No new dialog required for code '" + << lastAlert.code << "'.\n" << logofs_flush; + } + #endif + } + } + + // + // Reset state. + // + + lastAlert.code = 0; + lastAlert.local = 0; +} + +static inline void handleSetAgentInLoop(int &setFDs, fd_set &readSet, + fd_set &writeSet, struct timeval &selectTs) +{ + #ifdef TEST + *logofs << "Loop: Preparing the masks for the agent descriptors.\n" + << logofs_flush; + #endif + + agent -> saveChannelState(); + + agent -> saveReadMask(&readSet); + agent -> saveWriteMask(&writeSet); + + if (control -> ProxyStage >= stage_operational) + { + if (agent -> remoteCanRead(&readSet) || + agent -> remoteCanWrite(&writeSet) || + agent -> localCanRead() || + agent -> proxyCanRead()) + { + #ifdef TEST + *logofs << "Loop: Setting a null timeout with agent descriptors ready.\n" + << logofs_flush; + #endif + + // + // Force a null timeout so we'll bail out + // of the select immediately. We will ac- + // comodate the result code later. + // + + selectTs.tv_sec = 0; + selectTs.tv_usec = 0; + } + } + + #ifdef TEST + *logofs << "Loop: Clearing the read and write agent descriptors.\n" + << logofs_flush; + #endif + + agent -> clearReadMask(&readSet); + agent -> clearWriteMask(&writeSet); +} + +static inline void handleAgentInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, + fd_set &writeSet, struct timeval &selectTs) +{ + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Setting proxy and local agent descriptors.\n" + << logofs_flush; + #endif + + // + // Check if I/O is possible on the local + // agent or the proxy descriptor. + // + + if (resultFDs >= 0) + { + // + // Save if the proxy can read from the + // the agent descriptor. + // + + agent -> saveChannelState(); + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Values were resultFDs " << resultFDs + << " errorFDs " << errorFDs << " setFDs " + << setFDs << ".\n" << logofs_flush; + #endif + + if (agent -> localCanRead() == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Setting agent descriptor FD#" << agent -> + getLocalFd() << " as ready to read.\n" + << logofs_flush; + #endif + + agent -> setLocalRead(&readSet, &resultFDs); + } + + #if defined(TEST) || defined(INFO) + + if (agent -> proxyCanRead(&readSet) == 0 && + agent -> proxyCanRead() == 1) + { + *logofs << "Loop: WARNING! Can read from proxy FD#" + << proxyFD << " but the descriptor " + << "is not selected.\n" << logofs_flush; + } + + if (agent -> proxyCanRead(&readSet) == 1) + { + *logofs << "Loop: Setting proxy descriptor FD#" << agent -> + getProxyFd() << " as ready to read.\n" + << logofs_flush; + } + + #endif + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Values are now resultFDs " << resultFDs + << " errorFDs " << errorFDs << " setFDs " + << setFDs << ".\n" << logofs_flush; + #endif + } +} + +static inline void handleAgentLateInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet, + fd_set &writeSet, struct timeval &selectTs) +{ + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Setting remote agent descriptors.\n" + << logofs_flush; + #endif + + // + // We reset the masks before calling our select. + // We now set the descriptors that are ready but + // only if they were set in the original mask. + // We do this after having executed our loop as + // we may have produced more data and the agent + // descriptors may have become readable or writ- + // able in the meanwhile. + // + + if (resultFDs >= 0) + { + // + // Save if the proxy can read from the + // the agent descriptor. + // + + agent -> saveChannelState(); + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Values were resultFDs " << resultFDs + << " errorFDs " << errorFDs << " setFDs " + << setFDs << ".\n" << logofs_flush; + #endif + + if (agent -> remoteCanRead(agent -> + getSavedReadMask()) == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Setting agent descriptor FD#" << agent -> + getRemoteFd() << " as ready to read.\n" + << logofs_flush; + #endif + + agent -> setRemoteRead(&readSet, &resultFDs); + } + + if (agent -> remoteCanWrite(agent -> + getSavedWriteMask()) == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Setting agent descriptor FD#" << agent -> + getRemoteFd() << " as ready to write.\n" + << logofs_flush; + #endif + + agent -> setRemoteWrite(&writeSet, &resultFDs); + } + + #if defined(TEST) || defined(INFO) + *logofs << "Loop: Values are now resultFDs " << resultFDs + << " errorFDs " << errorFDs << " setFDs " + << setFDs << ".\n" << logofs_flush; + #endif + } +} + +static inline void handleReadableInLoop(int &resultFDs, fd_set &readSet) +{ + if (resultFDs > 0) + { + T_channel_type type = channel_none; + + const char *label = NULL; + int domain = -1; + int fd = -1; + + if (tcpFD != -1 && FD_ISSET(tcpFD, &readSet)) + { + type = channel_x11; + label = "X"; + domain = AF_INET; + fd = tcpFD; + + resultFDs--; + } + + if (unixFD != -1 && FD_ISSET(unixFD, &readSet)) + { + type = channel_x11; + label = "X"; + domain = AF_UNIX; + fd = unixFD; + + resultFDs--; + } + + if (cupsFD != -1 && FD_ISSET(cupsFD, &readSet)) + { + type = channel_cups; + label = "CUPS"; + domain = AF_INET; + fd = cupsFD; + + resultFDs--; + } + + if (auxFD != -1 && FD_ISSET(auxFD, &readSet)) + { + // + // Starting from version 1.5.0 we create real X + // connections for the keyboard channel, so they + // can use the fake authorization cookie. This + // means that there is not such a thing like a + // channel_aux anymore. + // + + type = channel_x11; + label = "auxiliary X11"; + domain = AF_INET; + fd = auxFD; + + resultFDs--; + } + + if (smbFD != -1 && FD_ISSET(smbFD, &readSet)) + { + type = channel_smb; + label = "SMB"; + domain = AF_INET; + fd = smbFD; + + resultFDs--; + } + + if (mediaFD != -1 && FD_ISSET(mediaFD, &readSet)) + { + type = channel_media; + label = "media"; + domain = AF_INET; + fd = mediaFD; + + resultFDs--; + } + + if (httpFD != -1 && FD_ISSET(httpFD, &readSet)) + { + type = channel_http; + label = "HTTP"; + domain = AF_INET; + fd = httpFD; + + resultFDs--; + } + + if (fontFD != -1 && FD_ISSET(fontFD, &readSet)) + { + type = channel_font; + label = "font server"; + domain = AF_INET; + fd = fontFD; + + resultFDs--; + } + + if (slaveFD != -1 && FD_ISSET(slaveFD, &readSet)) + { + type = channel_slave; + label = "slave"; + domain = AF_INET; + fd = slaveFD; + + resultFDs--; + } + + if (type != channel_none) + { + int newFD = AcceptConnection(fd, domain, label); + + if (newFD != -1) + { + if (proxy -> handleNewConnection(type, newFD) < 0) + { + #ifdef PANIC + *logofs << "Loop: PANIC! Error creating new " << label + << " connection.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Error creating new " << label + << " connection.\n"; + + close(newFD); + + // + // Don't kill the proxy in the case of an error. + // + // HandleCleanup(); + // + } + else if (proxy -> getReadable(newFD) > 0) + { + // + // Add the descriptor, so we can try + // to read immediately. + // + + #ifdef TEST + *logofs << "Loop: Trying to read immediately " + << "from descriptor FD#" << newFD + << ".\n" << logofs_flush; + #endif + + FD_SET(newFD, &readSet); + + resultFDs++; + } + #ifdef TEST + else + { + *logofs << "Loop: Nothing to read immediately " + << "from descriptor FD#" << newFD + << ".\n" << logofs_flush; + } + #endif + } + } + } + + #ifdef DEBUG + *logofs << "Loop: Going to check the readable descriptors.\n" + << logofs_flush; + #endif + + if (proxy -> handleRead(resultFDs, readSet) < 0) + { + #ifdef TEST + *logofs << "Loop: Failure reading from descriptors " + << "for proxy FD#" << proxyFD << ".\n" + << logofs_flush; + #endif + + HandleShutdown(); + } +} + +static inline void handleWritableInLoop(int &resultFDs, fd_set &writeSet) +{ + #ifdef DEBUG + *logofs << "Loop: Going to check the writable descriptors.\n" + << logofs_flush; + #endif + + if (resultFDs > 0 && proxy -> handleFlush(resultFDs, writeSet) < 0) + { + #ifdef TEST + *logofs << "Loop: Failure writing to descriptors " + << "for proxy FD#" << proxyFD << ".\n" + << logofs_flush; + #endif + + HandleShutdown(); + } +} + +static inline void handleFlushInLoop() +{ + #ifdef DEBUG + *logofs << "Loop: Going to flush any data to the proxy.\n" + << logofs_flush; + #endif + + if (agent == NULL || control -> + FlushPolicy == policy_immediate) + { + #if defined(TEST) || defined(INFO) + + if (usePolicy == -1 && control -> + ProxyMode == proxy_client) + { + *logofs << "Loop: WARNING! Flushing the proxy link " + << "on behalf of the agent.\n" << logofs_flush; + } + + #endif + + if (proxy -> handleFlush() < 0) + { + #ifdef TEST + *logofs << "Loop: Failure flushing the proxy FD#" + << proxyFD << ".\n" << logofs_flush; + #endif + + HandleShutdown(); + } + } +} + +static inline void handleRotateInLoop() +{ + #ifdef DEBUG + *logofs << "Loop: Going to rotate channels " + << "for proxy FD#" << proxyFD << ".\n" + << logofs_flush; + #endif + + proxy -> handleRotate(); +} + +static inline void handleEventsInLoop() +{ + #ifdef DEBUG + *logofs << "Loop: Going to check channel events " + << "for proxy FD#" << proxyFD << ".\n" + << logofs_flush; + #endif + + if (proxy -> handleEvents() < 0) + { + #ifdef TEST + *logofs << "Loop: Failure handling channel events " + << "for proxy FD#" << proxyFD << ".\n" + << logofs_flush; + #endif + + HandleShutdown(); + } +} + +static void handleLogReopenInLoop(T_timestamp &logsTs, T_timestamp &nowTs) +{ + // + // If need to limit the size of the + // log file, check the size at each + // loop. + // + + #ifndef QUOTA + + if (diffTimestamp(logsTs, nowTs) > control -> FileSizeCheckTimeout) + + #endif + { + #ifdef DEBUG + *logofs << "Loop: Checking size of log file '" + << errorsFileName << "'.\n" << logofs_flush; + #endif + + #ifndef MIXED + + if (ReopenLogFile(errorsFileName, logofs, control -> FileSizeLimit) < 0) + { + HandleShutdown(); + } + + #endif + + // + // Reset to current timestamp. + // + + logsTs = nowTs; + } +} + +static inline void handleSetReadInLoop(fd_set &readSet, int &setFDs, struct timeval &selectTs) +{ + proxy -> setReadDescriptors(&readSet, setFDs, selectTs); +} + +static inline void handleSetWriteInLoop(fd_set &writeSet, int &setFDs, struct timeval &selectTs) +{ + proxy -> setWriteDescriptors(&writeSet, setFDs, selectTs); +} + +static void handleSetListenersInLoop(fd_set &readSet, int &setFDs) +{ + // + // Set descriptors of listening sockets. + // + + if (control -> ProxyMode == proxy_client) + { + if (useTcpSocket == 1) + { + FD_SET(tcpFD, &readSet); + + if (tcpFD >= setFDs) + { + setFDs = tcpFD + 1; + } + + #ifdef DEBUG + *logofs << "Loop: Selected listener tcpFD " << tcpFD + << " with setFDs " << setFDs << ".\n" + << logofs_flush; + #endif + } + + if (useUnixSocket == 1) + { + FD_SET(unixFD, &readSet); + + if (unixFD >= setFDs) + { + setFDs = unixFD + 1; + } + + #ifdef DEBUG + *logofs << "Loop: Selected listener unixFD " << unixFD + << " with setFDs " << setFDs << ".\n" + << logofs_flush; + #endif + } + + if (useCupsSocket == 1) + { + FD_SET(cupsFD, &readSet); + + if (cupsFD >= setFDs) + { + setFDs = cupsFD + 1; + } + + #ifdef DEBUG + *logofs << "Loop: Selected listener cupsFD " << cupsFD + << " with setFDs " << setFDs << ".\n" + << logofs_flush; + #endif + } + + if (useAuxSocket == 1) + { + FD_SET(auxFD, &readSet); + + if (auxFD >= setFDs) + { + setFDs = auxFD + 1; + } + + #ifdef DEBUG + *logofs << "Loop: Selected listener auxFD " << auxFD + << " with setFDs " << setFDs << ".\n" + << logofs_flush; + #endif + } + + if (useSmbSocket == 1) + { + FD_SET(smbFD, &readSet); + + if (smbFD >= setFDs) + { + setFDs = smbFD + 1; + } + + #ifdef DEBUG + *logofs << "Loop: Selected listener smbFD " << smbFD + << " with setFDs " << setFDs << ".\n" + << logofs_flush; + #endif + } + + if (useMediaSocket == 1) + { + FD_SET(mediaFD, &readSet); + + if (mediaFD >= setFDs) + { + setFDs = mediaFD + 1; + } + + #ifdef DEBUG + *logofs << "Loop: Selected listener mediaFD " << mediaFD + << " with setFDs " << setFDs << ".\n" + << logofs_flush; + #endif + } + + if (useHttpSocket == 1) + { + FD_SET(httpFD, &readSet); + + if (httpFD >= setFDs) + { + setFDs = httpFD + 1; + } + + #ifdef DEBUG + *logofs << "Loop: Selected listener httpFD " << httpFD + << " with setFDs " << setFDs << ".\n" + << logofs_flush; + #endif + } + } + else + { + if (useFontSocket == 1) + { + FD_SET(fontFD, &readSet); + + if (fontFD >= setFDs) + { + setFDs = fontFD + 1; + } + + #ifdef DEBUG + *logofs << "Loop: Selected listener fontFD " << fontFD + << " with setFDs " << setFDs << ".\n" + << logofs_flush; + #endif + } + } + + if (useSlaveSocket == 1) + { + FD_SET(slaveFD, &readSet); + + if (slaveFD >= setFDs) + { + setFDs = slaveFD + 1; + } + + #ifdef DEBUG + *logofs << "Loop: Selected listener slaveFD " << slaveFD + << " with setFDs " << setFDs << ".\n" + << logofs_flush; + #endif + } +} diff --git a/nxcomp/MD5.c b/nxcomp/MD5.c new file mode 100644 index 000000000..e49d3cf58 --- /dev/null +++ b/nxcomp/MD5.c @@ -0,0 +1,399 @@ +/* + 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. + */ + +#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/MD5.h b/nxcomp/MD5.h new file mode 100644 index 000000000..698c995d8 --- /dev/null +++ b/nxcomp/MD5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/nxcomp/Makefile.in b/nxcomp/Makefile.in new file mode 100644 index 000000000..434118b4e --- /dev/null +++ b/nxcomp/Makefile.in @@ -0,0 +1,279 @@ +############################################################################ +# # +# Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. # +# # +# NXCOMP, NX protocol compression and NX extensions to this software # +# are copyright of NoMachine. Redistribution and use of the present # +# software is allowed according to terms specified in the file LICENSE # +# which comes in the source distribution. # +# # +# Check http://www.nomachine.com/licensing.html for applicability. # +# # +# NX and NoMachine are trademarks of Medialogic S.p.A. # +# # +# All rights reserved. # +# # +############################################################################ + +# +# Get these values from the configure script. The +# version printed by the program should be derived +# from the CHANGELOG. For example we may use the +# following command: +# +# head -n 3 CHANGELOG | grep 'nxcomp-' | cut -d '-' -f 2-3 +# + +VERSION=@VERSION@ +LIBVERSION=@LIBVERSION@ + +# +# We would really like to enable all warnings, -Wredundant-decls, +# though, gives a warning caused by pthread.h and unistd.h and +# GCC 3.4 was changed in a way that it now complains about some +# of the -W directives we used before (-Wmissing-declarations, +# -Wnested-externs, -Wstrict-prototypes and -Wmissing-prototypes). +# + +CXX = @CXX@ +CXXFLAGS = @CXXFLAGS@ @X_CFLAGS@ @DEFS@ \ + -Wall -Wpointer-arith +CXXINCLUDES = +CXXDEFINES = + +# +# C programs have their own CFLAGS. +# + +CC = @CC@ +CCFLAGS = @CFLAGS@ @X_CFLAGS@ @DEFS@ \ + -Wall -Wpointer-arith +CCINCLUDES = +CCDEFINES = + +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ + +# +# Other autoconfigured settings, not used at the moment. +# + +srcdir = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +man1dir = @mandir@/man1 +VPATH = @srcdir@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +# +# This should be autodetected. +# + +MAKEDEPEND = @MAKEDEPEND@ +DEPENDINCLUDES = -I/usr/include/c++ -I/usr/include/g++ -I/usr/include/g++-3 + +.SUFFIXES: .cpp.c + +.cpp.o: + $(CXX) -c $(CXXFLAGS) $(CXXINCLUDES) $(CXXDEFINES) $< +.c.o: + $(CC) -c $(CCFLAGS) $(CCINCLUDES) $(CCDEFINES) $< + +LIBRARY = Xcomp + +LIBNAME = lib$(LIBRARY) +LIBFULL = lib$(LIBRARY).so.$(VERSION) +LIBLOAD = lib$(LIBRARY).so.$(LIBVERSION) +LIBSHARED = lib$(LIBRARY).so +LIBARCHIVE = lib$(LIBRARY).a + +LIBCYGSHARED = cyg$(LIBRARY).dll +LIBCYGARCHIVE = lib$(LIBRARY).dll.a + +all: depend @ALL@ + +MSRC = + +CSRC = MD5.c \ + Pack.c \ + Vars.c + +CXXSRC = Loop.cpp \ + Children.cpp \ + Control.cpp \ + Misc.cpp \ + Socket.cpp \ + Fork.cpp \ + Pipe.cpp \ + List.cpp \ + Keeper.cpp \ + Timestamp.cpp \ + Transport.cpp \ + Statistics.cpp \ + Auth.cpp \ + Agent.cpp \ + Proxy.cpp \ + Channel.cpp \ + Message.cpp \ + Split.cpp \ + ClientProxy.cpp \ + ServerProxy.cpp \ + OpcodeStore.cpp \ + ClientStore.cpp \ + ServerStore.cpp \ + ChannelCache.cpp \ + ClientCache.cpp \ + ServerCache.cpp \ + ClientChannel.cpp \ + ServerChannel.cpp \ + GenericChannel.cpp \ + ReadBuffer.cpp \ + ProxyReadBuffer.cpp \ + ClientReadBuffer.cpp \ + ServerReadBuffer.cpp \ + GenericReadBuffer.cpp \ + EncodeBuffer.cpp \ + DecodeBuffer.cpp \ + WriteBuffer.cpp \ + SequenceQueue.cpp \ + IntCache.cpp \ + CharCache.cpp \ + XidCache.cpp \ + ActionCache.cpp \ + BlockCache.cpp \ + BlockCacheSet.cpp \ + StaticCompressor.cpp \ + TextCompressor.cpp \ + Unpack.cpp \ + Alpha.cpp \ + Colormap.cpp \ + Jpeg.cpp \ + Pgn.cpp \ + Bitmap.cpp \ + Rgb.cpp \ + Rle.cpp \ + Z.cpp \ + ChangeProperty.cpp \ + SendEvent.cpp \ + ChangeGC.cpp \ + CreateGC.cpp \ + CreatePixmap.cpp \ + SetClipRectangles.cpp \ + CopyArea.cpp \ + PolyLine.cpp \ + PolySegment.cpp \ + PolyFillRectangle.cpp \ + PutImage.cpp \ + TranslateCoords.cpp \ + GetImage.cpp \ + ClearArea.cpp \ + ConfigureWindow.cpp \ + PolyText8.cpp \ + PolyText16.cpp \ + ImageText8.cpp \ + ImageText16.cpp \ + PolyPoint.cpp \ + PolyFillArc.cpp \ + PolyArc.cpp \ + FillPoly.cpp \ + InternAtom.cpp \ + GetProperty.cpp \ + SetUnpackGeometry.cpp \ + SetUnpackColormap.cpp \ + SetUnpackAlpha.cpp \ + PutPackedImage.cpp \ + ShapeExtension.cpp \ + RenderExtension.cpp \ + GenericRequest.cpp \ + QueryFontReply.cpp \ + ListFontsReply.cpp \ + GetImageReply.cpp \ + GetPropertyReply.cpp \ + GenericReply.cpp \ + RenderGenericRequest.cpp \ + RenderCreatePicture.cpp \ + RenderChangePicture.cpp \ + RenderFreePicture.cpp \ + RenderPictureClip.cpp \ + RenderPictureTransform.cpp \ + RenderPictureFilter.cpp \ + RenderCreateGlyphSet.cpp \ + RenderFreeGlyphSet.cpp \ + RenderAddGlyphs.cpp \ + RenderComposite.cpp \ + RenderCompositeGlyphs.cpp \ + RenderFillRectangles.cpp \ + RenderTrapezoids.cpp \ + RenderTriangles.cpp \ + PositionCacheCompat.cpp \ + ChangeGCCompat.cpp \ + CreatePixmapCompat.cpp \ + SetUnpackColormapCompat.cpp \ + SetUnpackAlphaCompat.cpp \ + RenderCreatePictureCompat.cpp \ + RenderFreePictureCompat.cpp \ + RenderPictureClipCompat.cpp \ + RenderCreateGlyphSetCompat.cpp \ + RenderCompositeCompat.cpp \ + RenderCompositeGlyphsCompat.cpp + +MOBJ = $(MSRC:.c=.o) +COBJ = $(CSRC:.c=.o) +CXXOBJ = $(CXXSRC:.cpp=.o) + +$(LIBFULL): $(CXXOBJ) $(COBJ) + $(CXX) -o $@ $(LDFLAGS) $(CXXOBJ) $(COBJ) $(LIBS) + +$(LIBLOAD): $(LIBFULL) + rm -f $(LIBLOAD) + ln -s $(LIBFULL) $(LIBLOAD) + +$(LIBSHARED): $(LIBFULL) + rm -f $(LIBSHARED) + ln -s $(LIBFULL) $(LIBSHARED) + +$(LIBARCHIVE): $(CXXOBJ) $(COBJ) + rm -f $(LIBARCHIVE) + ar clq $(LIBARCHIVE) $(CXXOBJ) $(COBJ) + ranlib $(LIBARCHIVE) + +$(LIBCYGSHARED): $(LIBARCHIVE) + $(CC) -shared -o $(LIBCYGSHARED) \ + -Wl,--out-implib=$(LIBCYGARCHIVE) \ + -Wl,--export-all-symbols \ + -Wl,--enable-auto-import \ + -Wl,--whole-archive $(LIBARCHIVE) \ + -Wl,--no-whole-archive $(LIBS) \ + $(LDFLAGS) + +$(LIBCYGARCHIVE): $(LIBCYGSHARED) + +depends: depend.status + +depend: depend.status + +depend.status: + if [ -x $(MAKEDEPEND) ] ; then \ + $(MAKEDEPEND) $(CXXINCLUDES) $(CCINCLUDES) \ + $(DEPENDINCLUDES) -f Makefile $(MSRC) $(CSRC) \ + $(CXXSRC) 2>/dev/null; \ + fi + touch depend.status + +install: install.bin install.man + +install.bin: + +install.man: + +clean: + -rm -f *~ *.o *.bak *.orig *.rej st?????? core core.* *.out.* \ + @ALL@ + +distclean: clean + -rm -rf autom4te.cache config.status config.log \ + config.cache depend.status Makefile tags diff --git a/nxcomp/Message.cpp b/nxcomp/Message.cpp new file mode 100644 index 000000000..72d4fff3d --- /dev/null +++ b/nxcomp/Message.cpp @@ -0,0 +1,2345 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> + +#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; + + // + // This is used only for compatibility + // with older proxies. + // + + if (control -> isProtoStep7() == 1) + { + lastResize = -1; + } + else + { + lastResize = 0; + } + + // + // Private members. + // + + localStorageSize_ = 0; + remoteStorageSize_ = 0; + + #ifdef TEST + *logofs << "MessageStore: Size of total cache is " + << totalLocalStorageSize_ << " bytes at local side and " + << totalRemoteStorageSize_ << " bytes at remote side.\n" + << logofs_flush; + #endif + + messages_ = new T_messages(); + checksums_ = new T_checksums(); + + temporary_ = NULL; + + #ifdef REFERENCES + + references_++; + + *logofs << "MessageStore: Created new store at " + << this << "out of " << references_ + << " allocated stores.\n" << logofs_flush; + + #endif +} + +MessageStore::~MessageStore() +{ + // + // The virtual destructor of specialized class + // must get rid of both messages in container + // and temporary. + // + + #ifdef DEBUG + *logofs << "MessageStore: Deleting MD5 state for object at " + << this << ".\n" << logofs_flush; + #endif + + delete md5_state_; + + delete messages_; + delete checksums_; + + // + // Update the static members tracking + // size of total memory allocated for + // all stores. + // + + totalLocalStorageSize_ -= localStorageSize_; + totalRemoteStorageSize_ -= remoteStorageSize_; + + #ifdef TEST + *logofs << "MessageStore: Size of total cache is " + << totalLocalStorageSize_ << " bytes at local side and " + << totalRemoteStorageSize_ << " bytes at remote side.\n" + << logofs_flush; + #endif + + #ifdef REFERENCES + + references_--; + + *logofs << "MessageStore: Deleted store at " + << this << " out of " << references_ + << " allocated stores.\n" << logofs_flush; + + #endif +} + +// +// Here are the methods to parse and cache +// messages in the message stores. +// + +int MessageStore::parse(Message *message, int split, const unsigned char *buffer, + unsigned int size, T_checksum_action checksumAction, + T_data_action dataAction, int bigEndian) +{ + // + // Save the message size as received on the link. + // This information will be used to create an ap- + // propriate buffer at the time the message will + // be unparsed. + // + + message -> size_ = size; + message -> i_size_ = identitySize(buffer, size); + message -> c_size_ = 0; + + validateSize(size); + + if (checksumAction == use_checksum) + { + beginChecksum(message); + + parseIdentity(message, buffer, size, bigEndian); + + identityChecksum(message, buffer, size, bigEndian); + + parseData(message, split, buffer, size, checksumAction, dataAction, bigEndian); + + endChecksum(message); + } + else + { + parseIdentity(message, buffer, size, bigEndian); + + parseData(message, split, buffer, size, checksumAction, dataAction, bigEndian); + } + + return 1; +} + +int MessageStore::parse(Message *message, const unsigned char *buffer, + unsigned int size, const unsigned char *compressedData, + const unsigned int compressedDataSize, + T_checksum_action checksumAction, + T_data_action dataAction, int bigEndian) +{ + int offset = identitySize(buffer, size); + + message -> size_ = size; + message -> i_size_ = offset; + message -> c_size_ = compressedDataSize + offset; + + validateSize(message -> size_ - offset, compressedDataSize); + + if (checksumAction == use_checksum) + { + beginChecksum(message); + + parseIdentity(message, buffer, size, bigEndian); + + identityChecksum(message, buffer, size, bigEndian); + + parseData(message, buffer, size, compressedData, compressedDataSize, + checksumAction, dataAction, bigEndian); + + endChecksum(message); + } + else + { + parseIdentity(message, buffer, size, bigEndian); + + parseData(message, buffer, size, compressedData, compressedDataSize, + checksumAction, dataAction, bigEndian); + } + + return 1; +} + +int MessageStore::parseData(Message *message, int split, const unsigned char *buffer, + unsigned int size, T_checksum_action checksumAction, + T_data_action dataAction, int bigEndian) +{ + if ((int) size > message -> i_size_) + { + unsigned int dataSize = size - message -> i_size_; + + if (checksumAction == use_checksum) + { + #ifdef DEBUG + *logofs << name() << ": Calculating checksum of object at " + << message << " with data size " << dataSize + << ".\n" << logofs_flush; + #endif + + dataChecksum(message, buffer, size, bigEndian); + } + + if (dataAction == discard_data) + { + #ifdef DEBUG + *logofs << name() << ": Discarded " << dataSize + << " bytes of plain data. Real size is " + << message -> size_ << " compressed size is " + << message -> c_size_ << ".\n" << logofs_flush; + #endif + + return 1; + } + + // + // Accept anyway data beyond the + // expected limit. + // + + #ifdef TEST + + if (dataSize > (unsigned int) dataLimit) + { + *logofs << name() << ": WARNING! Data is " << dataSize + << " bytes. Ignoring the established limit.\n" + << logofs_flush; + } + + #endif + + if (dataSize != message -> data_.size()) + { + #ifdef DEBUG + *logofs << name() << ": Data will be resized from " + << message -> data_.size() << " to hold a plain buffer of " + << dataSize << " bytes.\n" << logofs_flush; + #endif + + message -> data_.clear(); + + message -> data_.resize(dataSize); + } + + if (split == 0) + { + memcpy(message -> data_.begin(), buffer + message -> i_size_, dataSize); + } + #ifdef TEST + else + { + *logofs << name() << ": Not copied " << dataSize + << " bytes of fake data for the split message.\n" + << logofs_flush; + } + #endif + + #ifdef DEBUG + *logofs << name() << ": Parsed " << dataSize + << " bytes of plain data. Real size is " + << message -> size_ << " compressed size is " + << message -> c_size_ << ".\n" << logofs_flush; + #endif + } + + return 1; +} + +// +// Store the data part in compressed format. +// + +int MessageStore::parseData(Message *message, const unsigned char *buffer, + unsigned int size, const unsigned char *compressedData, + const unsigned int compressedDataSize, + T_checksum_action checksumAction, + T_data_action dataAction, int bigEndian) +{ + if ((int) size > message -> i_size_) + { + unsigned int dataSize = size - message -> i_size_; + + if (checksumAction == use_checksum) + { + #ifdef DEBUG + *logofs << name() << ": Calculating checksum of object at " + << message << " with data size " << dataSize + << ".\n" << logofs_flush; + #endif + + dataChecksum(message, buffer, size, bigEndian); + } + + if (dataAction == discard_data) + { + #ifdef DEBUG + *logofs << name() << ": Discarded " << dataSize + << " bytes of compressed data. Real size is " + << message -> size_ << " compressed size is " + << message -> c_size_ << ".\n" << logofs_flush; + #endif + + return 1; + } + + #ifdef WARNING + if (dataSize > (unsigned int) dataLimit) + { + *logofs << name() << ": WARNING! Data is " << dataSize + << " bytes. Ignoring the established limit!\n" + << logofs_flush; + } + #endif + + dataSize = compressedDataSize; + + if (dataSize != message -> data_.size()) + { + #ifdef DEBUG + *logofs << name() << ": Data will be resized from " + << message -> data_.size() << " to hold a compressed buffer of " + << dataSize << " bytes.\n" << logofs_flush; + #endif + + message -> data_.clear(); + + message -> data_.resize(compressedDataSize); + } + + memcpy(message -> data_.begin(), compressedData, compressedDataSize); + + #ifdef DEBUG + *logofs << name() << ": Parsed " << dataSize + << " bytes of compressed data. Real size is " + << message -> size_ << " compressed size is " + << message -> c_size_ << ".\n" << logofs_flush; + #endif + } + + return 1; +} + +int MessageStore::unparseData(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) +{ + // + // Copy data, if any, to the buffer. + // + + if ((int) size > message -> i_size_) + { + // + // Check if message has been stored + // in compressed format. + // + + if (message -> c_size_ == 0) + { + memcpy(buffer + message -> i_size_, message -> data_.begin(), size - message -> i_size_); + + #ifdef DEBUG + *logofs << name() << ": Unparsed " << message -> size_ - message -> i_size_ + << " bytes of data to a buffer of " << message -> size_ - message -> i_size_ + << ".\n" << logofs_flush; + #endif + } + else + { + #ifdef DEBUG + *logofs << name() << ": Using static compressor at " << (void *) compressor_ + << ".\n" << logofs_flush; + #endif + + if (compressor_ -> + decompressBuffer(buffer + message -> i_size_, + size - message -> i_size_, + message -> data_.begin(), + message -> c_size_ - message -> i_size_) < 0) + { + #ifdef PANIC + *logofs << name() << ": PANIC! Data decompression failed.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Data decompression failed.\n"; + + return -1; + } + + #ifdef DEBUG + *logofs << name() << ": Unparsed " << message -> c_size_ - message -> i_size_ + << " bytes of compressed data to a buffer of " + << message -> size_ - message -> i_size_ << ".\n" << logofs_flush; + #endif + } + } + + // + // We could write size to the buffer but this + // is something the channel class is doing by + // itself. + // + // PutUINT(size >> 2, buffer + 2, bigEndian); + // + + return 1; +} + +void MessageStore::dumpData(const Message *message) const +{ + #ifdef DUMP + + *logofs << name() << ": Dumping enumerated data:\n" << logofs_flush; + + DumpData(message -> data_.begin(), message -> data_.size()); + + #endif + + #ifdef DUMP + + *logofs << name() << ": Dumping checksum data:\n" << logofs_flush; + + DumpData(message -> md5_digest_, MD5_LENGTH); + + #endif +} + +T_checksum MessageStore::getChecksum(const unsigned char *buffer, + unsigned int size, int bigEndian) +{ + Message *message = getTemporary(); + + message -> size_ = size; + message -> i_size_ = identitySize(buffer, size); + message -> c_size_ = 0; + + validateSize(size); + + beginChecksum(message); + + // + // We don't need to extract the identity + // data from the buffer. + // + // parseIdentity(message, buffer, size, bigEndian); + // + + identityChecksum(message, buffer, size, bigEndian); + + parseData(message, 0, buffer, size, use_checksum, discard_data, bigEndian); + + endChecksum(message); + + // + // The caller will have to explicitly + // deallocated the memory after use. + // + + T_checksum checksum = new md5_byte_t[MD5_LENGTH]; + + memcpy(checksum, message -> md5_digest_, MD5_LENGTH); + + return checksum; +} + +int MessageStore::clean(T_checksum_action checksumAction) +{ + int position = lastRemoved + 1; + + if (position >= cacheSlots) + { + position = 0; + } + + #ifdef DEBUG + *logofs << name() << ": Searching a message to remove " + << "starting at position " << position + << " with " << checksums_ -> size() + << " elements in cache.\n" + << logofs_flush; + #endif + + while (position != lastRemoved) + { + #ifdef DEBUG + *logofs << name() << ": Examining position " + << position << ".\n" << logofs_flush; + #endif + + if ((*messages_)[position] != NULL) + { + if (getRating((*messages_)[position], rating_for_clean) == 0) + { + break; + } + else + { + untouch((*messages_)[position]); + } + } + + if (++position == cacheSlots) + { + #ifdef DEBUG + *logofs << name() << ": Rolled position at " + << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + position = 0; + } + } + + // + // If no message is a good candidate, + // then try the object at the next slot + // in respect to last element removed. + // + + if (position == lastRemoved) + { + position = lastRemoved + 1; + + if (position >= cacheSlots) + { + position = 0; + } + + if ((*messages_)[position] == NULL || + (*messages_)[position] -> locks_ != 0) + { + #ifdef DEBUG + *logofs << name() << ": WARNING! No message found " + << "to be actually removed.\n" + << logofs_flush; + #endif + + return nothing; + } + + #ifdef DEBUG + *logofs << name() << ": WARNING! Assuming object " + << "at position " << position << ".\n" + << logofs_flush; + #endif + } + + return position; +} + +// +// This is the insertion method used at local side +// side. Cache at remote side side will be kept in +// sync by telling the to other party where to +// store the message. +// + +int MessageStore::findOrAdd(Message *message, T_checksum_action checksumAction, + T_data_action dataAction, int &added, int &locked) +{ + if (checksumAction != use_checksum) + { + #ifdef PANIC + *logofs << name() << ": PANIC! Internal error in context [A]. " + << "Cannot find or add message to repository " + << "without using checksum.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Internal error in context [A]. " + << "Cannot find or add message to repository " + << "without using checksum.\n"; + + HandleAbort(); + } + + // + // Set added to true only if message + // is inserted in cache. + // + + added = 0; + locked = 0; + + // + // First of all figure out where to + // store this object. + // + + #ifdef DEBUG + *logofs << name() << ": Searching an empty slot " + << "with last rated " << lastRated << " and " + << "last added " << lastAdded << ".\n" + << logofs_flush; + #endif + + int position = lastRated; + + if (position == nothing) + { + position = lastAdded + 1; + + if (position >= cacheSlots) + { + position = 0; + } + + #ifdef DEBUG + *logofs << name() << ": Searching an empty slot " + << "starting at position " << position + << " with " << checksums_ -> size() + << " elements in cache.\n" + << logofs_flush; + #endif + + while (position != lastAdded) + { + #ifdef DEBUG + *logofs << name() << ": Examining position " + << position << ".\n" << logofs_flush; + #endif + + if ((*messages_)[position] == NULL) + { + break; + } + else if (getRating((*messages_)[position], rating_for_insert) == 0) + { + break; + } + else + { + untouch((*messages_)[position]); + } + + if (++position == cacheSlots) + { + #ifdef DEBUG + *logofs << name() << ": Rolled position at " + << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + position = 0; + } + } + } + #ifdef DEBUG + else + { + *logofs << name() << ": Using last rated position " + << position << ".\n" << logofs_flush; + } + #endif + + // + // If we made an extensive check but did not + // find neither a free slot or a message to + // replace, assume slot at next position in + // respect to last added. This can happen if + // all objects in repository have got an hit + // recently. + // + + if (position == lastAdded) + { + position = lastAdded + 1; + + if (position >= cacheSlots) + { + position = 0; + } + + #ifdef DEBUG + *logofs << name() << ": WARNING! Assuming slot " + << "at position " << position << ".\n" + << logofs_flush; + #endif + } + #ifdef DEBUG + else + { + *logofs << name() << ": Found candidate slot " + << "at position " << position << ".\n" + << logofs_flush; + } + #endif + + // + // Save the search result so if the message + // is found in cache, we can use the slot + // at next run. + // + + lastRated = position; + + if ((*messages_)[position] != NULL && + (*messages_)[position] -> locks_ != 0) + { + #ifdef WARNING + *logofs << name() << ": WARNING! Insertion at position " + << position << " would replace a locked message. " + << "Forcing channel to discard the message.\n" + << logofs_flush; + #endif + + #ifdef TEST + *logofs << name() << ": Invalidating rating of object " + << "at position " << position << ".\n" + << logofs_flush; + #endif + + return (lastRated = nothing); + } + + if (checksumAction == use_checksum) + { + T_checksum checksum = getChecksum(message); + + #ifdef TEST + *logofs << name() << ": Searching checksum [" + << DumpChecksum(checksum) << "] in repository.\n" + << logofs_flush; + + #endif + + pair<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/Message.h b/nxcomp/Message.h new file mode 100644 index 000000000..dcfff7cb6 --- /dev/null +++ b/nxcomp/Message.h @@ -0,0 +1,1102 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Message_H +#define Message_H + +#include <X11/Xproto.h> + +#include "NXproto.h" + +#include "Misc.h" +#include "Control.h" + +#include "Types.h" +#include "Timestamp.h" + +#include "ActionCache.h" + +#include "ActionCacheCompat.h" +#include "PositionCacheCompat.h" + +#include "StaticCompressor.h" + +// +// Forward class declarations. +// + +class ChannelCache; + +class EncodeBuffer; +class DecodeBuffer; + +class WriteBuffer; + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Define this to know how many messages +// are allocated and deallocated. +// + +#undef REFERENCES + +// +// Set default values. We limit the maximum +// size of a request to 262144 but we need to +// consider the replies, whose size may be up +// to 4MB. +// + +#define MESSAGE_ENABLE_CACHE 0 +#define MESSAGE_ENABLE_DATA 0 +#define MESSAGE_ENABLE_SPLIT 0 +#define MESSAGE_ENABLE_COMPRESS 0 + +#define MESSAGE_DATA_LIMIT 4194304 - 4 +#define MESSAGE_DATA_OFFSET 4 + +#define MESSAGE_CACHE_SLOTS 6000 +#define MESSAGE_CACHE_THRESHOLD 50 +#define MESSAGE_CACHE_LOWER_THRESHOLD 5 + +// +// Base message class. +// + +class Message +{ + friend class MessageStore; + friend class RenderExtensionStore; + + public: + + Message() + { + hits_ = 0; + last_ = 0; + locks_ = 0; + + size_ = 0; + c_size_ = 0; + + md5_digest_ = NULL; + + #ifdef REFERENCES + + references_++; + + *logofs << "Message: Created new message at " + << this << " out of " << references_ + << " allocated messages.\n" + << logofs_flush; + + #endif + } + + Message(const Message &message) + { + size_ = message.size_; + c_size_ = message.c_size_; + i_size_ = message.i_size_; + + hits_ = message.hits_; + last_ = message.last_; + locks_ = message.locks_; + + data_ = message.data_; + + #ifdef REFERENCES + + references_++; + + *logofs << "Message: Creating new copied message at " + << this << " out of " << references_ + << " allocated messages.\n" + << logofs_flush; + #endif + + if (message.md5_digest_ != NULL) + { + md5_digest_ = new md5_byte_t[MD5_LENGTH]; + + memcpy(md5_digest_, message.md5_digest_, MD5_LENGTH); + + #ifdef DEBUG + *logofs << "Message: Created MD5 digest for object at " + << this << ".\n" << logofs_flush; + #endif + } + else + { + md5_digest_ = NULL; + } + } + + ~Message() + { + #ifdef DEBUG + if (md5_digest_ != NULL) + { + *logofs << "Message: Deleted MD5 digest for object at " + << this << ".\n" << logofs_flush; + } + #endif + + delete [] md5_digest_; + + #ifdef REFERENCES + + references_--; + + *logofs << "Message: Deleted message at " + << this << " out of " << references_ + << " allocated messages.\n" + << logofs_flush; + #endif + } + + // + // This is the original message size + // including the data part regardless + // data is still stored in the object. + // + + int size_; + + // + // This is the size of the identity. + // + + int i_size_; + + // + // This is the size, including identity, + // after message has been 'updated' to + // reflect storage of data in compressed + // format. + // + + int c_size_; + + protected: + + // + // This is the data part. + // + + T_data data_; + + // + // Time of last hit. + // + + time_t last_; + + // + // This is the number of cache hits + // registered for the object. + // + + short int hits_; + + // + // This is used to mark messages + // that have been split. + // + + short int locks_; + + // + // This is the MD5 checksum. + // + + md5_byte_t *md5_digest_; + + // + // Keep a reference counter + // of allocated objects. + // + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +// +// Repository of messages. +// + +class MessageStore +{ + public: + + // + // Enable or disable cache of messages in store. + // + + int enableCache; + + // + // Does message have a distinct data part. + // + + int enableData; + + // + // Enable or disable split of data part. + // + + int enableSplit; + + // + // Enable or disable compression of data part. + // + + int enableCompress; + + // + // Set starting point of data part in the message. + // + + int dataOffset; + + // + // Set maximum size for the data part of each message. + // + + int dataLimit; + + // + // Set maximum elements in cache. + // + + int cacheSlots; + + // + // Set the percentage of total cache memory which + // a given type of message is allowed to occupy. + // When threshold is exceeded store is cleaned to + // make room for a new message of the same type. + // + + int cacheThreshold; + + // + // Don't clean the store if percentage of cache + // memory occupied by messages of this type is + // below the threshold. + // + + int cacheLowerThreshold; + + // + // Last operation performed on cache. + // + + T_store_action lastAction; + + // + // Position of last element stored in cache. + // + + short int lastAdded; + + // + // Positions of last element found in cache. + // + + short int lastHit; + + // + // Position of last element erased. + // + + short int lastRemoved; + + // + // Used to encode the the action to + // perform on the store and the slot + // involved. + // + + ActionCache lastActionCache; + + // + // Used in old protocol versions. + // + + ActionCacheCompat lastActionCacheCompat; + + PositionCacheCompat lastAddedCacheCompat; + PositionCacheCompat lastHitCacheCompat; + PositionCacheCompat lastRemovedCacheCompat; + + // + // Position in cache where next insertion + // is going to take place. + // + + short int lastRated; + + // + // Size of data part of last split message + // once compressed. This is used only for + // compatibility with older proxies. + // + + int lastResize; + + // + // Constructors and destructors. + // + + public: + + MessageStore(StaticCompressor *compressor = NULL); + + virtual ~MessageStore(); + + virtual const char *name() const = 0; + + virtual unsigned char opcode() const = 0; + + virtual unsigned int storage() const = 0; + + // + // These are members that must be specialized. + // + + public: + + virtual Message *create() const = 0; + + virtual Message *create(const Message &message) const = 0; + + virtual void destroy(Message *message) const = 0; + + void validateSize(int size) + { + if (size < control -> MinimumMessageSize || + size > control -> MaximumMessageSize) + { + *logofs << name() << ": PANIC! Invalid size " << size + << " for message.\n" << logofs_flush; + + cerr << "Error" << ": Invalid size " << size + << " for message opcode " << opcode() << ".\n"; + + HandleAbort(); + } + } + + void validateSize(int dataSize, int compressedDataSize) + { + if (dataSize < 0 || dataSize > control -> + MaximumMessageSize - 4 || compressedDataSize < 0 || + compressedDataSize >= dataSize) + { + *logofs << name() << ": PANIC! Invalid data size " + << dataSize << " and compressed data size " + << compressedDataSize << " for message.\n" + << logofs_flush; + + cerr << "Error" << ": Invalid data size " + << dataSize << " and compressed data size " + << compressedDataSize << " for message " + << "opcode " << (unsigned) opcode() << ".\n"; + + HandleAbort(); + } + } + + // + // Determine if the message can be stored + // in the cache. + // + + virtual int validateMessage(const unsigned char *buffer, int size) + { + return (size >= control -> MinimumMessageSize && + size <= control -> MaximumMessageSize); + } + + // + // Get data offset based on major and minor + // opcode of the message. + // + + virtual int identitySize(const unsigned char *buffer, unsigned int size) + { + return dataOffset; + } + + // + // Encode identity and data using the + // specific message encoding. + // + // Some messages do not implement these + // methods because the encoding is done + // directly in the channel loop. Should + // move the encoding methods in in the + // message classes. + // + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int size, int bigEndian, + ChannelCache *channelCache) const + { + return 1; + } + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const + { + return 1; + } + + // + // Encode differences between message + // in cache and the one to be encoded. + // + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, ChannelCache *channelCache) const + { + } + + // + // Decode differences and update the + // cached version of the same message. + // + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const + { + } + + // + // Post process the message information + // contained in the store by either up- + // dating the size record or the actual + // data part once the message has been + // completely sent to our peer. + // + + void updateData(const int position, unsigned int dataSize, + unsigned int compressedDataSize); + + void updateData(const T_checksum checksum, unsigned int compressedDataSize); + + void updateData(const int position, const unsigned char *newData, + unsigned int dataSize, unsigned int compressedDataSize); + + // + // These members, used internally + // in the message store class, are + // mandatory. + // + + protected: + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const = 0; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const = 0; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const = 0; + + virtual void dumpIdentity(const Message *message) const = 0; + + // + // Design should preserve these from being + // virtual. + // + + int parseData(Message *message, int split, const unsigned char *buffer, + unsigned int size, T_checksum_action checksumAction, + T_data_action dataAction, int bigEndian); + + int parseData(Message *message, const unsigned char *buffer, + unsigned int size, const unsigned char *compressedData, + const unsigned int compressedDataSize, T_checksum_action checksumAction, + T_data_action dataAction, int bigEndian); + + int unparseData(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian); + + // + // Manage efficient allocation of messages + // in the heap. + // + + void recycle(Message *message) + { + #ifdef TEST + + if (message == NULL) + { + *logofs << name() << ": PANIC! Cannot recycle a null message.\n" + << logofs_flush; + + cerr << "Error" << ": Cannot recycle a null message.\n"; + + HandleAbort(); + } + + #endif + + if (temporary_ == NULL) + { + // + // Be careful when reusing the message as + // it can contain valid data that must be + // explicitly deallocated if not needed. + // Note also that you cannot count on the + // initialization made in costructor. + // + + temporary_ = message; + } + else + { + destroy(message); + } + } + + void beginChecksum(Message *message) + { + if (message -> md5_digest_ == NULL) + { + message -> md5_digest_ = new md5_byte_t[MD5_LENGTH]; + + #ifdef DEBUG + *logofs << name() << ": Created MD5 digest structure " + << "for object at " << message << ".\n" + << logofs_flush; + #endif + } + #ifdef DEBUG + else + { + *logofs << name() << ": Using existing MD5 digest structure " + << "for object at " << message << ".\n" + << logofs_flush; + } + #endif + + #ifdef DEBUG + *logofs << name() << ": Prepared MD5 digest for object at " + << message << ".\n" << logofs_flush; + #endif + + md5_init(md5_state_); + } + + void endChecksum(Message *message) + { + md5_finish(md5_state_, message -> md5_digest_); + + #ifdef DEBUG + *logofs << name() << ": Calculated checksum for object at " + << message << ".\n" << logofs_flush; + #endif + } + + void dataChecksum(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) + { + // + // Messages that have a data part starting + // at an offset possibly beyond the end of + // the message, must include the size in + // the identity checksum. + // + + if ((int) size > message -> i_size_) + { + md5_append(md5_state_, buffer + message -> i_size_, + size - message -> i_size_); + } + } + + // + // Repository handling methods. + // + + public: + + // + // Extract identity and data from buffer. + // The size field will be updated at the + // time of data parsing. + // + + int parse(Message *message, int split, const unsigned char *buffer, unsigned int size, + T_checksum_action checksumAction, T_data_action dataAction, int bigEndian); + + int parse(Message *message, const unsigned char *buffer, unsigned int size, + const unsigned char *compressedData, const unsigned int compressedDataSize, + T_checksum_action checksumAction, T_data_action dataAction, int bigEndian); + + // + // From identity and data write the + // final message to the raw buffer. + // + + int unparse(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) + { + return (unparseData(message, buffer, size, bigEndian) && + unparseIdentity(message, buffer, size, bigEndian)); + } + + void dump(const Message *message) const + { + dumpIdentity(message); + + dumpData(message); + } + + void dumpData(const Message *message) const; + + // + // This returns the original message size as it + // was received on the link. It takes in account + // the data part, regardless data is still stored + // in the message object. This information will + // be used at the time message is unparsed. + // + + int plainSize(const int position) const + { + return (*messages_)[position] -> size_; + } + + // + // This returns either the size of identity plus + // the compressed data part or 0 if message is + // stored in uncompressed format. + // + + int compressedSize(const int position) const + { + return (*messages_)[position] -> c_size_; + } + + // + // Returns a pointer to message + // given its position in cache. + // + + Message *get(const int position) const + { + if (position < 0 || position >= cacheSlots) + { + #ifdef PANIC + *logofs << name() << ": PANIC! Requested position " + << position << " is not inside the " + << "container.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Requested position " + << position << " is not inside the" + << "container.\n"; + + HandleAbort(); + } + else if ((*messages_)[position] == NULL) + { + #ifdef PANIC + *logofs << name() << ": PANIC! Message at position " + << position << " is NULL.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Message at position " + << position << " is NULL.\n"; + + HandleAbort(); + } + + return (*messages_)[position]; + } + + // + // This is the method called at encoding + // side to add a message to cache. + // + + int findOrAdd(Message *message, T_checksum_action checksumAction, + T_data_action dataAction, int &added, int &locked); + + // + // Utility interfaces to message insertion + // and deletion. + // + + int add(Message *message, const int position, + T_checksum_action checksumAction, T_data_action dataAction); + + int remove(const int position, T_checksum_action checksumAction, + T_data_action dataAction); + + // + // Make space in the repository by remove + // the first suitable message object. + // + + int clean(T_checksum_action checksumAction); + + // + // Increase or decrease the "rating" of + // the message object. + // + + int touch(Message *message) const; + int untouch(Message *message) const; + + int getTouches(const int position) const + { + Message *message = (*messages_)[position]; + + if (message == NULL) + { + return 0; + } + + return message -> hits_; + } + + // + // Gives a 'weight' to the cached message. A zero + // value means object can be safely removed. A value + // greater than zero means it is advisable to retain + // the object. A negative result means it is mandato- + // ry to keep object in cache. + // + + int getRating(Message *message, T_rating type) const; + + // + // Increase or decrease locks of message at given + // position. A locked message will not be removed + // from the message store until the lock counter + // is zero. + // + + int lock(const int position) const; + int unlock(const int position) const; + + int getLocks(const int position) const + { + Message *message = (*messages_)[position]; + + if (message == NULL) + { + return 0; + } + + return message -> locks_; + } + + T_checksum const getChecksum(const int position) const + { + return getChecksum(get(position)); + } + + T_checksum const getChecksum(const Message *message) const + { + if (message -> md5_digest_ == NULL) + { + #ifdef PANIC + *logofs << name() << ": PANIC! Checksum not initialized " + << "for object at " << message << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Checksum not initialized " + << "for object at " << message << ".\n"; + + HandleAbort(); + } + + #ifdef DEBUG + *logofs << name() << ": Got checksum for object at " + << message << ".\n" << logofs_flush; + #endif + + return message -> md5_digest_; + } + + // + // Calculate the checksum on the fly based the + // opcode in the buffer. Useful in the case a + // message was not processed or was not stored + // in the cache. The returned checksum must be + // explicitly deallocated by the caller, after + // use. + // + + T_checksum getChecksum(const unsigned char *buffer, + unsigned int size, int bigEndian); + + const unsigned char *getData(const Message *message) const + { + return message -> data_.begin(); + } + + int plainSize(const Message *message) const + { + return message -> size_; + } + + int identitySize(Message *message) + { + return message -> i_size_; + } + + int compressedSize(const Message *message) const + { + return message -> c_size_; + } + + Message *getTemporary() + { + if (temporary_ == NULL) + { + temporary_ = create(); + } + + return temporary_; + } + + void resetTemporary() + { + temporary_ = NULL; + } + + // + // On side where we don't have checksums, we + // count how many messages are in the array. + // This is obviously expensive and should be + // only performed when reporting statistics. + // + + int getSize() const + { + int size = checksums_ -> size(); + + if (size == 0) + { + for (int i = 0; i < cacheSlots; i++) + { + if ((*messages_)[i] != NULL) + { + size++; + } + } + } + + return size; + } + + int getLocalStorageSize() const + { + return localStorageSize_; + } + + int getRemoteStorageSize() const + { + return remoteStorageSize_; + } + + int getLocalTotalStorageSize() const + { + return totalLocalStorageSize_; + } + + int getRemoteTotalStorageSize() const + { + return totalRemoteStorageSize_; + } + + static int getCumulativeTotalStorageSize() + { + return (totalLocalStorageSize_ > totalRemoteStorageSize_ ? + totalLocalStorageSize_ : totalRemoteStorageSize_); + } + + int saveStore(ostream *cachefs, md5_state_t *md5_state_stream, + md5_state_t *md5_state_client, T_checksum_action checksumAction, + T_data_action dataAction, int bigEndian); + + int loadStore(istream *cachefs, md5_state_t *md5_state_stream, + T_checksum_action checksumAction, T_data_action dataAction, + int bigEndian); + + protected: + + // + // Estimate the memory requirements of given + // instance of message. Size includes memory + // allocated from heap to store checksum and + // data. + // + + void storageSize(const Message *message, unsigned int &local, + unsigned int &remote) const; + + // + // Just used for debug. + // + + void printStorageSize(); + + // + // Repositories where to save cached messages. + // First is a vector of pointers, the second + // is a hash table used for fast lookups. + // + + T_messages *messages_; + T_checksums *checksums_; + + // + // A message object to be used as a temporary. + // Reuse the temporary object if possible, if + // not, create a new instance. + // + + Message *temporary_; + + // + // Used to calculate message's checksum. + // + + md5_state_t *md5_state_; + + private: + + // + // Used to compress data payload. + // + + StaticCompressor *compressor_; + + // + // Keep track of how many bytes + // are taken by cache. + // + + int localStorageSize_; + int remoteStorageSize_; + + static int totalLocalStorageSize_; + static int totalRemoteStorageSize_; + + // + // Used to track object allocation and deallocation. + // + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +// +// This is an ancillary class of the message +// store, used to encode extensions based on +// the minor opcode. +// + +class MinorMessageStore +{ + public: + + virtual ~MinorMessageStore() + { + } + + virtual const char *name() const = 0; + + virtual int identitySize(const unsigned char *buffer, unsigned int size) = 0; + + virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const + { + return 1; + } + + virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, unsigned char type, int bigEndian, + WriteBuffer *writeBuffer, ChannelCache *channelCache) const + { + return 1; + } + + virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int size, int bigEndian, + ChannelCache *channelCache) const + { + } + + virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int size, int bigEndian, + ChannelCache *channelCache) const + { + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const = 0; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const = 0; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const + { + } + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const + { + } + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, md5_state_t *md5_state, + int bigEndian) const = 0; +}; + +#endif /* Message_H */ + diff --git a/nxcomp/Misc.cpp b/nxcomp/Misc.cpp new file mode 100644 index 000000000..2c72259e3 --- /dev/null +++ b/nxcomp/Misc.cpp @@ -0,0 +1,1893 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> + +#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 + +// +// TCP port offset applied to any NX port specification. +// + +const int DEFAULT_NX_PROXY_PORT_OFFSET = 4000; + +// +// Default TCP port used by client proxy to listen to +// X clients and by server proxy to connect to remote. +// + +const int DEFAULT_NX_PROXY_PORT = 8; + +// +// Default X display number that client proxy imitates. +// + +const int DEFAULT_NX_X_PORT = 8; + +// +// Default ports used for listening for cups, samba, http, +// multimedia and auxiliary X connections. Arbitrary ports +// can be used by passing the service's port at the proxy +// startup. By default ports are determined by adding the +// offset below to the offset of the proxied display. For +// example, if the proxy is impersonating the display :8, +// SMB tunnels can be created by connecting to port 3008. +// +// Considering that the NX server uses to start the first +// session at display offset 1000, we must lower the CUPS +// and SMB ports to avoid interference with normal X ses- +// sions run on the server. +// +// Font server connections are used to let the X server on +// the client connect to a font server on the NX server. +// +// Slave channels can be originated by both sides so we need +// different offsets in the case the user runs both proxies +// on the same host. +// + +const int DEFAULT_NX_CUPS_PORT_OFFSET = 2000; +const int DEFAULT_NX_SMB_PORT_OFFSET = 3000; +const int DEFAULT_NX_MEDIA_PORT_OFFSET = 7000; +const int DEFAULT_NX_AUX_PORT_OFFSET = 8000; +const int DEFAULT_NX_HTTP_PORT_OFFSET = 9000; +const int DEFAULT_NX_FONT_PORT_OFFSET = 10000; + +const int DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET = 11000; +const int DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET = 12000; + +// +// Usage info and copyright. +// + +static const char UsageInfo[] = +"\n\ + Usage: nxproxy [OPTIONS] host:port\n\ +\n\ + -C Specify that nxproxy has to run on the 'X client'\n\ + side, listening for connections and impersonating\n\ + an X server.\n\ +\n\ + -S Specify that nxproxy has to run in 'X server' mode,\n\ + thus forwarding the connections to daemons running\n\ + on the client.\n\ +\n\ + -h Print this message.\n\ +\n\ + -v Print version information.\n\ +\n\ + host:port Put at the end, specifies the host and port of the\n\ + listening proxy.\n\ +\n\ + name=value Set the NX option to the provided value.\n\ +\n\ + Multiple NX options can be specified in the DISPLAY environment\n\ + or on the command line, by using the nx/nx,option=value notation.\n\ +\n\ + Options:\n\ +\n\ + link=s An indication of the link speed that is going to be\n\ + used between the proxies. Usually the compression\n\ + and the other link parameters depend on this setting.\n\ + The value can be either 'modem', 'isdn', 'adsl',\n\ + 'wan', 'lan', 'local' or a bandwidth specification,\n\ + like for example '56k', '1m', '100m', etc.\n\ +\n\ + type=s Type of session, for example 'windows', 'unix-kde'.\n\ + 'unix-application', etc.\n\ +\n\ + display=s Specify the real display where X connections have\n\ + to be forwarded by the proxy running on the client.\n\ +\n\ + listen=n Local port used for accepting the proxy connection.\n\ +\n\ + accept=s Name or IP of host that can connect to the proxy.\n\ +\n\ + connect=s Name or IP of host that the proxy will connect to.\n\ +\n\ + port=n Remote port used for the connection.\n\ +\n\ + retry=n Number of connection atempts.\n\ +\n\ + root=s The root directory for the session. Usually is the\n\ + C-* or S-* in the .nx directory in the user's home,\n\ + with '*' being the virtual display.\n\ +\n\ + session=s Name of the session file. The default is the name\n\ + 'session' in the session directory.\n\ +\n\ + errors=s Name of the log file used by the proxy. The default\n\ + is the name 'errors' in the session directory.\n\ +\n\ + stats=s Name of the file where are written the proxy stat-\n\ + istics. The default is a file 'stats' in the session\n\ + directory. The proxy replaces the data in the file\n\ + whenever it receives a SIGUSR1 or SIGUSR2 signal:\n\ +\n\ + SIGUSR1 Gives total statistics, i.e. statistics\n\ + collected since the beginning of the\n\ + session.\n\ +\n\ + SIGUSR2 Gives partial statistics, i.e. statist-\n\ + ics collected since the last time this\n\ + signal was received.\n\ +\n\ + cookie=s Use the provided cookie for authenticating to the\n\ + remote proxy. The same cookie is used as the fake\n\ + value used for the X authorization. The fake cookie\n\ + is replaced on the X server side with the real cookie\n\ + to be used for the display, so that the real cookie\n\ + doesn't have to travel over the net. When not using\n\ + a proxy cookie, any host will be able to connect to\n\ + the proxy. See also the 'accept' parameter.\n\ +\n\ + nodelay=b A boolean indicating if TCP_NODELAY has to be set\n\ + on the proxy link. Old Linux kernels had problems\n\ + with handling TCP_NODELAY on PPP links.\n\ +\n\ + policy=b Let or not the agent decide when it is the best time\n\ + to flush the proxy link. If set to 0, the proxy will\n\ + flush any encoded data immediately. The option has\n\ + only effect on the X client side proxy.\n\ +\n\ + render=b Enable or disable use of the RENDER extension.\n\ +\n\ + taint=b Try to suppress trivial sources of X roundtrips by\n\ + generating the reply on the X client side.\n\ +\n\ + delta=b Enable X differential compression.\n\ +\n\ + data=n Enable or disable the ZLIB data compression. It is\n\ + possible to specify a value between 0 and 9. Usual-\n\ + ly the value is chosen automatically based on the\n\ + requested link setting.\n\ +\n\ + stream=n Enable or disable the ZLIB stream compression. The\n\ + value, between 0 and 9, is usually determined accor-\n\ + ding to the requested link setting.\n\ +\n\ + limit=n Specify a bitrate limit allowed for this session.\n\ +\n\ + memory=n Trigger memory optimizations used to keep small the\n\ + size of X buffers. This is useful on embedded plat-\n\ + forms, or where memory is scarce.\n\ +\n\ + cache=n Size of the in-memory X message cache. Setting the\n\ + value to 0 will disable the memory cache as well\n\ + as the NX differential compression.\n\ +\n\ + images=n Size of the persistent image cache.\n\ +\n\ + shseg=n Enable the use of the MIT-SHM extension between the\n\ + NX client proxy and the real X server. A value greater\n\ + than 1 is assumed to be the size of requested shared\n\ + memory segment. By default, the size of the segment is\n\ + determined based on the size of the in-memory cache.\n\ +\n\ + load=b Enable loading a persistent X message cache at the\n\ + proxy startup.\n\ +\n\ + save=b Enable saving a persistent X message cache at the\n\ + end of session.\n\ +\n\ + cups=n Enable or disable forwarding of CUPS connections,\n\ + by listening on the optional port 'n'.\n\ +\n\ + aux=n Enable or disable forwarding of the auxiliary X chan-\n\ + nel used for controlling the keyboard. The 'keybd=n'\n\ + form is accepted for backward compatibility.\n\ +\n\ + smb=n Enable or disable forwarding of SMB connections. The\n\ + 'samba=n' form is accepted for backward compatibility.\n\ +\n\ + media=n Enable forwarding of audio connections.\n\ +\n\ + http=n Enable forwarding of HTTP connections.\n\ +\n\ + font=n Enable forwarding of reversed connections to a font\n\ + server running on the NX server.\n\ +\n\ + file=n Enable forwarding of file transfer connections.\n\ +\n\ + mask=n Determine the distribution of channel ids between the\n\ + proxies. By default, channels whose ids are multiple\n\ + of 8 (starting from 0) are reserved for the NX client\n\ + side. All the other channels can be allocated by the\n\ + NX server side.\n\ +\n\ + timeout=t Specify the keep-alive timeout used by proxies to\n\ + determine if there is a network problem preventing\n\ + communication with the remote peer. A value of 0\n\ + disables the check.\n\ +\n\ + cleanup=t Specify the number of seconds the proxy has to wait\n\ + at session shutdown before closing all channels.\n\ + The feature is used by the NX server to ensure that\n\ + services are disconnected before shutting down the\n\ + link.\n\ +\n\ + pack=s Determine the method used to compress images.\n\ +\n\ + product=s The product id of the client or server. The value is\n\ + ignored by the proxy, but the client or server can\n\ + provide it to facilitate the support.\n\ +\n\ + core=b Enable production of core dumps when aborting the\n\ + proxy connection.\n\ +\n\ + options=s Specify an additional file containing options that\n\ + has to be merged with option read from the command\n\ + line or the environment.\n\ +\n\ + kill=n Add the given process to the list of daemons that\n\ + must be terminated at session shutdown. Multiple\n\ + 'kill=n' options can be specified. The proxy will\n\ + send them a SIGTERM signal just before exiting.\n\ +\n\ + strict=b Optimize for responsiveness, rather than for the best\n\ + use of all the available bandwidth.\n\ +\n\ + encryption=b Should be set to 1 if the proxy is running as part of\n\ + a program providing encryption of the point to point\n\ + communication.\n\ +\n\ +rootless=b\n\ +geometry=s\n\ +resize=b\n\ +fullscreen=b\n\ +keyboard=s\n\ +clipboard=n\n\ +streaming=n\n\ +backingstore=n\n\ +composite=n\n\ +shmem=b\n\ +shpix=b\n\ +kbtype=s\n\ +client=s\n\ +shadow=n\n\ +shadowuid=n\n\ +shadowmode=s\n\ +defer=n\n\ +tile=s\n\ +menu=n These options are interpreted by the NX agent. They\n\ + are ignored by the proxy.\n\ +\n\ + Environment:\n\ +\n\ + NX_ROOT The root NX directory is the place where the session\n\ + directory and the cache files are created. This is\n\ + usually overridden by passing the 'root=' option. By\n\ + default, the root NX directory is assumed to be the\n\ + directory '.nx' in the user's home.\n\ +\n\ + NX_SYSTEM The directory where NX programs and libraries reside.\n\ + If not set, the value is assumed to be '/usr/NX'.\n\ + Programs, libraries and data files are respectedly\n\ + searched in the 'bin', 'lib' and 'share' subdirecto-\n\ + ries.\n\ +\n\ + NX_HOME The NX user's home directory. If NX_ROOT is not set\n\ + or invalid, the user's NX directory is created here.\n\ +\n\ + NX_TEMP The directory where the X11 Unix Domain Sockets and\n\ + all temporary files are to be created.\n\ +\n\ + NX_CLIENT The full path to the nxclient executable. If the va-\n\ + riable is not set, the nxclient executable will be\n\ + run assuming that the program is in the system path.\n\ + This can be useful on platforms like Windows and the\n\ + Mac where nxclient is located in a different direct-\n\ + ory compared to the other programs, to make easier\n\ + for the user to execute the program from the shell.\n\ +\n\ + Shell environment:\n\ +\n\ + HOME The variable is checked in the case NX_HOME is not\n\ + set, null or invalid.\n\ +\n\ + TEMP The variable is checked whenever the NX_TEMP direct-\n\ + ory is not set, null or invalid.\n\ +\n\ + PATH The path where all executables are searched, except\n\ + nxclient. If NX_CLIENT is not set, also the client\n\ + executable is searched in the system path.\n\ +\n\ + LD_LIBRARY_PATH\n\ + System-wide library search order. This should be set\n\ + by the program invoking the proxy.\n\ +\n\ + DISPLAY On the X server side, the DISPLAY variable indicates\n\ + the location of the X11 server. When nxcomp is used\n\ + as a transport library, the DISPLAY may represent a\n\ + NX transport specification and options can passed in\n\ + the form nx/nx,option=value...\n\ +\n\ + XAUTHORITY This is the file containing the X11 authorization\n\ + cookie. If not set, the file is assumed to be in\n\ + the user's home (either NX_HOME or HOME).\n\ +\n\ +"; + +const char *GetUsageInfo() +{ + return UsageInfo; +} + +static const char CopyrightInfo[] = +"\ +Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/.\n\ +\n\ +NXCOMP, NX protocol compression and NX extensions to this software \n\ +are copyright of NoMachine. Redistribution and use of the present\n\ +software is allowed according to terms specified in the file LICENSE\n\ +which comes in the source distribution.\n\ +\n\ +Check http://www.nomachine.com/licensing.html for applicability.\n\ +\n\ +NX and NoMachine are trademarks of NoMachine S.r.l.\n\ +\n\ +All rights reserved.\n\ +"; + +const char *GetCopyrightInfo() +{ + return CopyrightInfo; +} + +static const char OtherCopyrightInfo[] = +"\ +NX protocol compression is derived from DXPC project.\n\ +\n\ +Copyright (c) 1995,1996 Brian Pane\n\ +Copyright (c) 1996,1997 Zachary Vonler and Brian Pane\n\ +Copyright (c) 1999 Kevin Vigor and Brian Pane\n\ +Copyright (c) 2000,2003 Gian Filippo Pinzari and Brian Pane\n\ +\n\ +All rights reserved.\n\ +"; + +const char *GetOtherCopyrightInfo() +{ + return OtherCopyrightInfo; +} + +int _hostBigEndian = 0; +int _storeBigEndian = 0; + +const unsigned int IntMask[33] = +{ + 0x00000000, + 0x00000001, + 0x00000003, + 0x00000007, + 0x0000000f, + 0x0000001f, + 0x0000003f, + 0x0000007f, + 0x000000ff, + 0x000001ff, + 0x000003ff, + 0x000007ff, + 0x00000fff, + 0x00001fff, + 0x00003fff, + 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff, + 0x00ffffff, + 0x01ffffff, + 0x03ffffff, + 0x07ffffff, + 0x0fffffff, + 0x1fffffff, + 0x3fffffff, + 0x7fffffff, + 0xffffffff +}; + +unsigned int GetUINT(unsigned const char *buffer, int bigEndian) +{ + // + // It doesn't work on SPARCs if the buffer + // is not aligned to the word boundary. We + // should check the CPU, not the OS as this + // surely applies to other architectures. + // + + #ifndef __sun + + if (_hostBigEndian == bigEndian) + { + return *((unsigned short *) buffer); + } + + #else + + if (_hostBigEndian == bigEndian && ((unsigned int) buffer) & 0x1 == 0) + { + return *((unsigned short *) buffer); + } + + #endif + + unsigned int result; + + if (bigEndian) + { + result = *buffer; + + result <<= 8; + + result += buffer[1]; + } + else + { + result = buffer[1]; + + result <<= 8; + + result += *buffer; + } + + return result; +} + +unsigned int GetULONG(unsigned const char *buffer, int bigEndian) +{ + // + // It doesn't work on SPARCs if the buffer + // is not aligned to word the boundary. + // + + #ifndef __sun + + if (_hostBigEndian == bigEndian) + { + return *((unsigned int *) buffer); + } + + #else + + if (_hostBigEndian == bigEndian && ((unsigned int) buffer) & 0x3 == 0) + { + return *((unsigned int *) buffer); + } + + #endif + + const unsigned char *next = (bigEndian ? buffer : buffer + 3); + + unsigned int result = 0; + + for (int i = 0; i < 4; i++) + { + result <<= 8; + + result += *next; + + if (bigEndian) + { + next++; + } + else + { + next--; + } + } + + return result; +} + +void PutUINT(unsigned int value, unsigned char *buffer, int bigEndian) +{ + if (_hostBigEndian == bigEndian) + { + *((unsigned short *) buffer) = value; + + return; + } + + if (bigEndian) + { + buffer[1] = (unsigned char) (value & 0xff); + + value >>= 8; + + *buffer = (unsigned char) value; + } + else + { + *buffer = (unsigned char) (value & 0xff); + + value >>= 8; + + buffer[1] = (unsigned char) value; + } +} + +void PutULONG(unsigned int value, unsigned char *buffer, int bigEndian) +{ + if (_hostBigEndian == bigEndian) + { + *((unsigned int *) buffer) = value; + + return; + } + + if (bigEndian) + { + buffer += 3; + + for (int i = 4; i > 0; i--) + { + *buffer-- = (unsigned char) (value & 0xff); + + value >>= 8; + } + } + else + { + for (int i = 4; i > 0; i--) + { + *buffer++ = (unsigned char) (value & 0xff); + + value >>= 8; + } + } +} + +int CheckData(istream *fs) +{ + if (fs == NULL || fs -> fail()) + { + return -1; + } + + return 1; +} + +int CheckData(ostream *fs) +{ + if (fs == NULL || fs -> fail()) + { + return -1; + } + + return 1; +} + +int PutData(ostream *fs, const unsigned char *buffer, int size) +{ + fs -> write((char *) buffer, size); + + #ifdef DEBUG + *logofs << "PutData: Written " << size << " bytes with eof " + << fs -> eof() << " fail " << fs -> fail() << " and bad " + << fs -> bad() << ".\n" << logofs_flush; + #endif + + if (fs -> fail()) + { + return -1; + } + + return size; +} + +int GetData(istream *fs, unsigned char *buffer, int size) +{ + fs -> read((char *) buffer, size); + + #ifdef DEBUG + *logofs << "GetData: Read " << size << " bytes with eof " + << fs -> eof() << " fail " << fs -> fail() + << " and bad " << fs -> bad() << ".\n" + << logofs_flush; + #endif + + #ifdef __APPLE__ + + if (fs -> bad()) + { + return -1; + } + + #else + + if (fs -> fail()) + { + return -1; + } + + #endif + + return size; +} + +int FlushData(ostream *fs) +{ + fs -> flush(); + + if (fs -> fail()) + { + return -1; + } + + return 1; +} + +unsigned int RoundUp2(unsigned int x) +{ + unsigned int y = x / 2; + + y *= 2; + + if (y != x) + { + y += 2; + } + + return y; +} + +unsigned int RoundUp4(unsigned int x) +{ + unsigned int y = x / 4; + + y *= 4; + + if (y != x) + { + y += 4; + } + + return y; +} + +unsigned int RoundUp8(unsigned int x) +{ + unsigned int y = x / 8; + + y *= 8; + + if (y != x) + { + y += 8; + } + + return y; +} + +const char *DumpSignal(int signal) +{ + switch (signal) + { + case SIGCHLD: + { + return "SIGCHLD"; + } + case SIGUSR1: + { + return "SIGUSR1"; + } + case SIGUSR2: + { + return "SIGUSR2"; + } + case SIGHUP: + { + return "SIGHUP"; + } + case SIGINT: + { + return "SIGINT"; + } + case SIGTERM: + { + return "SIGTERM"; + } + case SIGPIPE: + { + return "SIGPIPE"; + } + case SIGALRM: + { + return "SIGALRM"; + } + case SIGVTALRM: + { + return "SIGVTALRM"; + } + case SIGWINCH: + { + return "SIGWINCH"; + } + case SIGIO: + { + return "SIGIO"; + } + case SIGTSTP: + { + return "SIGTSTP"; + } + case SIGTTIN: + { + return "SIGTTIN"; + } + case SIGTTOU: + { + return "SIGTTOU"; + } + case SIGSEGV: + { + return "SIGSEGV"; + } + case SIGABRT: + { + return "SIGABRT"; + } + default: + { + return "Unknown"; + } + } +} + +const char *DumpPolicy(int type) +{ + switch ((T_flush_policy) type) + { + case policy_immediate: + { + return "immediate"; + } + case policy_deferred: + { + return "deferred"; + } + default: + { + #ifdef PANIC + *logofs << "Misc: PANIC! Unknown policy type '" + << type << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unknown policy type '" + << type << "'.\n"; + + HandleCleanup(); + } + } +} + +const char *DumpAction(int type) +{ + T_store_action action = (T_store_action) type; + + if (action == IS_HIT) + { + return "is_hit"; + } + else if (action == IS_ADDED) + { + return "is_added"; + } + else if (action == is_discarded) + { + return "is_discarded"; + } + else if (action == is_removed) + { + return "is_removed"; + } + else + { + #ifdef PANIC + *logofs << "Misc: PANIC! Unknown store action '" + << type << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unknown store action '" + << type << "'.\n"; + + HandleCleanup(); + } +} + +const char *DumpState(int type) +{ + switch ((T_split_state) type) + { + case split_added: + { + return "split_added"; + } + case split_missed: + { + return "split_missed"; + } + case split_loaded: + { + return "split_loaded"; + } + case split_aborted: + { + return "split_aborted"; + } + case split_notified: + { + return "split_notified"; + } + default: + { + #ifdef PANIC + *logofs << "Misc: PANIC! Unknown split state '" + << type << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unknown split state '" + << type << "'.\n"; + + HandleCleanup(); + } + } +} + +const char *DumpControl(int code) +{ + switch ((T_proxy_code) code) + { + case code_new_x_connection: + { + return "code_new_x_connection"; + } + case code_new_cups_connection: + { + return "code_new_cups_connection"; + } + case code_new_aux_connection: + { + return "code_new_aux_connection"; + } + case code_new_smb_connection: + { + return "code_new_smb_connection"; + } + case code_new_media_connection: + { + return "code_new_media_connection"; + } + case code_switch_connection: + { + return "code_switch_connection"; + } + case code_drop_connection: + { + return "code_drop_connection"; + } + case code_finish_connection: + { + return "code_finish_connection"; + } + case code_begin_congestion: + { + return "code_begin_congestion"; + } + case code_end_congestion: + { + return "code_end_congestion"; + } + case code_alert_request: + { + return "code_alert_request"; + } + case code_alert_reply: + { + return "code_alert_reply"; + } + case code_reset_request: + { + return "code_reset_request"; + } + case code_reset_reply: + { + return "code_reset_reply"; + } + case code_load_request: + { + return "code_load_request"; + } + case code_load_reply: + { + return "code_load_reply"; + } + case code_save_request: + { + return "code_save_request"; + } + case code_save_reply: + { + return "code_save_reply"; + } + case code_shutdown_request: + { + return "code_shutdown_request"; + } + case code_shutdown_reply: + { + return "code_shutdown_reply"; + } + case code_control_token_request: + { + return "code_control_token_request"; + } + case code_control_token_reply: + { + return "code_control_token_reply"; + } + case code_configuration_request: + { + return "code_configuration_request"; + } + case code_configuration_reply: + { + return "code_configuration_reply"; + } + case code_statistics_request: + { + return "code_statistics_request"; + } + case code_statistics_reply: + { + return "code_statistics_reply"; + } + case code_new_http_connection: + { + return "code_new_http_connection"; + } + case code_sync_request: + { + return "code_sync_request"; + } + case code_sync_reply: + { + return "code_sync_reply"; + } + case code_new_font_connection: + { + return "code_new_font_connection"; + } + case code_new_slave_connection: + { + return "code_new_slave_connection"; + } + case code_finish_listeners: + { + return "code_finish_listeners"; + } + case code_split_token_request: + { + return "code_split_token_request"; + } + case code_split_token_reply: + { + return "code_split_token_reply"; + } + case code_data_token_request: + { + return "code_data_token_request"; + } + case code_data_token_reply: + { + return "code_data_token_reply"; + } + default: + { + #ifdef WARNING + *logofs << "Misc: WARNING! Unknown control code '" + << code << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Unknown control code '" + << code << "'.\n"; + + return "unknown"; + } + } +} + +const char *DumpSession(int code) +{ + switch ((T_session_mode) code) + { + case session_agent: + { + return "session_agent"; + } + case session_shadow: + { + return "session_shadow"; + } + case session_proxy: + { + return "session_proxy"; + } + default: + { + #ifdef WARNING + *logofs << "Misc: WARNING! Unknown session type '" + << code << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Unknown session type '" + << code << "'.\n"; + + return "unknown"; + } + } +} + +const char *DumpToken(int type) +{ + switch ((T_token_type) type) + { + case token_control: + { + return "token_control"; + } + case token_split: + { + return "token_split"; + } + case token_data: + { + return "token_data"; + } + default: + { + #ifdef WARNING + *logofs << "Misc: WARNING! Unknown token type '" + << type << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Unknown token type '" + << type << "'.\n"; + + return "unknown"; + } + } +} + +// +// Always include this in code as it is generally +// needed to test channels and split store. +// + +const char *DumpChecksum(const void *checksum) +{ + static char string[MD5_LENGTH * 2 + 1]; + + if (checksum != NULL) + { + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + sprintf(string + (i * 2), "%02X", ((unsigned char *) checksum)[i]); + } + } + else + { + strcpy(string, "null"); + } + + return string; +} + +// +// Define OPCODES here and in the channel +// if you want to log the opcode literal. +// + +#ifdef OPCODES + +const char *DumpOpcode(const int &opcode) +{ + switch (opcode) + { + case X_CreateWindow: + { + return "X_CreateWindow"; + } + case X_ChangeWindowAttributes: + { + return "X_ChangeWindowAttributes"; + } + case X_GetWindowAttributes: + { + return "X_GetWindowAttributes"; + } + case X_DestroyWindow: + { + return "X_DestroyWindow"; + } + case X_DestroySubwindows: + { + return "X_DestroySubwindows"; + } + case X_ChangeSaveSet: + { + return "X_ChangeSaveSet"; + } + case X_ReparentWindow: + { + return "X_ReparentWindow"; + } + case X_MapWindow: + { + return "X_MapWindow"; + } + case X_MapSubwindows: + { + return "X_MapSubwindows"; + } + case X_UnmapWindow: + { + return "X_UnmapWindow"; + } + case X_UnmapSubwindows: + { + return "X_UnmapSubwindows"; + } + case X_ConfigureWindow: + { + return "X_ConfigureWindow"; + } + case X_CirculateWindow: + { + return "X_CirculateWindow"; + } + case X_GetGeometry: + { + return "X_GetGeometry"; + } + case X_QueryTree: + { + return "X_QueryTree"; + } + case X_InternAtom: + { + return "X_InternAtom"; + } + case X_GetAtomName: + { + return "X_GetAtomName"; + } + case X_ChangeProperty: + { + return "X_ChangeProperty"; + } + case X_DeleteProperty: + { + return "X_DeleteProperty"; + } + case X_GetProperty: + { + return "X_GetProperty"; + } + case X_ListProperties: + { + return "X_ListProperties"; + } + case X_SetSelectionOwner: + { + return "X_SetSelectionOwner"; + } + case X_GetSelectionOwner: + { + return "X_GetSelectionOwner"; + } + case X_ConvertSelection: + { + return "X_ConvertSelection"; + } + case X_SendEvent: + { + return "X_SendEvent"; + } + case X_GrabPointer: + { + return "X_GrabPointer"; + } + case X_UngrabPointer: + { + return "X_UngrabPointer"; + } + case X_GrabButton: + { + return "X_GrabButton"; + } + case X_UngrabButton: + { + return "X_UngrabButton"; + } + case X_ChangeActivePointerGrab: + { + return "X_ChangeActivePointerGrab"; + } + case X_GrabKeyboard: + { + return "X_GrabKeyboard"; + } + case X_UngrabKeyboard: + { + return "X_UngrabKeyboard"; + } + case X_GrabKey: + { + return "X_GrabKey"; + } + case X_UngrabKey: + { + return "X_UngrabKey"; + } + case X_AllowEvents: + { + return "X_AllowEvents"; + } + case X_GrabServer: + { + return "X_GrabServer"; + } + case X_UngrabServer: + { + return "X_UngrabServer"; + } + case X_QueryPointer: + { + return "X_QueryPointer"; + } + case X_GetMotionEvents: + { + return "X_GetMotionEvents"; + } + case X_TranslateCoords: + { + return "X_TranslateCoords"; + } + case X_WarpPointer: + { + return "X_WarpPointer"; + } + case X_SetInputFocus: + { + return "X_SetInputFocus"; + } + case X_GetInputFocus: + { + return "X_GetInputFocus"; + } + case X_QueryKeymap: + { + return "X_QueryKeymap"; + } + case X_OpenFont: + { + return "X_OpenFont"; + } + case X_CloseFont: + { + return "X_CloseFont"; + } + case X_QueryFont: + { + return "X_QueryFont"; + } + case X_QueryTextExtents: + { + return "X_QueryTextExtents"; + } + case X_ListFonts: + { + return "X_ListFonts"; + } + case X_ListFontsWithInfo: + { + return "X_ListFontsWithInfo"; + } + case X_SetFontPath: + { + return "X_SetFontPath"; + } + case X_GetFontPath: + { + return "X_GetFontPath"; + } + case X_CreatePixmap: + { + return "X_CreatePixmap"; + } + case X_FreePixmap: + { + return "X_FreePixmap"; + } + case X_CreateGC: + { + return "X_CreateGC"; + } + case X_ChangeGC: + { + return "X_ChangeGC"; + } + case X_CopyGC: + { + return "X_CopyGC"; + } + case X_SetDashes: + { + return "X_SetDashes"; + } + case X_SetClipRectangles: + { + return "X_SetClipRectangles"; + } + case X_FreeGC: + { + return "X_FreeGC"; + } + case X_ClearArea: + { + return "X_ClearArea"; + } + case X_CopyArea: + { + return "X_CopyArea"; + } + case X_CopyPlane: + { + return "X_CopyPlane"; + } + case X_PolyPoint: + { + return "X_PolyPoint"; + } + case X_PolyLine: + { + return "X_PolyLine"; + } + case X_PolySegment: + { + return "X_PolySegment"; + } + case X_PolyRectangle: + { + return "X_PolyRectangle"; + } + case X_PolyArc: + { + return "X_PolyArc"; + } + case X_FillPoly: + { + return "X_FillPoly"; + } + case X_PolyFillRectangle: + { + return "X_PolyFillRectangle"; + } + case X_PolyFillArc: + { + return "X_PolyFillArc"; + } + case X_PutImage: + { + return "X_PutImage"; + } + case X_GetImage: + { + return "X_GetImage"; + } + case X_PolyText8: + { + return "X_PolyText8"; + } + case X_PolyText16: + { + return "X_PolyText16"; + } + case X_ImageText8: + { + return "X_ImageText8"; + } + case X_ImageText16: + { + return "X_ImageText16"; + } + case X_CreateColormap: + { + return "X_CreateColormap"; + } + case X_FreeColormap: + { + return "X_FreeColormap"; + } + case X_CopyColormapAndFree: + { + return "X_CopyColormapAndFree"; + } + case X_InstallColormap: + { + return "X_InstallColormap"; + } + case X_UninstallColormap: + { + return "X_UninstallColormap"; + } + case X_ListInstalledColormaps: + { + return "X_ListInstalledColormaps"; + } + case X_AllocColor: + { + return "X_AllocColor"; + } + case X_AllocNamedColor: + { + return "X_AllocNamedColor"; + } + case X_AllocColorCells: + { + return "X_AllocColorCells"; + } + case X_AllocColorPlanes: + { + return "X_AllocColorPlanes"; + } + case X_FreeColors: + { + return "X_FreeColors"; + } + case X_StoreColors: + { + return "X_StoreColors"; + } + case X_StoreNamedColor: + { + return "X_StoreNamedColor"; + } + case X_QueryColors: + { + return "X_QueryColors"; + } + case X_LookupColor: + { + return "X_LookupColor"; + } + case X_CreateCursor: + { + return "X_CreateCursor"; + } + case X_CreateGlyphCursor: + { + return "X_CreateGlyphCursor"; + } + case X_FreeCursor: + { + return "X_FreeCursor"; + } + case X_RecolorCursor: + { + return "X_RecolorCursor"; + } + case X_QueryBestSize: + { + return "X_QueryBestSize"; + } + case X_QueryExtension: + { + return "X_QueryExtension"; + } + case X_ListExtensions: + { + return "X_ListExtensions"; + } + case X_ChangeKeyboardMapping: + { + return "X_ChangeKeyboardMapping"; + } + case X_GetKeyboardMapping: + { + return "X_GetKeyboardMapping"; + } + case X_ChangeKeyboardControl: + { + return "X_ChangeKeyboardControl"; + } + case X_GetKeyboardControl: + { + return "X_GetKeyboardControl"; + } + case X_Bell: + { + return "X_Bell"; + } + case X_ChangePointerControl: + { + return "X_ChangePointerControl"; + } + case X_GetPointerControl: + { + return "X_GetPointerControl"; + } + case X_SetScreenSaver: + { + return "X_SetScreenSaver"; + } + case X_GetScreenSaver: + { + return "X_GetScreenSaver"; + } + case X_ChangeHosts: + { + return "X_ChangeHosts"; + } + case X_ListHosts: + { + return "X_ListHosts"; + } + case X_SetAccessControl: + { + return "X_SetAccessControl"; + } + case X_SetCloseDownMode: + { + return "X_SetCloseDownMode"; + } + case X_KillClient: + { + return "X_KillClient"; + } + case X_RotateProperties: + { + return "X_RotateProperties"; + } + case X_ForceScreenSaver: + { + return "X_ForceScreenSaver"; + } + case X_SetPointerMapping: + { + return "X_SetPointerMapping"; + } + case X_GetPointerMapping: + { + return "X_GetPointerMapping"; + } + case X_SetModifierMapping: + { + return "X_SetModifierMapping"; + } + case X_GetModifierMapping: + { + return "X_GetModifierMapping"; + } + case X_NoOperation: + { + return "X_NoOperation"; + } + case X_NXInternalGenericData: + { + return "X_NXInternalGenericData"; + } + // + // case X_NXInternalGenericReply: + // { + // return "X_NXInternalGenericReply"; + // } + // + case X_NXInternalGenericRequest: + { + return "X_NXInternalGenericRequest"; + } + case X_NXInternalShapeExtension: + { + return "X_NXInternalShapeExtension"; + } + case X_NXGetControlParameters: + { + return "X_NXGetControlParameters"; + } + case X_NXGetCleanupParameters: + { + return "X_NXGetCleanupParameters"; + } + case X_NXGetImageParameters: + { + return "X_NXGetImageParameters"; + } + case X_NXGetUnpackParameters: + { + return "X_NXGetUnpackParameters"; + } + case X_NXGetShmemParameters: + { + return "X_NXGetShmemParameters"; + } + case X_NXGetFontParameters: + { + return "X_NXGetFontParameters"; + } + case X_NXSetExposeParameters: + { + return "X_NXSetExposeParameters"; + } + case X_NXSetCacheParameters: + { + return "X_NXSetCacheParameters"; + } + case X_NXStartSplit: + { + return "X_NXStartSplit"; + } + case X_NXEndSplit: + { + return "X_NXEndSplit"; + } + case X_NXSplitData: + { + return "X_NXSplitData"; + } + case X_NXSplitEvent: + { + return "X_NXSplitEvent"; + } + case X_NXCommitSplit: + { + return "X_NXCommitSplit"; + } + case X_NXFinishSplit: + { + return "X_NXFinishSplit"; + } + case X_NXAbortSplit: + { + return "X_NXAbortSplit"; + } + case X_NXFreeSplit: + { + return "X_NXFreeSplit"; + } + case X_NXSetUnpackGeometry: + { + return "X_NXSetUnpackGeometry"; + } + case X_NXSetUnpackColormap: + { + return "X_NXSetUnpackColormap"; + } + case X_NXSetUnpackAlpha: + { + return "X_NXSetUnpackAlpha"; + } + case X_NXPutPackedImage: + { + return "X_NXPutPackedImage"; + } + case X_NXFreeUnpack: + { + return "X_NXFreeUnpack"; + } + default: + { + if (opcode > 127) + { + return "Extension"; + } + else + { + return "?"; + } + } + } +} + +#else /* #ifdef OPCODES */ + +const char *DumpOpcode(const int &opcode) +{ + return "?"; +} + +#endif /* #ifdef OPCODES */ + +void DumpData(const unsigned char *buffer, unsigned int size) +{ + if (buffer != NULL) + { + unsigned int i = 0; + + while (i < size) + { + *logofs << "[" << i << "]\t"; + + for (unsigned int ii = 0; i < size && ii < 8; i++, ii++) + { + *logofs << (unsigned int) (buffer[i]) << "\t"; + } + + *logofs << "\n" << logofs_flush; + } + } +} + +void DumpChecksum(const unsigned char *buffer, unsigned int size) +{ + if (buffer != NULL) + { + md5_byte_t md5_digest[MD5_LENGTH]; + + md5_state_t md5_state; + + md5_init(&md5_state); + + md5_append(&md5_state, buffer, size); + + md5_finish(&md5_state, md5_digest); + + char md5_string[MD5_LENGTH * 2 + 1]; + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + sprintf(md5_string + (i * 2), "%02X", md5_digest[i]); + } + + *logofs << "[" << md5_string << "]" << logofs_flush; + } +} + +void DumpBlockChecksums(const unsigned char *buffer, + unsigned int size, unsigned int block) +{ + for (unsigned int i = 0; i < (size / block); i++) + { + *logofs << "[" << i * block << "]"; + + DumpChecksum(buffer + (i * block), block); + + *logofs << "\n"; + } + + if (size % block > 0) + { + *logofs << "[" << size / block * block << "]"; + + DumpChecksum(buffer + (size / block * block), size % block); + + *logofs << "\n"; + } +} + +void DumpHexData(const unsigned char *buffer, unsigned int size) +{ + char message [65536]; + char ascii [17]; + + unsigned int index = 0; + unsigned int linescan = 0; + unsigned int index_ascii = 0; + + sprintf (message,"\n#### Start Dump Buffer of [%.5d] Bytes ####\n\n",size); + + *logofs << message << logofs_flush; + + // + // "Index 0 1 2 3 4 5 6 7 8 9 a b c d e f Ascii " + // "----- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----------------" + // "00000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................" + // + + sprintf (message,"Index 0 1 2 3 4 5 6 7 8 9 a b c d e f Ascii \n"); + *logofs << message << logofs_flush; + sprintf (message,"----- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----------------\n"); + *logofs << message << logofs_flush; + + index = 0; + + while (index < size) + { + memset (ascii, ' ', sizeof(ascii)); + + ascii[16] = '\0'; + + sprintf (message,"%.5d ", index); + + for (index_ascii = 0, linescan = index; + ((index < (linescan + 16)) && (index < size)); + index++, index_ascii++) + { + if (isprint(buffer [index])) + { + ascii[index_ascii] = buffer [index]; + } + else + { + ascii[index_ascii] = '.'; + } + + sprintf (&message [strlen (message)],"%.2x ", (unsigned char) buffer [index]); + } + + for (linescan = index_ascii; linescan < 16; linescan++) + { + strcat (&message [strlen (message)], " "); + } + + sprintf (&message [strlen (message)]," %s\n", ascii); + + *logofs << message << logofs_flush; + } + + sprintf (message,"\n#### End Dump Buffer ####\n\n"); + + *logofs << message << logofs_flush; +} diff --git a/nxcomp/Misc.h b/nxcomp/Misc.h new file mode 100644 index 000000000..200831757 --- /dev/null +++ b/nxcomp/Misc.h @@ -0,0 +1,262 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Misc_H +#define Misc_H + +#include <iostream> +#include <fstream> + +#include <errno.h> +#include <string.h> + +#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; + +// +// Return strings containing various info. +// + +const char *GetUsageInfo(); +const char *GetCopyrightInfo(); +const char *GetOtherCopyrightInfo(); + +// +// Define this if you want immediate flush of +// the log output. +// + +#define FLUSH_LOGOFS + +// +// Global objects providing shared functions. +// + +class Auth; +class Control; +class Statistics; + +extern Auth *auth; +extern Control *control; +extern Statistics *statistics; + +// +// Log file. +// + +extern ostream *logofs; + +// +// Cleanup code. +// + +void HandleAbort() __attribute__((noreturn)); +void HandleShutdown() __attribute__((noreturn)); + +extern "C" +{ + void HandleCleanup(int code = 0) __attribute__((noreturn)); +} + +// +// Manage signal handlers. +// + +void DisableSignals(); +void EnableSignals(); + +// +// Manage timers. +// + +void SetTimer(int value); +void ResetTimer(); + +// +// Show a dialog asking the user if he/she +// wants to close the current session. Look +// in the alerts file for the known critical +// events. +// + +void HandleAlert(int code, int local); + +// +// Run the callback registered by the proxy +// or the agent. +// + +void KeeperCallback(); +void FlushCallback(int length); + +// +// Return the string literal corresponding +// the value. +// + +const char *DumpSignal(int signal); +const char *DumpPolicy(int type); +const char *DumpControl(int code); +const char *DumpSession(int code); +const char *DumpAction(int type); +const char *DumpState(int type); +const char *DumpToken(int type); + +// +// Print out content of buffer to log file. +// You need to define DUMP or OPCODES in +// the source to have these compiled. +// + +const char *DumpOpcode(const int &opcode); +const char *DumpChecksum(const void *checksum); + +void DumpData(const unsigned char *data, unsigned int length); +void DumpHexData(const unsigned char *data, unsigned int length); +void DumpChecksum(const unsigned char *data, unsigned int length); +void DumpBlockChecksums(const unsigned char *data, unsigned int length, + unsigned int block); + +// +// Defines logofs_flush as an empty string to +// avoid calling the corresponding ostream's +// flush() function. +// + +#ifdef FLUSH_LOGOFS + +#define logofs_flush "" ; logofs -> flush() + +#else + +#define logofs_flush "" + +#endif + +// +// Is the host where local proxy is running +// big-endian? +// + +extern int _hostBigEndian; +extern int _storeBigEndian; + +inline void setHostBigEndian(int flag) +{ + _hostBigEndian = flag; +} + +inline int hostBigEndian() +{ + return _hostBigEndian; +} + +inline int storeBigEndian() +{ + return _storeBigEndian; +} + +extern const unsigned int IntMask[33]; + +unsigned int GetUINT(unsigned const char *buffer, int bigEndian); +unsigned int GetULONG(unsigned const char *buffer, int bigEndian); +void PutUINT(unsigned int value, unsigned char *buffer, int bigEndian); +void PutULONG(unsigned int value, unsigned char *buffer, int bigEndian); + +inline void CleanData(unsigned char *buffer, int size) +{ + unsigned char *end = buffer + size; + + while (buffer < end) + { + *buffer++ = 0x00; + } +} + +int CheckData(istream *fs); +int CheckData(ostream *fs); +int PutData(ostream *fs, const unsigned char *buffer, int size); +int GetData(istream *fs, unsigned char *buffer, int size); +int FlushData(ostream *fs); + +unsigned int RoundUp2(unsigned int x); +unsigned int RoundUp4(unsigned int x); +unsigned int RoundUp8(unsigned int x); + +#endif /* Misc_H */ diff --git a/nxcomp/NX.h b/nxcomp/NX.h new file mode 100644 index 000000000..d98af79bb --- /dev/null +++ b/nxcomp/NX.h @@ -0,0 +1,449 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef NX_H +#define NX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <unistd.h> + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/select.h> +#include <sys/uio.h> + +#define NX_FD_ANY -1 + +#define NX_MODE_ANY -1 +#define NX_MODE_CLIENT 1 +#define NX_MODE_SERVER 2 + +#define NX_DISPLAY_ANY NULL + +#define NX_SIGNAL_ANY -1 +#define NX_SIGNAL_ENABLE 1 +#define NX_SIGNAL_DISABLE 2 +#define NX_SIGNAL_RAISE 3 +#define NX_SIGNAL_FORWARD 4 + +#define NX_POLICY_IMMEDIATE 1 +#define NX_POLICY_DEFERRED 2 + +#define NX_ALERT_REMOTE 0 +#define NX_ALERT_LOCAL 1 + +#define NX_HANDLER_FLUSH 0 +#define NX_HANDLER_STATISTICS 1 + +#define NX_STATISTICS_PARTIAL 0 +#define NX_STATISTICS_TOTAL 1 + +#define NX_CHANNEL_X11 0 +#define NX_CHANNEL_CUPS 1 +#define NX_CHANNEL_SMB 2 +#define NX_CHANNEL_MEDIA 3 +#define NX_CHANNEL_HTTP 4 +#define NX_CHANNEL_FONT 5 +#define NX_CHANNEL_SLAVE 6 + +#define NX_FILE_SESSION 0 +#define NX_FILE_ERRORS 1 +#define NX_FILE_OPTIONS 2 +#define NX_FILE_STATS 3 + +/* + * The following are the new interfaces to the NX transport. The + * NX proxy software is now intended to be run as a library of a + * higher level communication manager (nxssh, nxhttp, nxrtp, etc, + * not only nxproxy). This is a work-in-progress, so expect these + * interfaces to change in future. At the present moment, as an + * example, there is no provision for creating and managing mul- + * tiple proxy connections. + */ + +/* + * Attach a NX transport to the provided descriptor. This should be + * done after having created a pair of connected sockets. + */ + +extern int NXTransCreate(int fd, int mode, const char *options); + +/* + * Tell the proxy to use the second descriptor as its own end of + * the internal connection to the NX agent. The NX agent will use + * the first descriptor. Setting an agent connection will have the + * effect of disabling further X client connections and, if it is + * possible, will trigger the use of the memory-to-memory transport. + */ + +extern int NXTransAgent(int fd[2]); + +/* + * Prepare the file sets and the timeout for a later execution of + * the select(). The masks and the timeout must persist across all + * the calls, so if you don't need any of the values, it is requi- + * red that you create empty masks and a default timeout. To save + * a check at each run, all the functions below assume that valid + * pointers are passed. + */ + +extern int NXTransPrepare(int *maxfds, fd_set *readfds, + fd_set *writefds, struct timeval *timeout); + +/* + * Call select() to find out the descriptors in the sets having + * pending data. + */ + +extern int NXTransSelect(int *result, int *error, int *maxfds, fd_set *readfds, + fd_set *writefds, struct timeval *timeout); + +/* + * Perform the required I/O on all the NX descriptors having pen- + * ding data. This can include reading and writing to the NX chan- + * nels, encoding and decoding the proxy data or managing any of + * the other NX resources. + */ + +extern int NXTransExecute(int *result, int *error, int *maxfds, fd_set *readfds, + fd_set *writefds, struct timeval *timeout); + +/* + * Run an empty loop, giving to the NX transport a chance to check + * its descriptors. + */ + +extern int NXTransContinue(struct timeval *timeout); + +/* + * Perform I/O on the given descriptors. If memory-to-memory trans- + * port has been activated and the descriptor is recognized as a + * valid agent connection, then the functions will read and write + * the data directly to the proxy buffer, otherwise the correspond- + * ing network operation will be performed. + */ + +extern int NXTransRead(int fd, char *data, int size); +extern int NXTransWrite(int fd, char *data, int size); +extern int NXTransReadable(int fd, int *readable); + +extern int NXTransReadVector(int fd, struct iovec *iovdata, int iovsize); +extern int NXTransWriteVector(int fd, struct iovec *iovdata, int iovsize); + +extern int NXTransClose(int fd); + +/* + * Return true if the NX transport is running. The fd parameter can + * be either the local descriptor attached to the NX transport or + * NX_FD_ANY. + */ + +extern int NXTransRunning(int fd); + +/* + * Close down the NX transport and free all the allocated resources. + * The fd parameter can be either the local descriptor or NX_FD_ANY. + * This must be explicitly called by the agent before the proxy can + * start the tear down procedure. + */ + +extern int NXTransDestroy(int fd); + +/* + * Tell to the proxy how to handle the standard POSIX signals. For + * example, given the SIGINT signal, the caller can specify any of + * the following actions: + * + * NX_SIGNAL_ENABLE: A signal handler will have to be installed by + * the library, so that it can be intercepted by + * the proxy. + * + * NX_SIGNAL_DISABLE: The signal will be handled by the caller and, + * eventually, forwarded to the proxy by calling + * NXTransSignal() explicitly. + * + * NX_SIGNAL_RAISE: The signal must be handled now, as if it had + * been delivered by the operating system. This + * function can be called by the agent with the + * purpose of propagating a signal to the proxy. + * + * NX_SIGNAL_FORWARD: A signal handler will have to be installed by + * the library but the library will have to call + * the original signal handler when the signal + * is received. + * + * As a rule of thumb, agents should let the proxy handle SIGUSR1 + * and SIGUSR2, used for producing the NX protocol statistics, and + * SIGHUP, used for disconnecting the NX transport. + * + * The following signals are blocked by default upon creation of the + * NX transport: + * + * SIGCHLD These signals should be always put under the control + * SIGUSR1 of the proxy. If agents are intercepting them, agents + * SIGUSR2 should later call NXTransSignal(..., NX_SIGNAL_RAISE) + * SIGHUP to forward the signal to the proxy. As an alternative + * they can specify a NX_SIGNAL_FORWARD action, so they, + * in turn, can be notified about the signal. This can + * be especially useful for SIGCHLD. + * + * SIGINT These signals should be intercepted by agents. Agents + * SIGTERM should ensure that NXTransDestroy() is called before + * exiting, to give the proxy a chance to shut down the + * NX transport. + * + * SIGPIPE This signal is blocked by the proxy, but not used to + * implement any functionality. It can be handled by the + * NX agent without affecting the proxy. + * + * SIGALRM This is now used by the proxy and agents should not + * redefine it. Agents can use the signal to implement + * their own timers but should not interleave calls to + * the NX transport and should restore the old handler + * when the timeout is raised. + * + * SIGVTALRM These signals are not used but may be used in future + * SIGWINCH versions of the library. + * SIGIO + * SIGTSTP + * SIGTTIN + * SIGTTOU + * + * By calling NXTransSignal(..., NX_SIGNAL_DISABLE) nxcomp will res- + * tore the signal handler that was saved at the time the proxy hand- + * ler was installed. This means that you should call the function + * just after the XOpenDisplay() or any other function used to init- + * ialize the NX transport. + */ + +extern int NXTransSignal(int signal, int action); + +/* + * Return a value between 0 and 9 indicating the congestion level + * based on the tokens still available. A value of 9 means that + * the link is congested and no further data can be sent. + */ + +extern int NXTransCongestion(int fd); + +/* + * Let the application to be notified by the proxy when an event oc- + * curs. The parameter, as set at the time the handler is installed, + * is passed each time to the callback function. The parameter is + * presumably the display pointer, given that at the present moment + * the NX transport doesn't have access to the display structure and + * so wouldn't be able to determine the display to pass to the call- + * back function. + * + * NX_HANDLER_FLUSH: The handler function is called when some + * more data has been written to the proxy + * link. + * + * The data is the number of bytes written. + * + * NX_HANDLER_STATISTICS: This handler is called to let the agent + * include arbitrary data in the transport + * statistics. The parameter, in this case, + * is a pointer to a pointer to a null term- + * inated string. The pointer is set at the + * time the handler is registered. The point- + * ed string will have to be filled by the + * agent with its statistics data. + * + * The data can be NX_STATISTICS_PARTIAL or NX_STATISTICS_TOTAL. The + * agent can refer to the value by using the NXStatisticsPartial and + * NXStatisticsTotal constants defined in NXvars.h. + * + * Note that these interfaces are used by Xlib and nxcompext. Agents + * should never call these interfaces directly, but use the nxcompext + * wrapper. + */ + +extern int NXTransHandler(int fd, int type, void (*handler)(void *parameter, + int reason), void *parameter); + +/* + * Set the policy to be used by the NX transport to write data to the + * proxy link: + * + * NX_POLICY_IMMEDIATE: When set to immediate, the proxy will try to + * write the data just after having encoded it. + * + * NX_POLICY_DEFERRED: When policy is set to deferred, data will be + * accumulated in a buffer and written to the + * remote proxy when NXTransFlush() is called by + * the agent. + */ + +extern int NXTransPolicy(int fd, int type); + +/* + * Query the number of bytes that have been accumulated for a deferred + * flush. + */ + +extern int NXTransFlushable(int fd); + +/* + * Tell to the NX transport to write all the accumulated data to the + * remote proxy. + */ + +extern int NXTransFlush(int fd); + +/* + * Create a new channel of the given type. It returns 1 on success, + * 0 if the NX transport is not running, or -1 in the case of error. + * On success, the descriptor provided by the caller can be later + * used for the subsequent I/O. The type parameter not only tells to + * the proxy the remote port where the channel has to be connected, + * but also gives a hint about the type of data that will be carried + * by the channel, so that the proxy can try to optimize the traffic + * on the proxy link. + * + * NX_CHANNEL_X: The channel will carry X traffic and it + * will be connected to the remote X display. + * + * NX_CHANNEL_CUPS: The channel will carry CUPS/IPP protocol. + * + * NX_CHANNEL_SMB: The channel will carry SMB/CIFS protocol. + * + * NX_CHANNEL_MEDIA: The channel will transport audio or other + * multimedia data. + * + * NX_CHANNEL_HTTP: The channel will carry HTTP protocol. + * + * NX_CHANNEL_FONT: The channel will forward a X font server + * connection. + * + * Only a proxy running at the NX server/X client side will be able + * to create a X, CUPS, SMB, MEDIA and HTTP channel. A proxy running + * at the NX client/X server side can create font server connections. + * The channel creation will also fail if the remote end has not been + * set up to forward the connection. + * + * To create a new channel the agent will have to set up a socketpair + * and pass to the proxy one of the socket descriptors. + * + * Example: + * + * #include <sys/types.h> + * #include <sys/socket.h> + * + * int fds[2]; + * + * if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) + * { + * ... + * } + * else + * { + * // + * // Use fds[0] locally and let the + * // proxy use fds[1]. + * // + * + * if (NXTransChannel(NX_FD_ANY, fds[1], NX_CHANNEL_X) <= 0) + * { + * ... + * } + * + * // + * // The agent can now use fds[0] in + * // read(), write() and select() + * // system calls. + * // + * + * ... + * } + * + * Note that all the I/O on the descriptor should be non-blocking, to + * give a chance to the NX transport to run in the background and handle + * the data that will be fed to the agent's side of the socketpair. This + * will happen automatically, as long as the agent uses the XSelect() + * version of the select() function (as it is normal whenever performing + * Xlib I/O). In all the other cases, like presumably in the agent's main + * loop, the agent will have to loop through NXTransPrepare(), NXTrans- + * Select() and NXTransExecute() functions explicitly, adding to the sets + * the descriptors that are awaited by the agent. Please check the imple- + * mentation of _XSelect() in nx-X11/lib/X11/XlibInt.c for an example. + */ + +extern int NXTransChannel(int fd, int channelfd, int type); + +/* + * Return the name of the files used by the proxy for the current session. + * + * The type parameter can be: + * + * NX_FILE_SESSION: Usually the file 'session' in the user's session + * directory. + * + * NX_FILE_ERRORS: The file used for the diagnostic output. Usually + * the file 'errors' in the session directory. + * + * NX_FILE_OPTIONS: The file containing the NX options, if any. + * + * NX_FILE_STATS: The file used for the statistics output. + * + * The returned string is allocated in static memory. The caller should + * copy the string upon returning from the function, without freeing the + * pointer. + */ + +extern const char *NXTransFile(int type); + +/* + * Return the time in milliseconds elapsed since the last call to this + * same function. + */ + +extern long NXTransTime(void); + +/* + * Other interfaces to the internal transport functions. + */ + +extern int NXTransProxy(int fd, int mode, const char *display); + +extern int NXTransClient(const char *display); + +extern int NXTransDialog(const char *caption, const char *message, + const char *window, const char *type, int local, + const char *display); + +extern int NXTransAlert(int code, int local); + +extern int NXTransWatchdog(int timeout); + +extern int NXTransKeeper(int caches, int images, const char *root); + +extern void NXTransExit(int code) __attribute__((noreturn)); + +extern int NXTransParseCommandLine(int argc, const char **argv); +extern int NXTransParseEnvironment(const char *env, int force); + +extern void NXTransCleanup(void) __attribute__((noreturn)); + +#ifdef __cplusplus +} +#endif + +#endif /* NX_H */ diff --git a/nxcomp/NXalert.h b/nxcomp/NXalert.h new file mode 100644 index 000000000..2f01a3071 --- /dev/null +++ b/nxcomp/NXalert.h @@ -0,0 +1,268 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef NXalert_H +#define NXalert_H + +#define ALERT_CAPTION_PREFIX "NX - " + +#define INTERNAL_ERROR_ALERT 1 +#define INTERNAL_ERROR_ALERT_TYPE "error" +#define INTERNAL_ERROR_ALERT_STRING \ +"\ +An unrecoverable internal error was detected.\n\ +Press OK to terminate the current session.\n\ +" + +#define CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT 2 +#define CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_TYPE "yesno" +#define CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_STRING \ +"\ +One of the applications currently in use is not responding.\n\ +Do you want to terminate the current session?\n\ +" + +#define CLOSE_DEAD_X_CONNECTION_SERVER_ALERT 3 +#define CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_TYPE "yesno" +#define CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_STRING \ +"\ +One of the applications did not behave correctly and caused\n\ +the X server to stop responding in a timely fashion. Do you\n\ +want to terminate the current session?\n\ +" + +#define CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT 4 +#define CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT_TYPE NULL +#define CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT_STRING NULL + +#define CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT 5 +#define CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE "yesno" +#define CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING \ +"\ +No response received from the remote server.\n\ +Do you want to terminate the current session?\n\ +" + +#define RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT 6 +#define RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT_TYPE NULL +#define RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT_STRING NULL + +#define RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT 7 +#define RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE "yesno" +#define RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING \ +"\ +Connection with remote server was shut down. NX will try\n\ +to establish a new server connection. Session could have\n\ +been left in a unusable state. Do you want to terminate\n\ +the session?\n\ +" + +#define CLOSE_UNRESPONSIVE_X_SERVER_ALERT 8 +#define CLOSE_UNRESPONSIVE_X_SERVER_ALERT_TYPE "panic" +#define CLOSE_UNRESPONSIVE_X_SERVER_ALERT_STRING \ +"\ +You pressed the key sequence CTRL+ALT+SHIFT+ESC.\n\ +This is probably because your X server has become\n\ +unresponsive. Session will be terminated in 30\n\ +seconds unless you abort the procedure by pressing\n\ +the Cancel button.\n\ +" + +#define WRONG_PROXY_VERSION_ALERT 9 +#define WRONG_PROXY_VERSION_ALERT_TYPE "ok" +#define WRONG_PROXY_VERSION_ALERT_STRING \ +"\ +Local NX libraries version " VERSION " do not match the NX\n\ +version of the remote server. Please check the error\n\ +log on the server to find out which client version you\n\ +need to install to be able to access this server.\n\ +" + +#define FAILED_PROXY_CONNECTION_CLIENT_ALERT 10 +#define FAILED_PROXY_CONNECTION_CLIENT_ALERT_TYPE NULL +#define FAILED_PROXY_CONNECTION_CLIENT_ALERT_STRING NULL + +#define FAILED_PROXY_CONNECTION_SERVER_ALERT 11 +#define FAILED_PROXY_CONNECTION_SERVER_ALERT_TYPE "yesno" +#define FAILED_PROXY_CONNECTION_SERVER_ALERT_STRING \ +"\ +Could not yet establish the connection to the remote\n\ +proxy. Do you want to terminate the current session?\n\ +" + +#define MISSING_PROXY_CACHE_ALERT 12 +#define MISSING_PROXY_CACHE_ALERT_TYPE "ok" +#define MISSING_PROXY_CACHE_ALERT_STRING \ +"\ +NX was unable to negotiate a cache for this session.\n\ +This may happen if this is the first time you run a\n\ +session on this server or if cache was corrupted or\n\ +produced by an incompatible NX version.\n\ +" + +#define ABORT_PROXY_CONNECTION_ALERT 13 +#define ABORT_PROXY_CONNECTION_ALERT_TYPE "ok" +#define ABORT_PROXY_CONNECTION_ALERT_STRING \ +"\ +The connection with the remote server was shut down.\n\ +Please check the state of your network connection.\n\ +" + +/* + * The one below is a special alert, used to close + * a previous alert that is running on the given + * side. This can be used to get rid of a message + * that has ceased to hold true. + */ + +#define DISPLACE_MESSAGE_ALERT 14 +#define DISPLACE_MESSAGE_ALERT_TYPE NULL +#define DISPLACE_MESSAGE_ALERT_STRING NULL + +/* + * These are the other alert messages that were + * added in the 1.5.0 release. The first is never + * shown and is intended just for testing. + */ + +#define GREETING_MESSAGE_ALERT 15 +#define GREETING_MESSAGE_ALERT_TYPE "ok" +#define GREETING_MESSAGE_ALERT_STRING \ +"\ +Welcome to NX from the NoMachine team. We really\n\ +hope you will enjoy this wonderful software as much\n\ +as we had fun making it ;-).\n\ +" + +/* + * These alerts are intended to notify the user + * of the reason why the agent failed to resume + * the session. + */ + +#define START_RESUME_SESSION_ALERT 16 +#define START_RESUME_SESSION_ALERT_TYPE "ok" +#define START_RESUME_SESSION_ALERT_STRING \ +"\ +You appear to run your NX session across a slow network\n\ +connection. Resuming the session may require some time.\n\ +Please wait.\ +" + +#define FAILED_RESUME_DISPLAY_ALERT 17 +#define FAILED_RESUME_DISPLAY_ALERT_TYPE "error" +#define FAILED_RESUME_DISPLAY_ALERT_STRING \ +"\ +Failed to open the display. Can't resume the NX\n\ +session on this display.\n\ +" + +#define FAILED_RESUME_DISPLAY_BROKEN_ALERT 18 +#define FAILED_RESUME_DISPLAY_BROKEN_TYPE "error" +#define FAILED_RESUME_DISPLAY_BROKEN_STRING \ +"\ +The display connection was broken while trying to\n\ +resume the session. Please, check your network\n\ +connection and try again.\n\ +" + +#define FAILED_RESUME_VISUALS_ALERT 19 +#define FAILED_RESUME_VISUALS_ALERT_TYPE "error" +#define FAILED_RESUME_VISUALS_ALERT_STRING \ +"\ +Failed to restore all the required visuals.\n\ +Can't resume the NX session on this display.\n\ +" + +#define FAILED_RESUME_COLORMAPS_ALERT 20 +#define FAILED_RESUME_COLORMAPS_ALERT_TYPE "error" +#define FAILED_RESUME_COLORMAPS_ALERT_STRING \ +"\ +The number of available colormaps is different\n\ +on the new display. Can't resume the NX session\n\ +on this display.\n\ +" + +#define FAILED_RESUME_PIXMAPS_ALERT 21 +#define FAILED_RESUME_PIXMAPS_ALERT_TYPE "error" +#define FAILED_RESUME_PIXMAPS_ALERT_STRING \ +"\ +Failed to restore all the required pixmap formats.\n\ +Can't resume the NX session on this display.\n\ +" + +#define FAILED_RESUME_DEPTHS_ALERT 22 +#define FAILED_RESUME_DEPTHS_ALERT_TYPE "error" +#define FAILED_RESUME_DEPTHS_ALERT_STRING \ +"\ +Failed to restore all the required screen depths.\n\ +Can't resume the NX session on this display.\n\ +" + +#define FAILED_RESUME_RENDER_ALERT 23 +#define FAILED_RESUME_RENDER_ALERT_TYPE "error" +#define FAILED_RESUME_RENDER_ALERT_STRING \ +"\ +The render extension is missing or an incompatible\n\ +version was detected on your X server. Can't resume\n\ +the NX session on this display.\n\ +" + +#define FAILED_RESUME_FONTS_ALERT 24 +#define FAILED_RESUME_FONTS_ALERT_TYPE "error" +#define FAILED_RESUME_FONTS_ALERT_STRING \ +"\ +One or more of the fonts that are in use by the\n\ +session are missing. Can't resume the NX session\n\ +on this display.\n\ +" + +#define ABORT_PROXY_NEGOTIATION_ALERT 62 +#define ABORT_PROXY_NEGOTIATION_ALERT_TYPE "ok" +#define ABORT_PROXY_NEGOTIATION_ALERT_STRING \ +"\ +The remote proxy closed the connection while negotiating\n\ +the session. This may be due to the wrong authentication\n\ +credentials passed to the server.\n\ +" + +#define ABORT_PROXY_SHUTDOWN_ALERT 64 +#define ABORT_PROXY_SHUTDOWN_ALERT_TYPE "ok" +#define ABORT_PROXY_SHUTDOWN_ALERT_STRING \ +"\ +No response received from the remote proxy while\n\ +waiting for the session shutdown.\n\ +" + +#define FAILED_XDMCP_CONNECTION_ALERT 65 +#define FAILED_XDMCP_CONNECTION_ALERT_TYPE "ok" +#define FAILED_XDMCP_CONNECTION_ALERT_STRING \ +"\ +The XDM host that was contacted by the NX server doesn't\n\ +seem to be able to start the session. Please check your\n\ +server configuration.\n\ +" + +/* + * Used to handle the backward compatibility. + * Update the numbers if you add a new alert. + */ + +#define LAST_PROTO_STEP_6_ALERT 63 +#define LAST_PROTO_STEP_7_ALERT 65 + +#endif /* NXalert_H */ diff --git a/nxcomp/NXmitshm.h b/nxcomp/NXmitshm.h new file mode 100644 index 000000000..aa79ec8ae --- /dev/null +++ b/nxcomp/NXmitshm.h @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef NXmitshm_H +#define NXmitshm_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Import opcodes from <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/NXpack.h b/nxcomp/NXpack.h new file mode 100644 index 000000000..de1c0f0d9 --- /dev/null +++ b/nxcomp/NXpack.h @@ -0,0 +1,133 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef NXpack_H +#define NXpack_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MASK_METHOD_LIMIT 10 + +#define NO_MASK 0 + +#define MASK_8_COLORS 1 +#define MASK_64_COLORS 2 +#define MASK_256_COLORS 3 +#define MASK_512_COLORS 4 +#define MASK_4K_COLORS 5 +#define MASK_32K_COLORS 6 +#define MASK_64K_COLORS 7 +#define MASK_256K_COLORS 8 +#define MASK_2M_COLORS 9 +#define MASK_16M_COLORS 10 + +#define PACK_METHOD_LIMIT 128 + +#define NO_PACK 0 + +#define PACK_MASKED_8_COLORS 1 +#define PACK_MASKED_64_COLORS 2 +#define PACK_MASKED_256_COLORS 3 +#define PACK_MASKED_512_COLORS 4 +#define PACK_MASKED_4K_COLORS 5 +#define PACK_MASKED_32K_COLORS 6 +#define PACK_MASKED_64K_COLORS 7 +#define PACK_MASKED_256K_COLORS 8 +#define PACK_MASKED_2M_COLORS 9 +#define PACK_MASKED_16M_COLORS 10 + +#define PACK_RAW_8_BITS 3 +#define PACK_RAW_16_BITS 7 +#define PACK_RAW_24_BITS 10 + +#define PACK_COLORMAP_256_COLORS 11 + +#define PACK_JPEG_8_COLORS 26 +#define PACK_JPEG_64_COLORS 27 +#define PACK_JPEG_256_COLORS 28 +#define PACK_JPEG_512_COLORS 29 +#define PACK_JPEG_4K_COLORS 30 +#define PACK_JPEG_32K_COLORS 31 +#define PACK_JPEG_64K_COLORS 32 +#define PACK_JPEG_256K_COLORS 33 +#define PACK_JPEG_2M_COLORS 34 +#define PACK_JPEG_16M_COLORS 35 + +#define PACK_PNG_8_COLORS 37 +#define PACK_PNG_64_COLORS 38 +#define PACK_PNG_256_COLORS 39 +#define PACK_PNG_512_COLORS 40 +#define PACK_PNG_4K_COLORS 41 +#define PACK_PNG_32K_COLORS 42 +#define PACK_PNG_64K_COLORS 43 +#define PACK_PNG_256K_COLORS 44 +#define PACK_PNG_2M_COLORS 45 +#define PACK_PNG_16M_COLORS 46 + +#define PACK_RGB_16M_COLORS 63 +#define PACK_RLE_16M_COLORS 64 + +#define PACK_ALPHA 65 +#define PACK_COLORMAP 66 + +#define PACK_BITMAP_16M_COLORS 67 + +/* + * Not really pack methods. These values + * allow dynamic selection of the pack + * method by the agent. + */ + +#define PACK_NONE 0 +#define PACK_LOSSY 253 +#define PACK_LOSSLESS 254 +#define PACK_ADAPTIVE 255 + +/* + * Reduce the number of colors in the + * image by applying a mask. + */ + +typedef struct +{ + unsigned int color_mask; + unsigned int correction_mask; + unsigned int white_threshold; + unsigned int black_threshold; + +} ColorMask; + +extern const ColorMask Mask8TrueColor; +extern const ColorMask Mask64TrueColor; +extern const ColorMask Mask512TrueColor; +extern const ColorMask Mask4KTrueColor; +extern const ColorMask Mask32KTrueColor; +extern const ColorMask Mask256KTrueColor; +extern const ColorMask Mask2MTrueColor; +extern const ColorMask Mask16MTrueColor; + +const ColorMask *MethodColorMask(unsigned int method); + +int MethodBitsPerPixel(unsigned int method); + +#ifdef __cplusplus +} +#endif + +#endif /* NXpack_H */ diff --git a/nxcomp/NXproto.h b/nxcomp/NXproto.h new file mode 100644 index 000000000..ce25cc4c7 --- /dev/null +++ b/nxcomp/NXproto.h @@ -0,0 +1,439 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef NXproto_H +#define NXproto_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <X11/X.h> +#include <X11/Xmd.h> +#include <X11/Xproto.h> + +/* + * Force the size to match the wire protocol. + */ + +#define Drawable CARD32 +#define GContext CARD32 + +#define sz_xNXGetControlParametersReq 4 +#define sz_xNXGetCleanupParametersReq 4 +#define sz_xNXGetImageParametersReq 4 +#define sz_xNXGetUnpackParametersReq 8 +#define sz_xNXGetShmemParametersReq 16 +#define sz_xNXGetFontParametersReq 4 +#define sz_xNXSetExposeParametersReq 8 +#define sz_xNXSetCacheParametersReq 8 +#define sz_xNXStartSplitReq 8 +#define sz_xNXEndSplitReq 4 +#define sz_xNXCommitSplitReq 12 +#define sz_xNXSetUnpackGeometryReq 24 +#define sz_xNXSetUnpackColormapReq 16 +#define sz_xNXSetUnpackAlphaReq 16 +#define sz_xNXPutPackedImageReq 40 +#define sz_xNXFreeUnpackReq 4 +#define sz_xNXFinishSplitReq 4 +#define sz_xNXAbortSplitReq 4 +#define sz_xNXFreeSplitReq 4 + +#define sz_xGetControlParametersReply 32 +#define sz_xGetCleanupParametersReply 32 +#define sz_xGetImageParametersReply 32 +#define sz_xGetUnpackParametersReply 32 +#define sz_xGetShmemParametersReply 32 + +#define LINK_TYPE_LIMIT 5 + +#define LINK_TYPE_NONE 0 +#define LINK_TYPE_MODEM 1 +#define LINK_TYPE_ISDN 2 +#define LINK_TYPE_ADSL 3 +#define LINK_TYPE_WAN 4 +#define LINK_TYPE_LAN 5 + +/* + * NX Replies. + */ + +/* + * The following reply has 4 new boolean + * fields in the last protocol version. + */ + +typedef struct _NXGetControlParametersReply { + BYTE type; /* Is X_Reply. */ + CARD8 linkType; + CARD16 sequenceNumber B16; + CARD32 length B32; /* Is 0. */ + CARD8 localMajor; + CARD8 localMinor; + CARD8 localPatch; + CARD8 remoteMajor; + CARD8 remoteMinor; + CARD8 remotePatch; + CARD16 splitTimeout B16; + CARD16 motionTimeout B16; + CARD8 splitMode; + CARD8 pad1; + CARD32 splitSize B32; + CARD8 packMethod; + CARD8 packQuality; + CARD8 dataLevel; + CARD8 streamLevel; + CARD8 deltaLevel; + CARD8 loadCache; + CARD8 saveCache; + CARD8 startupCache; +} xNXGetControlParametersReply; + +typedef struct _NXGetCleanupParametersReply { + BYTE type; /* Is X_Reply. */ + BYTE pad; + CARD16 sequenceNumber B16; + CARD32 length B32; /* Is 0. */ + BOOL cleanGet; + BOOL cleanAlloc; + BOOL cleanFlush; + BOOL cleanSend; + BOOL cleanImages; + BYTE pad1, pad2, pad3; + CARD32 pad4 B32; + CARD32 pad5 B32; + CARD32 pad6 B32; + CARD32 pad7 B32; +} xNXGetCleanupParametersReply; + +typedef struct _NXGetImageParametersReply { + BYTE type; /* Is X_Reply. */ + BYTE pad; + CARD16 sequenceNumber B16; + CARD32 length B32; /* Is 0. */ + BOOL imageSplit; + BOOL imageMask; + BOOL imageFrame; + CARD8 imageMaskMethod; + CARD8 imageSplitMethod; + BYTE pad1, pad2, pad3; + CARD32 pad4 B32; + CARD32 pad5 B32; + CARD32 pad6 B32; + CARD32 pad7 B32; +} xNXGetImageParametersReply; + +/* + * Data is made of PACK_METHOD_LIMIT values of + * type BOOL telling which unpack capabilities + * are implemented in proxy. + */ + +typedef struct _NXGetUnpackParametersReply { + BYTE type; /* Is X_Reply. */ + BYTE pad; + CARD16 sequenceNumber B16; + CARD32 length B32; /* Is PACK_METHOD_LIMIT / 4 from NXpack.h. */ + CARD8 entries; /* Is PACK_METHOD_LIMIT. */ + BYTE pad1, pad2, pad3; + CARD32 pad4 B32; + CARD32 pad5 B32; + CARD32 pad6 B32; + CARD32 pad7 B32; + CARD32 pad8 B32; +} xNXGetUnpackParametersReply; + +typedef struct _NXGetShmemParametersReply { + BYTE type; /* Is X_Reply. */ + CARD8 stage; /* As in the corresponding request. */ + CARD16 sequenceNumber B16; + CARD32 length B32; /* Is 0. */ + BOOL clientEnabled; /* SHM on path agent to proxy. */ + BOOL serverEnabled; /* SHM on path proxy to X server. */ + BYTE pad1, pad2; /* Previous values can be checked */ + CARD32 clientSize B32; /* at end of stage 2. */ + CARD32 serverSize B32; + CARD32 pad3 B32; + CARD32 pad4 B32; + CARD32 pad5 B32; +} xNXGetShmemParametersReply; + +typedef struct _NXGetFontParametersReply { + BYTE type; /* Is X_Reply. */ + BYTE pad1; + CARD16 sequenceNumber B16; + CARD32 length B32; /* Is length of path string + 1 / 4. */ + CARD32 pad2 B32; + CARD32 pad3 B32; + CARD32 pad4 B32; + CARD32 pad5 B32; + CARD32 pad6 B32; + CARD32 pad7 B32; +} xNXGetFontParametersReply; + +/* + * NX Requests. + */ + +typedef struct _NXGetControlParametersReq { + CARD8 reqType; + BYTE pad; + CARD16 length B16; +} xNXGetControlParametersReq; + +typedef struct _NXGetCleanupParametersReq { + CARD8 reqType; + BYTE pad; + CARD16 length B16; +} xNXGetCleanupParametersReq; + +typedef struct _NXGetImageParametersReq { + CARD8 reqType; + BYTE pad; + CARD16 length B16; +} xNXGetImageParametersReq; + +typedef struct _NXGetUnpackParametersReq { + CARD8 reqType; + BYTE pad; + CARD16 length B16; + CARD8 entries; + BYTE pad1, pad2, pad3; +} xNXGetUnpackParametersReq; + +typedef struct _NXGetShmemParametersReq { + CARD8 reqType; + CARD8 stage; /* It is between 0 and 2. */ + CARD16 length B16; + BOOL enableClient; /* X client side support is */ + BOOL enableServer; /* not implemented yet. */ + BYTE pad1, pad2; + CARD32 clientSegment; /* XID identifying the shared */ + CARD32 serverSegment; /* memory segments. */ +} xNXGetShmemParametersReq; + +typedef struct _NXGetFontParametersReq { + CARD8 reqType; + CARD8 pad; + CARD16 length B16; +} xNXGetFontParametersReq; + +/* + * The available split modes. + */ + +#define NXSplitModeDefault 0 +#define NXSplitModeAsync 1 +#define NXSplitModeSync 2 + +typedef struct _NXStartSplitReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; + CARD8 mode; + BYTE pad1, pad2, pad3; +} xNXStartSplitReq; + +typedef struct _NXEndSplitReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; +} xNXEndSplitReq; + +typedef struct _NXCommitSplitReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; + CARD8 propagate; + CARD8 request; + BYTE pad1, pad2; + CARD32 position B32; +} xNXCommitSplitReq; + +typedef struct _NXFinishSplitReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; +} xNXFinishSplitReq; + +typedef struct _NXAbortSplitReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; +} xNXAbortSplitReq; + +typedef struct _NXFreeSplitReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; +} xNXFreeSplitReq; + +typedef struct _NXSetExposeParametersReq { + CARD8 reqType; + BYTE pad; + CARD16 length B16; + BOOL expose; + BOOL graphicsExpose; + BOOL noExpose; + BYTE pad1; +} xNXSetExposeParametersReq; + +typedef struct _NXSetCacheParametersReq { + CARD8 reqType; + BYTE pad; + CARD16 length B16; + BOOL enableCache; + BOOL enableSplit; + BOOL enableSave; + BOOL enableLoad; +} xNXSetCacheParametersReq; + +typedef struct _NXSetUnpackGeometryReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; + CARD8 depth1Bpp; + CARD8 depth4Bpp; + CARD8 depth8Bpp; + CARD8 depth16Bpp; + CARD8 depth24Bpp; + CARD8 depth32Bpp; + BYTE pad1, pad2; + CARD32 redMask B32; + CARD32 greenMask B32; + CARD32 blueMask B32; +} xNXSetUnpackGeometryReq; + +typedef struct _NXSetUnpackColormapReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; + CARD8 method; + BYTE pad1, pad2, pad3; + CARD32 srcLength B32; + CARD32 dstLength B32; +} xNXSetUnpackColormapReq; + +typedef struct _NXSetUnpackAlphaReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; + CARD8 method; + BYTE pad1, pad2, pad3; + CARD32 srcLength B32; + CARD32 dstLength B32; +} xNXSetUnpackAlphaReq; + +typedef struct _NXPutPackedImageReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; + Drawable drawable B32; + GContext gc B32; + CARD8 method; + CARD8 format; + CARD8 srcDepth; + CARD8 dstDepth; + CARD32 srcLength B32; + CARD32 dstLength B32; + INT16 srcX B16, srcY B16; + CARD16 srcWidth B16, srcHeight B16; + INT16 dstX B16, dstY B16; + CARD16 dstWidth B16, dstHeight B16; +} xNXPutPackedImageReq; + +typedef struct _NXFreeUnpackReq { + CARD8 reqType; + CARD8 resource; + CARD16 length B16; +} xNXFreeUnpackReq; + +/* + * The X_NXSplitData and X_NXSplitEvent opcodes + * are used internally and are ignored if coming + * from the agent. + */ + +#define X_NXInternalGenericData 0 +#define X_NXInternalGenericReply 1 +#define X_NXInternalGenericRequest 255 + +#define X_NXInternalShapeExtension 128 +#define X_NXInternalRenderExtension 129 + +#define X_NXFirstOpcode 230 +#define X_NXLastOpcode 252 + +#define X_NXGetControlParameters 230 +#define X_NXGetCleanupParameters 231 +#define X_NXGetImageParameters 232 +#define X_NXGetUnpackParameters 233 +#define X_NXStartSplit 234 +#define X_NXEndSplit 235 +#define X_NXSplitData 236 +#define X_NXCommitSplit 237 +#define X_NXSetExposeParameters 240 +#define X_NXSetUnpackGeometry 241 +#define X_NXSetUnpackColormap 242 +#define X_NXPutPackedImage 243 +#define X_NXSplitEvent 244 +#define X_NXGetShmemParameters 245 +#define X_NXSetUnpackAlpha 246 +#define X_NXFreeUnpack 247 +#define X_NXFinishSplit 248 +#define X_NXAbortSplit 249 +#define X_NXFreeSplit 250 +#define X_NXGetFontParameters 251 +#define X_NXSetCacheParameters 252 + +/* + * The following events are received by the agent + * in the form of a ClientMessage with the value + * 0 in fields atom and window. The format is + * always 32. Event specific data starts at byte + * offset 12. + * + * These events are sent by the NX transport to + * notify the agent about the result of a split + * operation. + */ + +#define NXNoSplitNotify 1 +#define NXStartSplitNotify 2 +#define NXCommitSplitNotify 3 +#define NXEndSplitNotify 4 +#define NXEmptySplitNotify 5 + +/* + * Notifications of collect events. These events + * don't come from the NX transport but are put + * back in client's event queue by NXlib. + */ + +#define NXCollectImageNotify 8 +#define NXCollectPropertyNotify 9 +#define NXCollectGrabPointerNotify 10 +#define NXCollectInputFocusNotify 11 + +#undef Drawable +#undef GContext + +#ifdef __cplusplus +} +#endif + +#endif /* NXproto_H */ diff --git a/nxcomp/NXrender.h b/nxcomp/NXrender.h new file mode 100644 index 000000000..bc359f273 --- /dev/null +++ b/nxcomp/NXrender.h @@ -0,0 +1,70 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef NXrender_H +#define NXrender_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Import this from <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/NXvars.h b/nxcomp/NXvars.h new file mode 100644 index 000000000..3bee6d3ed --- /dev/null +++ b/nxcomp/NXvars.h @@ -0,0 +1,193 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef NXvars_H +#define NXvars_H + +/* + * This can be included by the proxy or another + * layer that doesn't use Xlib. + */ + +#if !defined(_XLIB_H_) && !defined(_XKBSRV_H_) + +#define NeedFunctionPrototypes 1 + +#define Display void + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Display flush policies. + */ + +#define NXPolicyImmediate 1 +#define NXPolicyDeferred 2 + +/* + * Type of flush. + */ + +#define NXFlushBuffer 0 +#define NXFlushLink 1 + +/* + * Type of statistics. + */ + +#define NXStatisticsPartial 0 +#define NXStatisticsTotal 1 + +/* + * Reason why the display is blocking. + */ + +#define NXBlockRead 1 +#define NXBlockWrite 2 + +/* + * Set if the client is interested in ignoring + * the display error and continue with the exe- + * cution of the program. By default the usual + * Xlib behaviour is gotten, and the library + * will call an exit(). + */ + +extern int _NXHandleDisplayError; + +/* + * The function below is called whenever Xlib is + * going to perform an I/O operation. The funct- + * ion can be redefined to include additional + * checks aimed at detecting if the display needs + * to be closed, for example because of an event + * or a signal mandating the end of the session. + * In this way the client program can regain the + * control before Xlib blocks waiting for input + * from the network. + */ + +typedef int (*NXDisplayErrorPredicate)( +#if NeedFunctionPrototypes + Display* /* display */, + int /* reason */ +#endif +); + +extern NXDisplayErrorPredicate _NXDisplayErrorFunction; + +/* + * This is called when Xlib is going to block + * waiting for the display to become readable or + * writable. The client can use the hook to run + * any arbitrary operation that may require some + * time to complete. The user should not try to + * read or write to the display inside the call- + * back routine. + */ + +typedef void (*NXDisplayBlockHandler)( +#if NeedFunctionPrototypes + Display* /* display */, + int /* reason */ +#endif +); + +extern NXDisplayBlockHandler _NXDisplayBlockFunction; + +/* + * Used to notify the program when more data + * is written to the socket. + */ + +typedef void (*NXDisplayWriteHandler)( +#if NeedFunctionPrototypes + Display* /* display */, + int /* length */ +#endif +); + +extern NXDisplayWriteHandler _NXDisplayWriteFunction; + +/* + * This callback is used to notify the agent + * that the proxy link has been flushed. + */ + +typedef void (*NXDisplayFlushHandler)( +#if NeedFunctionPrototypes + Display* /* display */, + int /* length */ +#endif +); + +extern NXDisplayFlushHandler _NXDisplayFlushFunction; + +/* + * Used by the NX transport to get an arbitrary + * string to add to its protocol statistics. + */ + +typedef void (*NXDisplayStatisticsHandler)( +#if NeedFunctionPrototypes + Display* /* display */, + char* /* buffer */, + int /* size */ +#endif +); + +extern NXDisplayStatisticsHandler _NXDisplayStatisticsFunction; + +/* + * Let users redefine the function printing an + * error message in the case of a out-of-order + * sequence number. + */ + +typedef void (*NXLostSequenceHandler)( +#if NeedFunctionPrototypes + Display* /* display */, + unsigned long /* newseq */, + unsigned long /* lastseq */, + unsigned int /* type */ +#endif +); + +extern NXLostSequenceHandler _NXLostSequenceFunction; + +/* + * Let the X server run the children processes + * (as for example the keyboard initialization + * utilities) by using the native system libra- + * ries, instead of the libraries shipped with + * the NX environment. If set, the Popen() in + * the X server will remove the LD_LIBRARY_PATH + * setting from the environment before calling + * the execl() function in the child process. + */ + +extern int _NXUnsetLibraryPath; + +#ifdef __cplusplus +} +#endif + +#endif /* NXvars_H */ diff --git a/nxcomp/OpcodeCache.h b/nxcomp/OpcodeCache.h new file mode 100644 index 000000000..529f8eee1 --- /dev/null +++ b/nxcomp/OpcodeCache.h @@ -0,0 +1,45 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef OpcodeCache_H +#define OpcodeCache_H + +#include "CharCache.h" + +class OpcodeCache +{ + friend class EncodeBuffer; + friend class DecodeBuffer; + + public: + + OpcodeCache() + { + slot_ = 0; + } + + ~OpcodeCache() + { + } + + private: + + CharCache base_[256]; + unsigned char slot_; +}; + +#endif /* OpcodeCache_H */ diff --git a/nxcomp/OpcodeStore.cpp b/nxcomp/OpcodeStore.cpp new file mode 100644 index 000000000..909744821 --- /dev/null +++ b/nxcomp/OpcodeStore.cpp @@ -0,0 +1,76 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "OpcodeStore.h" + +OpcodeStore::OpcodeStore() +{ + // + // Assign values of the specific + // NX opcodes. + // + + getControlParameters = X_NXGetControlParameters; + getCleanupParameters = X_NXGetCleanupParameters; + getImageParameters = X_NXGetImageParameters; + getUnpackParameters = X_NXGetUnpackParameters; + getShmemParameters = X_NXGetShmemParameters; + getFontParameters = X_NXGetFontParameters; + + startSplit = X_NXStartSplit; + endSplit = X_NXEndSplit; + commitSplit = X_NXCommitSplit; + finishSplit = X_NXFinishSplit; + abortSplit = X_NXAbortSplit; + + splitData = X_NXSplitData; + splitEvent = X_NXSplitEvent; + + setCacheParameters = X_NXSetCacheParameters; + setExposeParameters = X_NXSetExposeParameters; + + setUnpackGeometry = X_NXSetUnpackGeometry; + setUnpackColormap = X_NXSetUnpackColormap; + setUnpackAlpha = X_NXSetUnpackAlpha; + + putPackedImage = X_NXPutPackedImage; + + freeUnpack = X_NXFreeUnpack; + freeSplit = X_NXFreeSplit; + + // + // These values must be fetched + // from the X server. + // + + shapeExtension = 0; + renderExtension = 0; + + // + // Events sent as ClientMessage. + // + + noSplitNotify = NXNoSplitNotify; + startSplitNotify = NXStartSplitNotify; + commitSplitNotify = NXCommitSplitNotify; + endSplitNotify = NXEndSplitNotify; + emptySplitNotify = NXEmptySplitNotify; +} + +OpcodeStore::~OpcodeStore() +{ +} diff --git a/nxcomp/OpcodeStore.h b/nxcomp/OpcodeStore.h new file mode 100644 index 000000000..f6626dfef --- /dev/null +++ b/nxcomp/OpcodeStore.h @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef OpcodeStore_H +#define OpcodeStore_H + +#include "NXproto.h" + +class OpcodeStore +{ + public: + + OpcodeStore(); + + ~OpcodeStore(); + + // + // Map NX protocol messages. At the moment mapping is hard- + // coded. Opcodes should be instead agreed with the proxied + // X server (by excluding all opcodes used for extensions) + // and exported by the proxy class to channels. + // + // Some toolkits query the server only once for extensions' + // opcodes and share the same settings across all channels. + // This could be a problem as channels needed to monitor the + // traffic to find out the extensions' opcodes themselves, + // so it is important that proxy passes an instance of this + // class to new channels. + // + + unsigned char getControlParameters; + unsigned char getCleanupParameters; + unsigned char getImageParameters; + unsigned char getUnpackParameters; + unsigned char getShmemParameters; + unsigned char getFontParameters; + + unsigned char startSplit; + unsigned char endSplit; + unsigned char commitSplit; + unsigned char finishSplit; + unsigned char abortSplit; + + unsigned char splitData; + unsigned char splitEvent; + + unsigned char setCacheParameters; + unsigned char setExposeParameters; + + unsigned char setUnpackGeometry; + unsigned char setUnpackColormap; + unsigned char setUnpackAlpha; + + unsigned char putPackedImage; + + unsigned char freeUnpack; + unsigned char freeSplit; + + unsigned char shapeExtension; + unsigned char renderExtension; + + unsigned char noSplitNotify; + unsigned char startSplitNotify; + unsigned char commitSplitNotify; + unsigned char endSplitNotify; + unsigned char emptySplitNotify; +}; + +#endif /* OpcodeStore_H */ diff --git a/nxcomp/Pack.c b/nxcomp/Pack.c new file mode 100644 index 000000000..59a54cfbf --- /dev/null +++ b/nxcomp/Pack.c @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#ifdef __cplusplus +extern "C" { +#endif + +#include <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/Pgn.cpp b/nxcomp/Pgn.cpp new file mode 100644 index 000000000..a68373441 --- /dev/null +++ b/nxcomp/Pgn.cpp @@ -0,0 +1,794 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// This file obviously supports PNG +// decompression. It was renamed to +// avoid name clashes on Windows. +// + +#include <X11/Xmd.h> + +#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 (infoPtr -> color_type == PNG_COLOR_TYPE_PALETTE) + { + png_set_expand(pngPtr); + } + + // + // data points to dstBuf which is + // already padded correctly for the final + // image to put + // + + data = dstBuf; + rowPointers = (png_byte *) tmpBuf; + + // + // We use setjmp() to save our context. + // The PNG library will call longjmp() + // in case of error. + // + + if (setjmp(png_jmpbuf(pngPtr))) + { + #ifdef PANIC + *logofs << "DecompressPng16: PANIC! " + << "Error during read of PNG rows" + << ".\n" << logofs_flush; + #endif + + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + + return -1; + } + + unsigned long pixel; + + for (dy = 0; dy < h; dy++) + { + png_read_row(pngPtr, rowPointers, NULL); + + for (dx = 0; dx < w; dx++) + { + pixel = RGB24_TO_PIXEL(16, tmpBuf[dx*3], tmpBuf[dx*3+1], tmpBuf[dx*3+2]); + + // + // Follow the server byte order when arranging data. + // + + if (byteOrder == LSBFirst) + { + data[0] = (unsigned char) (pixel & 0xff); + data[1] = (unsigned char) ((pixel >> 8) & 0xff); + } + else + { + data[1] = (unsigned char) (pixel & 0xff); + data[0] = (unsigned char) ((pixel >> 8) & 0xff); + } + + data += 2; + } + + // + // Move pixelPtr at the beginning of the + // next line. + // + + data = data + (RoundUp4(w * 2) - w * 2); + } + + png_destroy_read_struct(&pngPtr, &infoPtr,NULL); + + #ifdef DEBUG + *logofs << "DecompressPng16: Decompression finished." + << dy << " lines handled.\n" + << logofs_flush; + #endif + + return 1; +} + +int DecompressPng24(unsigned char *compressedData, int compressedLen, + unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) +{ + static CARD8 *pixelPtr = NULL; + unsigned int dx, dy; + + png_structp pngPtr; + png_infop infoPtr; + png_bytep rowPointers; + + + streamPos = 0; + + pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!pngPtr) + { + #ifdef PANIC + *logofs << "DecompressPng24: PANIC! " + << "Failed png_create_read_struct operation" + << ".\n" << logofs_flush; + #endif + + return -1; + } + + infoPtr = png_create_info_struct(pngPtr); + + if (!infoPtr) + { + #ifdef PANIC + *logofs << "DecompressPng24: PANIC! " + << "Failed png_create_info_struct operation" + << ".\n" << logofs_flush; + #endif + + png_destroy_read_struct(&pngPtr, NULL, NULL); + + return -1; + } + + if (setjmp(png_jmpbuf(pngPtr))) + { + #ifdef PANIC + *logofs << "DecompressPng24: PANIC! " + << "Error during IO initialization" + << ".\n" << logofs_flush; + #endif + + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + + return -1; + } + + png_set_read_fn(pngPtr, (void *)compressedData, PngReadData); + + if (setjmp(png_jmpbuf(pngPtr))) + { + #ifdef PANIC + *logofs << "DecompressPng24: PANIC! " + << "Error during read of PNG header" + << ".\n" << logofs_flush; + #endif + + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + + return -1; + } + + png_read_info( pngPtr, infoPtr ) ; + + if (infoPtr -> color_type == PNG_COLOR_TYPE_PALETTE) + { + png_set_expand(pngPtr); + } + + // + // PixelPtr points to dstBuf which is + // already padded correctly for the final + // image to put + // + + pixelPtr = (CARD8 *) dstBuf; + + rowPointers = (png_byte *)tmpBuf; + + if (setjmp(png_jmpbuf(pngPtr))) + { + #ifdef PANIC + *logofs << "DecompressPng24: PANIC! " + << "Error during read of PNG rows" + << ".\n" << logofs_flush; + #endif + + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + + return -1; + } + + for (dy = 0; dy < h; dy++) + { + png_read_row(pngPtr, rowPointers, NULL); + + for (dx = 0; dx < w; dx++) + { + // + // Follow the server byte order when arranging data. + // + + if (byteOrder == LSBFirst) + { + pixelPtr[0] = tmpBuf[dx * 3]; + pixelPtr[1] = tmpBuf[dx * 3 + 1]; + pixelPtr[2] = tmpBuf[dx * 3 + 2]; + } + else + { + pixelPtr[2] = tmpBuf[dx * 3]; + pixelPtr[1] = tmpBuf[dx * 3 + 1]; + pixelPtr[0] = tmpBuf[dx * 3 + 2]; + } + + pixelPtr += 3; + } + + // + // Go to the next line. + // + + pixelPtr = (CARD8 *) (((char *) pixelPtr) + (RoundUp4(w * 3) - w * 3)); + } + + png_destroy_read_struct(&pngPtr, &infoPtr,NULL); + + #ifdef DEBUG + *logofs << "DecompressPng24: Decompression finished." + << dy << " lines handled.\n" + << logofs_flush; + #endif + + return 1; +} + +int DecompressPng32(unsigned char *compressedData, int compressedLen, + unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder) +{ + unsigned char *data; + + unsigned int dx, dy; + + png_structp pngPtr; + png_infop infoPtr; + png_bytep rowPointers; + + streamPos = 0; + + pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!pngPtr) + { + #ifdef PANIC + *logofs << "DecompressPng32: PANIC! " + << "Failed png_create_read_struct operation" + << ".\n" << logofs_flush; + #endif + + return -1; + } + + infoPtr = png_create_info_struct(pngPtr); + + if (!infoPtr) + { + #ifdef PANIC + *logofs << "DecompressPng32: PANIC! " + << "Failed png_create_info_struct operation." + << ".\n" << logofs_flush; + #endif + + png_destroy_read_struct(&pngPtr, NULL, NULL); + + return -1; + } + + if (setjmp(png_jmpbuf(pngPtr))) + { + #ifdef PANIC + *logofs << "DecompressPng32: PANIC! " + << "Error during IO initialization" + << ".\n" << logofs_flush; + #endif + + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + + return -1; + } + + png_set_read_fn(pngPtr, (void *)compressedData, PngReadData); + + if (setjmp(png_jmpbuf(pngPtr))) + { + #ifdef PANIC + *logofs << "DecompressPng32: PANIC! " + << "Error during read of PNG header" + << ".\n" << logofs_flush; + #endif + + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + + return -1; + } + + png_read_info(pngPtr, infoPtr) ; + + + if (infoPtr -> color_type == PNG_COLOR_TYPE_PALETTE) + { + png_set_expand(pngPtr); + } + + // + // data points to dstBuf which is + // already padded correctly for the final + // image to put + // + + data = dstBuf; + + rowPointers = (png_byte *) tmpBuf; + + if (setjmp(png_jmpbuf(pngPtr))) + { + #ifdef PANIC + *logofs << "DecompressPng32: PANIC! " + << "Error during read of PNG rows" + << ".\n" << logofs_flush; + #endif + + png_destroy_read_struct(&pngPtr, &infoPtr, NULL); + + return -1; + } + + unsigned long pixel; + + int i; + + for (dy = 0; dy < h; dy++) + { + png_read_row(pngPtr, rowPointers, NULL); + + for (dx = 0; dx < w; dx++) + { + pixel = RGB24_TO_PIXEL(32, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1], + tmpBuf[dx * 3 + 2]); + + // + // Follow the server byte order when arranging data. + // + + if (byteOrder == LSBFirst) + { + for (i = 0; i < 4; i++) + { + data[i] = (unsigned char)(pixel & 0xff); + pixel >>= 8; + } + } + else + { + for (i = 3; i >= 0; i--) + { + data[i] = (unsigned char) (pixel & 0xff); + pixel >>= 8; + } + } + + data += 4; + } + } + + png_destroy_read_struct(&pngPtr, &infoPtr,NULL); + + #ifdef DEBUG + *logofs << "DecompressPng32: Decompression finished." + << dy << " lines handled.\n" + << logofs_flush; + #endif + + return 1; +} + +static void PngReadData(png_structp png_ptr, png_bytep data, png_size_t length) +{ + memcpy((char *) data, (char *) png_get_io_ptr(png_ptr) + streamPos, length); + + streamPos += length; +} diff --git a/nxcomp/Pgn.h b/nxcomp/Pgn.h new file mode 100644 index 000000000..ddf9b75d1 --- /dev/null +++ b/nxcomp/Pgn.h @@ -0,0 +1,34 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Pgn_H +#define Pgn_H + +// +// This file obviously supports PNG +// decompression. It was renamed to +// avoid name clashes on Windows. +// + +#include "Misc.h" +#include "Unpack.h" + +int UnpackPng(T_geometry *geometry, unsigned char method, unsigned char *srcData, + int srcSize, int dstBpp, int dstWidth, int dstHeight, + unsigned char *dstData, int dstSize); + +#endif /* Pgn_H */ diff --git a/nxcomp/Pipe.cpp b/nxcomp/Pipe.cpp new file mode 100644 index 000000000..7238d0c73 --- /dev/null +++ b/nxcomp/Pipe.cpp @@ -0,0 +1,412 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <pwd.h> +#include <sys/types.h> +#include <sys/wait.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. + // + + setgid(getgid()); + setuid(getuid()); + + if (*type == 'r') + { + if (pdes[1] != 1) + { + // + // Set up stdout. + // + + dup2(pdes[1], 1); + close(pdes[1]); + } + + close(pdes[0]); + } + else + { + if (pdes[0] != 0) + { + // + // Set up stdin. + // + + dup2(pdes[0], 0); + close(pdes[0]); + } + + close(pdes[1]); + } + + execvp(parameters[0], parameters + 1); + + exit(127); + } + } + + // + // Parent. Save data about the child. + // + + RegisterChild(pid); + + if (*type == 'r') + { + iop = fdopen(pdes[0], type); + + close(pdes[1]); + } + else + { + iop = fdopen(pdes[1], type); + + close(pdes[0]); + } + + cur -> fp = iop; + cur -> self = pid; + cur -> next = pidlist; + + pidlist = cur; + + #ifdef TEST + *logofs << "Popen: Executing "; + + for (int i = 0; i < 256 && parameters[i] != NULL; i++) + { + *logofs << "[" << parameters[i] << "]"; + } + + *logofs << " with descriptor " << fileno(iop) + << ".\n" << logofs_flush; + #endif + + return iop; +} + +FILE *Popen(const char *command, const char *type) +{ + char *parameters[256]; + + if (Psplit(command, parameters, 256) > 0) + { + FILE *file = Popen(parameters, type); + + for (int i = 0; i < 256; i++) + { + delete [] parameters[i]; + } + + return file; + } + else + { + #ifdef PANIC + *logofs << "Popen: PANIC! Failed to parse command '" + << command << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Failed to parse command '" + << command << "'.\n"; + + return NULL; + } +} + +int Pclose(FILE *iop) +{ + struct pid *cur, *last; + + int pstat; + int pid; + + #ifdef TEST + *logofs << "Pclose: Closing command with output " + << "on descriptor " << fileno(iop) << ".\n" + << logofs_flush; + #endif + + fclose((FILE *) iop); + + for (last = NULL, cur = pidlist; cur; last = cur, cur = cur -> next) + { + if (cur -> fp == iop) + { + break; + } + } + + if (cur == NULL) + { + #ifdef PANIC + *logofs << "Pclose: PANIC! Failed to find the process " + << "for descriptor " << fileno(iop) << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failed to find the process " + << "for descriptor " << fileno(iop) << ".\n"; + + return -1; + } + + do + { + #ifdef TEST + *logofs << "Pclose: Going to wait for process " + << "with pid '" << cur -> self << "'.\n" + << logofs_flush; + #endif + + pid = waitpid(cur -> self, &pstat, 0); + } + while (pid == -1 && errno == EINTR); + + if (last == NULL) + { + pidlist = cur -> next; + } + else + { + last -> next = cur -> next; + } + + free(cur); + + // + // Child has finished and we called the + // waitpid(). We can enable signals again. + // + + EnableSignals(); + + return (pid == -1 ? -1 : pstat); +} diff --git a/nxcomp/Pipe.h b/nxcomp/Pipe.h new file mode 100644 index 000000000..b4563a967 --- /dev/null +++ b/nxcomp/Pipe.h @@ -0,0 +1,27 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// These are slightly modified versions of popen(3) and pclose(3) +// that don't rely on a shell to be available on the system, so +// that they can also work on Windows. +// + +extern FILE *Popen(char * const parameters[], const char *type); +extern FILE *Popen(const char *command, const char *type); + +extern int Pclose(FILE *file); diff --git a/nxcomp/PolyArc.cpp b/nxcomp/PolyArc.cpp new file mode 100644 index 000000000..e572fa454 --- /dev/null +++ b/nxcomp/PolyArc.cpp @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "PolyArc.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int PolyArcStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyArcMessage *polyArc = (PolyArcMessage *) message; + + // + // Here is the fingerprint. + // + + polyArc -> drawable = GetULONG(buffer + 4, bigEndian); + polyArc -> gcontext = GetULONG(buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int PolyArcStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyArcMessage *polyArc = (PolyArcMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(polyArc -> drawable, buffer + 4, bigEndian); + PutULONG(polyArc -> gcontext, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void PolyArcStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + PolyArcMessage *polyArc = (PolyArcMessage *) message; + + *logofs << name() << ": Identity drawable " << polyArc -> drawable + << ", gcontext " << polyArc -> gcontext + << ", size " << polyArc -> size_ << ".\n" << logofs_flush; + #endif +} + +void PolyArcStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ +} + +void PolyArcStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + PolyArcMessage *polyArc = (PolyArcMessage *) message; + PolyArcMessage *cachedPolyArc = (PolyArcMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyArc -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyArc -> drawable, clientCache -> drawableCache); + + cachedPolyArc -> drawable = polyArc -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyArc -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyArc -> gcontext, clientCache -> gcCache); + + cachedPolyArc -> gcontext = polyArc -> gcontext; +} + +void PolyArcStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + PolyArcMessage *polyArc = (PolyArcMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + polyArc -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyArc -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + polyArc -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyArc -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif +} + + diff --git a/nxcomp/PolyArc.h b/nxcomp/PolyArc.h new file mode 100644 index 000000000..50d2fd9a9 --- /dev/null +++ b/nxcomp/PolyArc.h @@ -0,0 +1,177 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PolyArc_H +#define PolyArc_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define POLYARC_ENABLE_CACHE 1 +#define POLYARC_ENABLE_DATA 0 +#define POLYARC_ENABLE_SPLIT 0 +#define POLYARC_ENABLE_COMPRESS 0 + +#define POLYARC_DATA_LIMIT 1980 +#define POLYARC_DATA_OFFSET 12 + +#define POLYARC_CACHE_SLOTS 2000 +#define POLYARC_CACHE_THRESHOLD 2 +#define POLYARC_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class PolyArcMessage : public Message +{ + friend class PolyArcStore; + + public: + + PolyArcMessage() + { + } + + ~PolyArcMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int drawable; + unsigned int gcontext; +}; + +class PolyArcStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + PolyArcStore() : MessageStore() + { + enableCache = POLYARC_ENABLE_CACHE; + enableData = POLYARC_ENABLE_DATA; + enableSplit = POLYARC_ENABLE_SPLIT; + enableCompress = POLYARC_ENABLE_COMPRESS; + + dataLimit = POLYARC_DATA_LIMIT; + dataOffset = POLYARC_DATA_OFFSET; + + cacheSlots = POLYARC_CACHE_SLOTS; + cacheThreshold = POLYARC_CACHE_THRESHOLD; + cacheLowerThreshold = POLYARC_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~PolyArcStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "PolyArc"; + } + + virtual unsigned char opcode() const + { + return X_PolyArc; + } + + virtual unsigned int storage() const + { + return sizeof(PolyArcMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new PolyArcMessage(); + } + + virtual Message *create(const Message &message) const + { + return new PolyArcMessage((const PolyArcMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (PolyArcMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* PolyArc_H */ diff --git a/nxcomp/PolyFillArc.cpp b/nxcomp/PolyFillArc.cpp new file mode 100644 index 000000000..2733eb62a --- /dev/null +++ b/nxcomp/PolyFillArc.cpp @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "PolyFillArc.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int PolyFillArcStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message; + + // + // Here is the fingerprint. + // + + polyFillArc -> drawable = GetULONG(buffer + 4, bigEndian); + polyFillArc -> gcontext = GetULONG(buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int PolyFillArcStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(polyFillArc -> drawable, buffer + 4, bigEndian); + PutULONG(polyFillArc -> gcontext, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void PolyFillArcStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message; + + *logofs << name() << ": Identity drawable " << polyFillArc -> drawable + << ", gcontext " << polyFillArc -> gcontext + << ", size " << polyFillArc -> size_ << ".\n" << logofs_flush; + #endif +} + +void PolyFillArcStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ +} + +void PolyFillArcStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message; + PolyFillArcMessage *cachedPolyFillArc = (PolyFillArcMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyFillArc -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyFillArc -> drawable, clientCache -> drawableCache); + + cachedPolyFillArc -> drawable = polyFillArc -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyFillArc -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyFillArc -> gcontext, clientCache -> gcCache); + + cachedPolyFillArc -> gcontext = polyFillArc -> gcontext; +} + +void PolyFillArcStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + polyFillArc -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyFillArc -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + polyFillArc -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyFillArc -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif +} + + diff --git a/nxcomp/PolyFillArc.h b/nxcomp/PolyFillArc.h new file mode 100644 index 000000000..721f5ac97 --- /dev/null +++ b/nxcomp/PolyFillArc.h @@ -0,0 +1,177 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PolyFillArc_H +#define PolyFillArc_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define POLYFILLARC_ENABLE_CACHE 1 +#define POLYFILLARC_ENABLE_DATA 0 +#define POLYFILLARC_ENABLE_SPLIT 0 +#define POLYFILLARC_ENABLE_COMPRESS 0 + +#define POLYFILLARC_DATA_LIMIT 6144 +#define POLYFILLARC_DATA_OFFSET 12 + +#define POLYFILLARC_CACHE_SLOTS 2000 +#define POLYFILLARC_CACHE_THRESHOLD 2 +#define POLYFILLARC_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class PolyFillArcMessage : public Message +{ + friend class PolyFillArcStore; + + public: + + PolyFillArcMessage() + { + } + + ~PolyFillArcMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int drawable; + unsigned int gcontext; +}; + +class PolyFillArcStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + PolyFillArcStore() : MessageStore() + { + enableCache = POLYFILLARC_ENABLE_CACHE; + enableData = POLYFILLARC_ENABLE_DATA; + enableSplit = POLYFILLARC_ENABLE_SPLIT; + enableCompress = POLYFILLARC_ENABLE_COMPRESS; + + dataLimit = POLYFILLARC_DATA_LIMIT; + dataOffset = POLYFILLARC_DATA_OFFSET; + + cacheSlots = POLYFILLARC_CACHE_SLOTS; + cacheThreshold = POLYFILLARC_CACHE_THRESHOLD; + cacheLowerThreshold = POLYFILLARC_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~PolyFillArcStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "PolyFillArc"; + } + + virtual unsigned char opcode() const + { + return X_PolyFillArc; + } + + virtual unsigned int storage() const + { + return sizeof(PolyFillArcMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new PolyFillArcMessage(); + } + + virtual Message *create(const Message &message) const + { + return new PolyFillArcMessage((const PolyFillArcMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (PolyFillArcMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* PolyFillArc_H */ diff --git a/nxcomp/PolyFillRectangle.cpp b/nxcomp/PolyFillRectangle.cpp new file mode 100644 index 000000000..e1e6b7876 --- /dev/null +++ b/nxcomp/PolyFillRectangle.cpp @@ -0,0 +1,148 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "PolyFillRectangle.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int PolyFillRectangleStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message; + + // + // Here is the fingerprint. + // + + polyFillRectangle -> drawable = GetULONG(buffer + 4, bigEndian); + polyFillRectangle -> gcontext = GetULONG(buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int PolyFillRectangleStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(polyFillRectangle -> drawable, buffer + 4, bigEndian); + PutULONG(polyFillRectangle -> gcontext, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void PolyFillRectangleStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message; + + *logofs << name() << ": Identity drawable " << polyFillRectangle -> drawable + << ", gcontext " << polyFillRectangle -> gcontext + << ", size " << polyFillRectangle -> size_ << ".\n" << logofs_flush; + #endif +} + +void PolyFillRectangleStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ +} + +void PolyFillRectangleStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message; + PolyFillRectangleMessage *cachedPolyFillRectangle = (PolyFillRectangleMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding value " << polyFillRectangle -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyFillRectangle -> drawable, clientCache -> drawableCache); + + cachedPolyFillRectangle -> drawable = polyFillRectangle -> drawable; + + #ifdef DEBUG + *logofs << name() << ": Encoding value " << polyFillRectangle -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyFillRectangle -> gcontext, clientCache -> gcCache); + + cachedPolyFillRectangle -> gcontext = polyFillRectangle -> gcontext; +} + +void PolyFillRectangleStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + polyFillRectangle -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyFillRectangle -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + polyFillRectangle -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyFillRectangle -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif +} diff --git a/nxcomp/PolyFillRectangle.h b/nxcomp/PolyFillRectangle.h new file mode 100644 index 000000000..bf94c4818 --- /dev/null +++ b/nxcomp/PolyFillRectangle.h @@ -0,0 +1,177 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PolyFillRectangle_H +#define PolyFillRectangle_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define POLYFILLRECTANGLE_ENABLE_CACHE 1 +#define POLYFILLRECTANGLE_ENABLE_DATA 0 +#define POLYFILLRECTANGLE_ENABLE_SPLIT 0 +#define POLYFILLRECTANGLE_ENABLE_COMPRESS 0 + +#define POLYFILLRECTANGLE_DATA_LIMIT 2048 +#define POLYFILLRECTANGLE_DATA_OFFSET 12 + +#define POLYFILLRECTANGLE_CACHE_SLOTS 4000 +#define POLYFILLRECTANGLE_CACHE_THRESHOLD 5 +#define POLYFILLRECTANGLE_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class PolyFillRectangleMessage : public Message +{ + friend class PolyFillRectangleStore; + + public: + + PolyFillRectangleMessage() + { + } + + ~PolyFillRectangleMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int drawable; + unsigned int gcontext; +}; + +class PolyFillRectangleStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + PolyFillRectangleStore() : MessageStore() + { + enableCache = POLYFILLRECTANGLE_ENABLE_CACHE; + enableData = POLYFILLRECTANGLE_ENABLE_DATA; + enableSplit = POLYFILLRECTANGLE_ENABLE_SPLIT; + enableCompress = POLYFILLRECTANGLE_ENABLE_COMPRESS; + + dataLimit = POLYFILLRECTANGLE_DATA_LIMIT; + dataOffset = POLYFILLRECTANGLE_DATA_OFFSET; + + cacheSlots = POLYFILLRECTANGLE_CACHE_SLOTS; + cacheThreshold = POLYFILLRECTANGLE_CACHE_THRESHOLD; + cacheLowerThreshold = POLYFILLRECTANGLE_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~PolyFillRectangleStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "PolyFillRectangle"; + } + + virtual unsigned char opcode() const + { + return X_PolyFillRectangle; + } + + virtual unsigned int storage() const + { + return sizeof(PolyFillRectangleMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new PolyFillRectangleMessage(); + } + + virtual Message *create(const Message &message) const + { + return new PolyFillRectangleMessage((const PolyFillRectangleMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (PolyFillRectangleMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* PolyFillRectangle_H */ diff --git a/nxcomp/PolyLine.cpp b/nxcomp/PolyLine.cpp new file mode 100644 index 000000000..4d285e0b1 --- /dev/null +++ b/nxcomp/PolyLine.cpp @@ -0,0 +1,170 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "PolyLine.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int PolyLineStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyLineMessage *polyLine = (PolyLineMessage *) message; + + // + // Here is the fingerprint. + // + + polyLine -> mode = *(buffer + 1); + + polyLine -> drawable = GetULONG(buffer + 4, bigEndian); + polyLine -> gcontext = GetULONG(buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int PolyLineStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyLineMessage *polyLine = (PolyLineMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = polyLine -> mode; + + PutULONG(polyLine -> drawable, buffer + 4, bigEndian); + PutULONG(polyLine -> gcontext, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void PolyLineStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + PolyLineMessage *polyLine = (PolyLineMessage *) message; + + *logofs << name() << ": Identity drawable " << polyLine -> drawable + << ", gcontext " << polyLine -> gcontext + << ", size " << polyLine -> size_ << ".\n" << logofs_flush; + #endif +} + +void PolyLineStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + if (control -> isProtoStep8() == 1) + { + md5_append(md5_state_, buffer + 1, 1); + } +} + +void PolyLineStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + PolyLineMessage *polyLine = (PolyLineMessage *) message; + PolyLineMessage *cachedPolyLine = (PolyLineMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + if (control -> isProtoStep8() == 0) + { + encodeBuffer.encodeBoolValue((unsigned int) polyLine -> mode); + } + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyLine -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyLine -> drawable, clientCache -> drawableCache); + + cachedPolyLine -> drawable = polyLine -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyLine -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyLine -> gcontext, clientCache -> gcCache); + + cachedPolyLine -> gcontext = polyLine -> gcontext; +} + +void PolyLineStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + PolyLineMessage *polyLine = (PolyLineMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + if (control -> isProtoStep8() == 0) + { + decodeBuffer.decodeBoolValue(value); + + polyLine -> mode = value; + } + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + polyLine -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyLine -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + polyLine -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyLine -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif +} + + diff --git a/nxcomp/PolyLine.h b/nxcomp/PolyLine.h new file mode 100644 index 000000000..39447e659 --- /dev/null +++ b/nxcomp/PolyLine.h @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PolyLine_H +#define PolyLine_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define POLYLINE_ENABLE_CACHE 1 +#define POLYLINE_ENABLE_DATA 0 +#define POLYLINE_ENABLE_SPLIT 0 +#define POLYLINE_ENABLE_COMPRESS 0 + +#define POLYLINE_DATA_LIMIT 144 +#define POLYLINE_DATA_OFFSET 12 + +#define POLYLINE_CACHE_SLOTS 3000 +#define POLYLINE_CACHE_THRESHOLD 3 +#define POLYLINE_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class PolyLineMessage : public Message +{ + friend class PolyLineStore; + + public: + + PolyLineMessage() + { + } + + ~PolyLineMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char mode; + unsigned int drawable; + unsigned int gcontext; +}; + +class PolyLineStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + PolyLineStore() : MessageStore() + { + enableCache = POLYLINE_ENABLE_CACHE; + enableData = POLYLINE_ENABLE_DATA; + enableSplit = POLYLINE_ENABLE_SPLIT; + enableCompress = POLYLINE_ENABLE_COMPRESS; + + dataLimit = POLYLINE_DATA_LIMIT; + dataOffset = POLYLINE_DATA_OFFSET; + + cacheSlots = POLYLINE_CACHE_SLOTS; + cacheThreshold = POLYLINE_CACHE_THRESHOLD; + cacheLowerThreshold = POLYLINE_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~PolyLineStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "PolyLine"; + } + + virtual unsigned char opcode() const + { + return X_PolyLine; + } + + virtual unsigned int storage() const + { + return sizeof(PolyLineMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new PolyLineMessage(); + } + + virtual Message *create(const Message &message) const + { + return new PolyLineMessage((const PolyLineMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (PolyLineMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* PolyLine_H */ diff --git a/nxcomp/PolyPoint.cpp b/nxcomp/PolyPoint.cpp new file mode 100644 index 000000000..847300f78 --- /dev/null +++ b/nxcomp/PolyPoint.cpp @@ -0,0 +1,170 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "PolyPoint.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int PolyPointStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyPointMessage *polyPoint = (PolyPointMessage *) message; + + // + // Here is the fingerprint. + // + + polyPoint -> mode = *(buffer + 1); + + polyPoint -> drawable = GetULONG(buffer + 4, bigEndian); + polyPoint -> gcontext = GetULONG(buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int PolyPointStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyPointMessage *polyPoint = (PolyPointMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = polyPoint -> mode; + + PutULONG(polyPoint -> drawable, buffer + 4, bigEndian); + PutULONG(polyPoint -> gcontext, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void PolyPointStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + PolyPointMessage *polyPoint = (PolyPointMessage *) message; + + *logofs << name() << ": Identity drawable " << polyPoint -> drawable + << ", gcontext " << polyPoint -> gcontext + << ", size " << polyPoint -> size_ << ".\n" << logofs_flush; + #endif +} + +void PolyPointStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + if (control -> isProtoStep8() == 1) + { + md5_append(md5_state_, buffer + 1, 1); + } +} + +void PolyPointStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + PolyPointMessage *polyPoint = (PolyPointMessage *) message; + PolyPointMessage *cachedPolyPoint = (PolyPointMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + if (control -> isProtoStep8() == 0) + { + encodeBuffer.encodeBoolValue((unsigned int) polyPoint -> mode); + } + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyPoint -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyPoint -> drawable, clientCache -> drawableCache); + + cachedPolyPoint -> drawable = polyPoint -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyPoint -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyPoint -> gcontext, clientCache -> gcCache); + + cachedPolyPoint -> gcontext = polyPoint -> gcontext; +} + +void PolyPointStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + PolyPointMessage *polyPoint = (PolyPointMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + if (control -> isProtoStep8() == 0) + { + decodeBuffer.decodeBoolValue(value); + + polyPoint -> mode = value; + } + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + polyPoint -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyPoint -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + polyPoint -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyPoint -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif +} + + diff --git a/nxcomp/PolyPoint.h b/nxcomp/PolyPoint.h new file mode 100644 index 000000000..e090ff9fa --- /dev/null +++ b/nxcomp/PolyPoint.h @@ -0,0 +1,178 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PolyPoint_H +#define PolyPoint_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define POLYPOINT_ENABLE_CACHE 1 +#define POLYPOINT_ENABLE_DATA 0 +#define POLYPOINT_ENABLE_SPLIT 0 +#define POLYPOINT_ENABLE_COMPRESS 0 + +#define POLYPOINT_DATA_LIMIT 3200 +#define POLYPOINT_DATA_OFFSET 12 + +#define POLYPOINT_CACHE_SLOTS 3000 +#define POLYPOINT_CACHE_THRESHOLD 3 +#define POLYPOINT_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class PolyPointMessage : public Message +{ + friend class PolyPointStore; + + public: + + PolyPointMessage() + { + } + + ~PolyPointMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char mode; + unsigned int drawable; + unsigned int gcontext; +}; + +class PolyPointStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + PolyPointStore() : MessageStore() + { + enableCache = POLYPOINT_ENABLE_CACHE; + enableData = POLYPOINT_ENABLE_DATA; + enableSplit = POLYPOINT_ENABLE_SPLIT; + enableCompress = POLYPOINT_ENABLE_COMPRESS; + + dataLimit = POLYPOINT_DATA_LIMIT; + dataOffset = POLYPOINT_DATA_OFFSET; + + cacheSlots = POLYPOINT_CACHE_SLOTS; + cacheThreshold = POLYPOINT_CACHE_THRESHOLD; + cacheLowerThreshold = POLYPOINT_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~PolyPointStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "PolyPoint"; + } + + virtual unsigned char opcode() const + { + return X_PolyPoint; + } + + virtual unsigned int storage() const + { + return sizeof(PolyPointMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new PolyPointMessage(); + } + + virtual Message *create(const Message &message) const + { + return new PolyPointMessage((const PolyPointMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (PolyPointMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* PolyPoint_H */ diff --git a/nxcomp/PolySegment.cpp b/nxcomp/PolySegment.cpp new file mode 100644 index 000000000..e9259d958 --- /dev/null +++ b/nxcomp/PolySegment.cpp @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "PolySegment.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int PolySegmentStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolySegmentMessage *polySegment = (PolySegmentMessage *) message; + + // + // Here is the fingerprint. + // + + polySegment -> drawable = GetULONG(buffer + 4, bigEndian); + polySegment -> gcontext = GetULONG(buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int PolySegmentStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolySegmentMessage *polySegment = (PolySegmentMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(polySegment -> drawable, buffer + 4, bigEndian); + PutULONG(polySegment -> gcontext, buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void PolySegmentStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + PolySegmentMessage *polySegment = (PolySegmentMessage *) message; + + *logofs << name() << ": Identity drawable " << polySegment -> drawable + << ", gcontext " << polySegment -> gcontext + << ", size " << polySegment -> size_ << ".\n" << logofs_flush; + #endif +} + +void PolySegmentStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ +} + +void PolySegmentStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + PolySegmentMessage *polySegment = (PolySegmentMessage *) message; + PolySegmentMessage *cachedPolySegment = (PolySegmentMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polySegment -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polySegment -> drawable, clientCache -> drawableCache); + + cachedPolySegment -> drawable = polySegment -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polySegment -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polySegment -> gcontext, clientCache -> gcCache); + + cachedPolySegment -> gcontext = polySegment -> gcontext; +} + +void PolySegmentStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + PolySegmentMessage *polySegment = (PolySegmentMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + polySegment -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polySegment -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + polySegment -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polySegment -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif +} + + diff --git a/nxcomp/PolySegment.h b/nxcomp/PolySegment.h new file mode 100644 index 000000000..a74865827 --- /dev/null +++ b/nxcomp/PolySegment.h @@ -0,0 +1,177 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PolySegment_H +#define PolySegment_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define POLYSEGMENT_ENABLE_CACHE 1 +#define POLYSEGMENT_ENABLE_DATA 0 +#define POLYSEGMENT_ENABLE_SPLIT 0 +#define POLYSEGMENT_ENABLE_COMPRESS 0 + +#define POLYSEGMENT_DATA_LIMIT 8192 +#define POLYSEGMENT_DATA_OFFSET 12 + +#define POLYSEGMENT_CACHE_SLOTS 3000 +#define POLYSEGMENT_CACHE_THRESHOLD 5 +#define POLYSEGMENT_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class PolySegmentMessage : public Message +{ + friend class PolySegmentStore; + + public: + + PolySegmentMessage() + { + } + + ~PolySegmentMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int drawable; + unsigned int gcontext; +}; + +class PolySegmentStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + PolySegmentStore() : MessageStore() + { + enableCache = POLYSEGMENT_ENABLE_CACHE; + enableData = POLYSEGMENT_ENABLE_DATA; + enableSplit = POLYSEGMENT_ENABLE_SPLIT; + enableCompress = POLYSEGMENT_ENABLE_COMPRESS; + + dataLimit = POLYSEGMENT_DATA_LIMIT; + dataOffset = POLYSEGMENT_DATA_OFFSET; + + cacheSlots = POLYSEGMENT_CACHE_SLOTS; + cacheThreshold = POLYSEGMENT_CACHE_THRESHOLD; + cacheLowerThreshold = POLYSEGMENT_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~PolySegmentStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "PolySegment"; + } + + virtual unsigned char opcode() const + { + return X_PolySegment; + } + + virtual unsigned int storage() const + { + return sizeof(PolySegmentMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new PolySegmentMessage(); + } + + virtual Message *create(const Message &message) const + { + return new PolySegmentMessage((const PolySegmentMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (PolySegmentMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* PolySegment_H */ diff --git a/nxcomp/PolyText16.cpp b/nxcomp/PolyText16.cpp new file mode 100644 index 000000000..d90b093ec --- /dev/null +++ b/nxcomp/PolyText16.cpp @@ -0,0 +1,300 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "PolyText16.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int PolyText16Store::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyText16Message *polyText16 = (PolyText16Message *) message; + + // + // Here is the fingerprint. + // + + polyText16 -> drawable = GetULONG(buffer + 4, bigEndian); + polyText16 -> gcontext = GetULONG(buffer + 8, bigEndian); + + polyText16 -> x = GetUINT(buffer + 12, bigEndian); + polyText16 -> y = GetUINT(buffer + 14, bigEndian); + + // + // Clean up padding bytes. + // + + #ifdef DUMP + + DumpData(buffer, size); + + *logofs << "\n" << logofs_flush; + + #endif + + if ((int) size > dataOffset) + { + int current; + int length; + int delta; + int item; + + unsigned int nitem; + + unsigned char *pad = NULL; + unsigned char *end = NULL; + + delta = 1; + nitem = 0; + + #ifdef DUMP + *logofs << name() << " Size " << size << ".\n" << logofs_flush; + #endif + + // + // Data is a list of TextItem where element + // can be a string or a font shift. + // + + current = POLYTEXT16_DATA_OFFSET; + length = POLYTEXT16_DATA_OFFSET; + + do + { + #ifdef DUMP + *logofs << name() << " Current " << current << ".\n" << logofs_flush; + #endif + + item = GetUINT(buffer + length , bigEndian); + + if (item < 255) + { + // + // Text element. Number represents + // the 'Length of CHAR2B string' + // field. + // + + length += ((item * 2) + delta + 1); + + nitem++; + } + else if (item == 255) + { + // + // Element is a font shift. + // + + length += 5; + + nitem++; + } + + #ifdef DUMP + *logofs << name() << " Item " << item << ".\n" << logofs_flush; + #endif + + current += length; + } + while(current < (int) size && item != 0); + + #ifdef DUMP + *logofs << name() << " Final length " << length << ".\n" << logofs_flush; + #endif + + end = ((unsigned char *) buffer) + size; + + pad = ((unsigned char *) buffer) + length; + + for (; pad < end && nitem >= 1; pad++) + { + #ifdef DUMP + *logofs << name() << " Padding " << " .\n" << logofs_flush; + #endif + + *pad = 0; + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int PolyText16Store::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyText16Message *polyText16 = (PolyText16Message *) message; + + // + // Fill all the message's fields. + // + + PutULONG(polyText16 -> drawable, buffer + 4, bigEndian); + PutULONG(polyText16 -> gcontext, buffer + 8, bigEndian); + + PutUINT(polyText16 -> x, buffer + 12, bigEndian); + PutUINT(polyText16 -> y, buffer + 14, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void PolyText16Store::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + PolyText16Message *polyText16 = (PolyText16Message *) message; + + *logofs << name() << ": Identity drawable " << polyText16 -> drawable + << ", gcontext " << polyText16 -> gcontext << ", x " << polyText16 -> x + << ", y " << polyText16 -> y << ", size " << polyText16 -> size_ + << ".\n"; + + #endif +} + +void PolyText16Store::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ +} + +void PolyText16Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + PolyText16Message *polyText16 = (PolyText16Message *) message; + PolyText16Message *cachedPolyText16 = (PolyText16Message *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyText16 -> drawable + << " as " << "drawable" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyText16 -> drawable, clientCache -> drawableCache); + + cachedPolyText16 -> drawable = polyText16 -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyText16 -> gcontext + << " as " << "gcontext" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyText16 -> gcontext, clientCache -> gcCache); + + cachedPolyText16 -> gcontext = polyText16 -> gcontext; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyText16 -> x + << " as " << "x" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_x = polyText16 -> x - cachedPolyText16 -> x; + + encodeBuffer.encodeCachedValue(diff_x, 16, + clientCache -> polyTextCacheX); + + cachedPolyText16 -> x = polyText16 -> x; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyText16 -> y + << " as " << "y" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_y = polyText16 -> y - cachedPolyText16 -> y; + + encodeBuffer.encodeCachedValue(diff_y, 16, + clientCache -> polyTextCacheY); + + cachedPolyText16 -> y = polyText16 -> y; +} + +void PolyText16Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + PolyText16Message *polyText16 = (PolyText16Message *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + polyText16 -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyText16 -> drawable + << " as " << "drawable" << " field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + polyText16 -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyText16 -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> polyTextCacheX); + + polyText16 -> x += value; + polyText16 -> x &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyText16 -> x + << " as x field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> polyTextCacheY); + + polyText16 -> y += value; + polyText16 -> y &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyText16 -> y + << " as y field.\n" << logofs_flush; + #endif +} + + diff --git a/nxcomp/PolyText16.h b/nxcomp/PolyText16.h new file mode 100644 index 000000000..cda6cceed --- /dev/null +++ b/nxcomp/PolyText16.h @@ -0,0 +1,180 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PolyText16_H +#define PolyText16_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define POLYTEXT16_ENABLE_CACHE 1 +#define POLYTEXT16_ENABLE_DATA 0 +#define POLYTEXT16_ENABLE_SPLIT 0 +#define POLYTEXT16_ENABLE_COMPRESS 0 + +#define POLYTEXT16_DATA_LIMIT 420 +#define POLYTEXT16_DATA_OFFSET 16 + +#define POLYTEXT16_CACHE_SLOTS 3000 +#define POLYTEXT16_CACHE_THRESHOLD 4 +#define POLYTEXT16_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class PolyText16Message : public Message +{ + friend class PolyText16Store; + + public: + + PolyText16Message() + { + } + + ~PolyText16Message() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int drawable; + unsigned int gcontext; + + unsigned short x; + unsigned short y; +}; + +class PolyText16Store : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + PolyText16Store() : MessageStore() + { + enableCache = POLYTEXT16_ENABLE_CACHE; + enableData = POLYTEXT16_ENABLE_DATA; + enableSplit = POLYTEXT16_ENABLE_SPLIT; + enableCompress = POLYTEXT16_ENABLE_COMPRESS; + + dataLimit = POLYTEXT16_DATA_LIMIT; + dataOffset = POLYTEXT16_DATA_OFFSET; + + cacheSlots = POLYTEXT16_CACHE_SLOTS; + cacheThreshold = POLYTEXT16_CACHE_THRESHOLD; + cacheLowerThreshold = POLYTEXT16_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~PolyText16Store() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "PolyText16"; + } + + virtual unsigned char opcode() const + { + return X_PolyText16; + } + + virtual unsigned int storage() const + { + return sizeof(PolyText16Message); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new PolyText16Message(); + } + + virtual Message *create(const Message &message) const + { + return new PolyText16Message((const PolyText16Message &) message); + } + + virtual void destroy(Message *message) const + { + delete (PolyText16Message *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* PolyText16_H */ diff --git a/nxcomp/PolyText8.cpp b/nxcomp/PolyText8.cpp new file mode 100644 index 000000000..15752721d --- /dev/null +++ b/nxcomp/PolyText8.cpp @@ -0,0 +1,298 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "PolyText8.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int PolyText8Store::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyText8Message *polyText8 = (PolyText8Message *) message; + + // + // Here is the fingerprint. + // + + polyText8 -> drawable = GetULONG(buffer + 4, bigEndian); + polyText8 -> gcontext = GetULONG(buffer + 8, bigEndian); + + polyText8 -> x = GetUINT(buffer + 12, bigEndian); + polyText8 -> y = GetUINT(buffer + 14, bigEndian); + + // + // Clean up padding bytes. + // + + #ifdef DUMP + + DumpData(buffer, size); + + *logofs << "\n\n" << logofs_flush; + + #endif + + if ((int) size > dataOffset) + { + int length; + int current; + int delta; + int item; + + unsigned int nitem; + + unsigned char *pad = NULL; + unsigned char *end = NULL; + + delta = 1; + nitem = 0; + + #ifdef DUMP + *logofs << name() << " Size " << size << ".\n" << logofs_flush; + #endif + + // + // Data is a list of TextItem where element + // can be a string or a font shift. + // + + current = POLYTEXT8_DATA_OFFSET; + length = POLYTEXT8_DATA_OFFSET; + + do + { + #ifdef DUMP + *logofs << name() << " Current " << current << ".\n" << logofs_flush; + #endif + + item = GetUINT(buffer + length , bigEndian); + + if (item < 255) + { + // + // Text element. Number represents + // the 'Length of string' field. + // + + length += (item + delta + 1); + + nitem++; + } + else if (item == 255) + { + // + // Element is a font shift. + // + + length += 5; + + nitem++; + } + + #ifdef DUMP + *logofs << name() << " Item " << item << ".\n" << logofs_flush; + #endif + + current += length; + } + while(current < (int) size && item != 0); + + + #ifdef DUMP + *logofs << name() << " Final length " << length << ".\n" << logofs_flush; + #endif + + end = ((unsigned char *) buffer) + size; + + pad = ((unsigned char *) buffer) + length; + + for (; pad < end && nitem >= 1; pad++) + { + #ifdef DUMP + *logofs << name() << " Padding " << " .\n" << logofs_flush; + #endif + + *pad = 0; + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int PolyText8Store::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PolyText8Message *polyText8 = (PolyText8Message *) message; + + // + // Fill all the message's fields. + // + + PutULONG(polyText8 -> drawable, buffer + 4, bigEndian); + PutULONG(polyText8 -> gcontext, buffer + 8, bigEndian); + + PutUINT(polyText8 -> x, buffer + 12, bigEndian); + PutUINT(polyText8 -> y, buffer + 14, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void PolyText8Store::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + PolyText8Message *polyText8 = (PolyText8Message *) message; + + *logofs << name() << ": Identity drawable " << polyText8 -> drawable + << ", gcontext " << polyText8 -> gcontext << ", x " << polyText8 -> x + << ", y " << polyText8 -> y << ", size " << polyText8 -> size_ + << ".\n"; + + #endif +} + +void PolyText8Store::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ +} + +void PolyText8Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + PolyText8Message *polyText8 = (PolyText8Message *) message; + PolyText8Message *cachedPolyText8 = (PolyText8Message *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyText8 -> drawable + << " as " << "drawable" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyText8 -> drawable, clientCache -> drawableCache); + + cachedPolyText8 -> drawable = polyText8 -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyText8 -> gcontext + << " as " << "gcontext" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(polyText8 -> gcontext, clientCache -> gcCache); + + cachedPolyText8 -> gcontext = polyText8 -> gcontext; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyText8 -> x + << " as " << "x" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_x = polyText8 -> x - cachedPolyText8 -> x; + + encodeBuffer.encodeCachedValue(diff_x, 16, + clientCache -> polyTextCacheX); + + cachedPolyText8 -> x = polyText8 -> x; + + #ifdef TEST + *logofs << name() << ": Encoding value " << polyText8 -> y + << " as " << "y" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_y = polyText8 -> y - cachedPolyText8 -> y; + + encodeBuffer.encodeCachedValue(diff_y, 16, + clientCache -> polyTextCacheY); + + cachedPolyText8 -> y = polyText8 -> y; +} + +void PolyText8Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + PolyText8Message *polyText8 = (PolyText8Message *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + polyText8 -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyText8 -> drawable + << " as " << "drawable" << " field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + polyText8 -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyText8 -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> polyTextCacheX); + + polyText8 -> x += value; + polyText8 -> x &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyText8 -> x + << " as x field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> polyTextCacheY); + + polyText8 -> y += value; + polyText8 -> y &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << polyText8 -> y + << " as y field.\n" << logofs_flush; + #endif +} diff --git a/nxcomp/PolyText8.h b/nxcomp/PolyText8.h new file mode 100644 index 000000000..eac5aab7c --- /dev/null +++ b/nxcomp/PolyText8.h @@ -0,0 +1,180 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PolyText8_H +#define PolyText8_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define POLYTEXT8_ENABLE_CACHE 1 +#define POLYTEXT8_ENABLE_DATA 0 +#define POLYTEXT8_ENABLE_SPLIT 0 +#define POLYTEXT8_ENABLE_COMPRESS 0 + +#define POLYTEXT8_DATA_LIMIT 380 +#define POLYTEXT8_DATA_OFFSET 16 + +#define POLYTEXT8_CACHE_SLOTS 3000 +#define POLYTEXT8_CACHE_THRESHOLD 5 +#define POLYTEXT8_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class PolyText8Message : public Message +{ + friend class PolyText8Store; + + public: + + PolyText8Message() + { + } + + ~PolyText8Message() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int drawable; + unsigned int gcontext; + + unsigned short x; + unsigned short y; +}; + +class PolyText8Store : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + PolyText8Store() : MessageStore() + { + enableCache = POLYTEXT8_ENABLE_CACHE; + enableData = POLYTEXT8_ENABLE_DATA; + enableSplit = POLYTEXT8_ENABLE_SPLIT; + enableCompress = POLYTEXT8_ENABLE_COMPRESS; + + dataLimit = POLYTEXT8_DATA_LIMIT; + dataOffset = POLYTEXT8_DATA_OFFSET; + + cacheSlots = POLYTEXT8_CACHE_SLOTS; + cacheThreshold = POLYTEXT8_CACHE_THRESHOLD; + cacheLowerThreshold = POLYTEXT8_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~PolyText8Store() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "PolyText8"; + } + + virtual unsigned char opcode() const + { + return X_PolyText8; + } + + virtual unsigned int storage() const + { + return sizeof(PolyText8Message); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new PolyText8Message(); + } + + virtual Message *create(const Message &message) const + { + return new PolyText8Message((const PolyText8Message &) message); + } + + virtual void destroy(Message *message) const + { + delete (PolyText8Message *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* PolyText8_H */ diff --git a/nxcomp/PositionCacheCompat.cpp b/nxcomp/PositionCacheCompat.cpp new file mode 100644 index 000000000..4a6a2cd63 --- /dev/null +++ b/nxcomp/PositionCacheCompat.cpp @@ -0,0 +1,45 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Control.h" + +#include "PositionCacheCompat.h" + +PositionCacheCompat::PositionCacheCompat() +{ + if (control -> isProtoStep7() == 0) + { + for (int i = 0; i < 32; i++) + { + base_[i] = new IntCache(8); + } + + slot_ = 0; + last_ = 0; + } +} + +PositionCacheCompat::~PositionCacheCompat() +{ + if (control -> isProtoStep7() == 0) + { + for (int i = 0; i < 32; i++) + { + delete base_[i]; + } + } +} diff --git a/nxcomp/PositionCacheCompat.h b/nxcomp/PositionCacheCompat.h new file mode 100644 index 000000000..983e45382 --- /dev/null +++ b/nxcomp/PositionCacheCompat.h @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PositionCacheCompat_H +#define PositionCacheCompat_H + +#include "IntCache.h" + +class PositionCacheCompat +{ + friend class EncodeBuffer; + friend class DecodeBuffer; + + public: + + PositionCacheCompat(); + ~PositionCacheCompat(); + + private: + + IntCache *base_[32]; + + unsigned int slot_; + short int last_; +}; + +#endif /* PositionCacheCompat_H */ diff --git a/nxcomp/Proxy.cpp b/nxcomp/Proxy.cpp new file mode 100644 index 000000000..3b4df7eb6 --- /dev/null +++ b/nxcomp/Proxy.cpp @@ -0,0 +1,6450 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "Misc.h" + +#if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun) +#include <netinet/in_systm.h> +#endif + +#ifndef __CYGWIN32__ +#include <sys/un.h> +#endif + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#if defined(__EMX__ ) || defined(__CYGWIN32__) + +struct sockaddr_un +{ + u_short sun_family; + char sun_path[108]; +}; + +#endif + +#include "NXalert.h" +#include "NXvars.h" + +#include "Proxy.h" + +#include "Socket.h" +#include "Channel.h" +#include "Statistics.h" + +#include "ClientChannel.h" +#include "ServerChannel.h" +#include "GenericChannel.h" + +// +// We need to adjust some values related +// to these messages at the time the mes- +// sage stores are reconfigured. +// + +#include "PutImage.h" +#include "ChangeGC.h" +#include "PolyFillRectangle.h" +#include "PutPackedImage.h" + +// +// This is from the main loop. +// + +extern void CleanupListeners(); + +// +// Default size of string buffers. +// + +#define DEFAULT_STRING_LENGTH 512 + +// +// Set the verbosity level. You also need +// to define DUMP in Misc.cpp if DUMP is +// defined here. +// + +#define WARNING +#define PANIC +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Log the important tracepoints related +// to writing packets to the peer proxy. +// + +#undef FLUSH + +// +// Log the operations related to splits. +// + +#undef SPLIT + +// +// Log the operations related to sending +// and receiving the control tokens. +// + +#undef TOKEN + +// +// Log the operations related to setting +// the token limits. +// + +#undef LIMIT + +// +// Log a warning if no data is written by +// the proxy within a timeout. +// + +#undef TIME + +// +// Log the operation related to generating +// the ping message at idle time. +// + +#undef PING + +Proxy::Proxy(int fd) + + : transport_(new ProxyTransport(fd)), fd_(fd), readBuffer_(transport_) +{ + for (int channelId = 0; + channelId < CONNECTIONS_LIMIT; + channelId++) + { + channels_[channelId] = NULL; + transports_[channelId] = NULL; + congestions_[channelId] = 0; + + fdMap_[channelId] = nothing; + channelMap_[channelId] = nothing; + } + + inputChannel_ = nothing; + outputChannel_ = nothing; + + controlLength_ = 0; + + operation_ = operation_in_negotiation; + + draining_ = 0; + priority_ = 0; + finish_ = 0; + shutdown_ = 0; + congestion_ = 0; + + timer_ = 0; + alert_ = 0; + + agent_ = nothing; + + // + // Set null timeouts. This will require + // a new link configuration. + // + + timeouts_.split = 0; + timeouts_.motion = 0; + + timeouts_.readTs = getTimestamp(); + timeouts_.writeTs = getTimestamp(); + + timeouts_.loopTs = getTimestamp(); + timeouts_.pingTs = getTimestamp(); + timeouts_.alertTs = nullTimestamp(); + timeouts_.loadTs = nullTimestamp(); + + timeouts_.splitTs = nullTimestamp(); + timeouts_.motionTs = nullTimestamp(); + + // + // Initialize the token counters. This + // will require a new link configuration. + // + + for (int i = token_control; i <= token_data; i++) + { + tokens_[i].size = 0; + tokens_[i].limit = 0; + + tokens_[i].bytes = 0; + tokens_[i].remaining = 0; + } + + tokens_[token_control].request = code_control_token_request; + tokens_[token_control].reply = code_control_token_reply; + tokens_[token_control].type = token_control; + + tokens_[token_split].request = code_split_token_request; + tokens_[token_split].reply = code_split_token_reply; + tokens_[token_split].type = token_split; + + tokens_[token_data].request = code_data_token_request; + tokens_[token_data].reply = code_data_token_reply; + tokens_[token_data].type = token_data; + + currentStatistics_ = NULL; + + // + // Create compressor and decompressor + // for image and data payload. + // + + compressor_ = new StaticCompressor(control -> LocalDataCompressionLevel, + control -> LocalDataCompressionThreshold); + + // + // Create object storing NX specific + // opcodes. + // + + opcodeStore_ = new OpcodeStore(); + + // + // Create the message stores. + // + + clientStore_ = new ClientStore(compressor_); + serverStore_ = new ServerStore(compressor_); + + // + // Older proxies will refuse to store + // messages bigger than 262144 bytes. + // + + if (control -> isProtoStep7() == 0) + { + #ifdef TEST + *logofs << "Proxy: WARNING! Limiting the maximum " + << "message size to " << 262144 << ".\n" + << logofs_flush; + #endif + + control -> MaximumMessageSize = 262144; + } + + clientCache_ = new ClientCache(); + serverCache_ = new ServerCache(); + + if (clientCache_ == NULL || serverCache_ == NULL) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Failed to create the channel cache.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failed to create the channel cache.\n"; + + HandleCleanup(); + } + + // + // Prepare for image decompression. + // + + UnpackInit(); + + #ifdef DEBUG + *logofs << "Proxy: Created new object at " << this + << ".\n" << logofs_flush; + #endif +} + +Proxy::~Proxy() +{ + for (int channelId = 0; + channelId < CONNECTIONS_LIMIT; + channelId++) + { + if (channels_[channelId] != NULL) + { + deallocateTransport(channelId); + + delete channels_[channelId]; + channels_[channelId] = NULL; + } + } + + delete transport_; + delete compressor_; + + // + // Delete storage shared among channels. + // + + delete opcodeStore_; + + delete clientStore_; + delete serverStore_; + + delete clientCache_; + delete serverCache_; + + // + // Get rid of the image decompression + // resources. + // + + UnpackDestroy(); + + #ifdef DEBUG + *logofs << "Proxy: Deleted proxy object at " << this + << ".\n" << logofs_flush; + #endif +} + +int Proxy::setOperational() +{ + #ifdef TEST + *logofs << "Proxy: Entering operational mode.\n" + << logofs_flush; + #endif + + operation_ = operation_in_messages; + + return 1; +} + +int Proxy::setReadDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax) +{ + // + // Set the initial timeout to the time of + // the next ping. If the congestion count + // is greater than zero, anyway, use a + // shorter timeout to force a congestion + // update. + // + + if (agent_ != nothing && congestions_[agent_] == 0 && + statistics -> getCongestionInFrame() >= 1 && + tokens_[token_control].remaining >= + (tokens_[token_control].limit - 1)) + { + setMinTimestamp(tsMax, control -> IdleTimeout); + + #ifdef TEST + *logofs << "Proxy: Initial timeout is " << tsMax.tv_sec + << " S and " << (double) tsMax.tv_usec / + 1000 << " Ms with congestion " + << statistics -> getCongestionInFrame() + << ".\n" << logofs_flush; + #endif + } + else + { + setMinTimestamp(tsMax, control -> PingTimeout); + + #ifdef TEST + *logofs << "Proxy: Initial timeout is " << tsMax.tv_sec + << " S and " << (double) tsMax.tv_usec / + 1000 << " Ms.\n" << logofs_flush; + #endif + } + + int fd = -1; + + if (isTimeToRead() == 1) + { + // + // If we don't have split tokens available + // don't set the timeout. + // + + if (tokens_[token_split].remaining > 0 && + isTimestamp(timeouts_.splitTs) == 1) + { + int diffTs = getTimeToNextSplit(); + + #if defined(TEST) || defined(INFO) || \ + defined(FLUSH) || defined(SPLIT) + + if (diffTimestamp(timeouts_.splitTs, + getTimestamp()) > timeouts_.split) + { + *logofs << "Proxy: FLUSH! SPLIT! WARNING! Running with " + << diffTimestamp(timeouts_.splitTs, getTimestamp()) + << " Ms elapsed since the last split.\n" + << logofs_flush; + } + + *logofs << "Proxy: FLUSH! SPLIT! Requesting timeout of " + << diffTs << " Ms as there are splits to send.\n" + << logofs_flush; + + #endif + + setMinTimestamp(tsMax, diffTs); + } + #if defined(TEST) || defined(INFO) + else if (isTimestamp(timeouts_.splitTs) == 1) + { + *logofs << "Proxy: WARNING! Not requesting a split " + << "timeout with " << tokens_[token_split].remaining + << " split tokens remaining.\n" << logofs_flush; + } + #endif + + // + // Loop through the valid channels and set + // the descriptors selected for read and + // the timeout. + // + + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] == NULL) + { + continue; + } + + fd = getFd(channelId); + + if (channels_[channelId] -> getFinish() == 0 && + (channels_[channelId] -> getType() == channel_x11 || + tokens_[token_data].remaining > 0) && + congestions_[channelId] == 0) + { + FD_SET(fd, fdSet); + + if (fd >= fdMax) + { + fdMax = fd + 1; + } + + #ifdef TEST + *logofs << "Proxy: Descriptor FD#" << fd + << " selected for read with buffer length " + << transports_[channelId] -> length() + << ".\n" << logofs_flush; + #endif + + // + // Wakeup the proxy if there are motion + // events to flush. + // + + if (isTimestamp(timeouts_.motionTs) == 1) + { + int diffTs = getTimeToNextMotion(); + + #if defined(TEST) || defined(INFO) + + if (diffTimestamp(timeouts_.motionTs, + getTimestamp()) > timeouts_.motion) + { + *logofs << "Proxy: FLUSH! WARNING! Running with " + << diffTimestamp(timeouts_.motionTs, getTimestamp()) + << " Ms elapsed since the last motion.\n" + << logofs_flush; + } + + *logofs << "Proxy: FLUSH! Requesting timeout of " + << diffTs << " Ms as FD#" << fd << " has motion " + << "events to send.\n" << logofs_flush; + + #endif + + setMinTimestamp(tsMax, diffTs); + } + } + #if defined(TEST) || defined(INFO) + else + { + if (channels_[channelId] -> getType() != channel_x11 && + tokens_[token_data].remaining <= 0) + { + *logofs << "Proxy: WARNING! Descriptor FD#" << fd + << " not selected for read with " + << tokens_[token_data].remaining << " data " + << "tokens remaining.\n" << logofs_flush; + } + } + #endif + } + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "Proxy: WARNING! Disabled reading from channels.\n" + << logofs_flush; + + *logofs << "Proxy: WARNING! Congestion is " << congestion_ + << " pending " << transport_ -> pending() << " blocked " + << transport_ -> blocked() << " length " << transport_ -> + length() << ".\n" << logofs_flush; + } + #endif + + // + // Include the proxy descriptor. + // + + FD_SET(fd_, fdSet); + + if (fd_ >= fdMax) + { + fdMax = fd_ + 1; + } + + #ifdef TEST + *logofs << "Proxy: Proxy descriptor FD#" << fd_ + << " selected for read with buffer length " + << transport_ -> length() << ".\n" + << logofs_flush; + #endif + + return 1; +} + +// +// Add to the mask the file descriptors of all +// X connections to write to. +// + +int Proxy::setWriteDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax) +{ + int fd = -1; + + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] != NULL) + { + fd = getFd(channelId); + + if (transports_[channelId] -> length() > 0) + { + FD_SET(fd, fdSet); + + #ifdef TEST + *logofs << "Proxy: Descriptor FD#" << fd << " selected " + << "for write with blocked " << transports_[channelId] -> + blocked() << " and length " << transports_[channelId] -> + length() << ".\n" << logofs_flush; + #endif + + if (fd >= fdMax) + { + fdMax = fd + 1; + } + } + #ifdef TEST + else + { + *logofs << "Proxy: Descriptor FD#" << fd << " not selected " + << "for write with blocked " << transports_[channelId] -> + blocked() << " and length " << transports_[channelId] -> + length() << ".\n" << logofs_flush; + } + #endif + + #if defined(TEST) || defined(INFO) + + if (transports_[channelId] -> getType() != + transport_agent && transports_[channelId] -> + length() > 0 && transports_[channelId] -> + blocked() != 1) + { + *logofs << "Proxy: PANIC! Descriptor FD#" << fd + << " has data to write but blocked flag is " + << transports_[channelId] -> blocked() + << ".\n" << logofs_flush; + + cerr << "Error" << ": Descriptor FD#" << fd + << " has data to write but blocked flag is " + << transports_[channelId] -> blocked() + << ".\n"; + + HandleCleanup(); + } + + #endif + } + } + + // + // Check if the proxy transport has data + // from a previous blocking write. + // + + if (transport_ -> blocked() == 1) + { + FD_SET(fd_, fdSet); + + #ifdef TEST + *logofs << "Proxy: Proxy descriptor FD#" + << fd_ << " selected for write. Blocked is " + << transport_ -> blocked() << " length is " + << transport_ -> length() << ".\n" + << logofs_flush; + #endif + + if (fd_ >= fdMax) + { + fdMax = fd_ + 1; + } + } + #ifdef TEST + else + { + *logofs << "Proxy: Proxy descriptor FD#" + << fd_ << " not selected for write. Blocked is " + << transport_ -> blocked() << " length is " + << transport_ -> length() << ".\n" + << logofs_flush; + } + #endif + + // + // We are entering the main select. Save + // the timestamp of the last loop so that + // we can detect the clock drifts. + // + + timeouts_.loopTs = getTimestamp(); + + return 1; +} + +int Proxy::getChannels(T_channel_type type) +{ + int channels = 0; + + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] != NULL && + (type == channel_none || + type == channels_[channelId] -> + getType())) + { + channels++; + } + } + + return channels; +} + +T_channel_type Proxy::getType(int fd) +{ + int channelId = getChannel(fd); + + if (channelId < 0 || channels_[channelId] == NULL) + { + return channel_none; + } + + return channels_[channelId] -> getType(); +} + +const char *Proxy::getTypeName(T_channel_type type) +{ + switch (type) + { + case channel_x11: + { + return "X"; + } + case channel_cups: + { + return "CUPS"; + } + case channel_smb: + { + return "SMB"; + } + case channel_media: + { + return "media"; + } + case channel_http: + { + return "HTTP"; + } + case channel_font: + { + return "font"; + } + case channel_slave: + { + return "slave"; + } + default: + { + return "unknown"; + } + } +} + +const char *Proxy::getComputerName() +{ + // + // Strangely enough, under some Windows OSes SMB + // service doesn't bind to localhost. Fall back + // to localhost if can't find computer name in + // the environment. In future we should try to + // bind to localhost and then try the other IPs. + // + + const char *hostname = NULL; + + #ifdef __CYGWIN32__ + + hostname = getenv("COMPUTERNAME"); + + #endif + + if (hostname == NULL) + { + hostname = "localhost"; + } + + return hostname; +} + +// +// Handle data from channels selected for read. +// + +int Proxy::handleRead(int &resultFds, fd_set &readSet) +{ + #ifdef DEBUG + *logofs << "Proxy: Checking descriptors selected for read.\n" + << logofs_flush; + #endif + + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + #ifdef DEBUG + *logofs << "Proxy: Looping with current channel " + << *j << ".\n" << logofs_flush; + #endif + + int fd = getFd(*j); + + if (fd >= 0 && resultFds > 0 && FD_ISSET(fd, &readSet)) + { + #ifdef DEBUG + *logofs << "Proxy: Going to read messages from FD#" + << fd << ".\n" << logofs_flush; + #endif + + int result = handleRead(fd); + + if (result < 0) + { + #ifdef TEST + *logofs << "Proxy: Failure reading messages from FD#" + << fd << ".\n" << logofs_flush; + #endif + + return -1; + } + + #ifdef DEBUG + *logofs << "Proxy: Clearing the read descriptor " + << "for FD#" << fd << ".\n" << logofs_flush; + #endif + + FD_CLR(fd, &readSet); + + resultFds--; + } + } + + if (resultFds > 0 && FD_ISSET(fd_, &readSet)) + { + #ifdef DEBUG + *logofs << "Proxy: Going to read messages from " + << "proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + if (handleRead() < 0) + { + #ifdef TEST + *logofs << "Proxy: Failure reading from proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return -1; + } + + #ifdef DEBUG + *logofs << "Proxy: Clearing the read descriptor " + << "for proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + FD_CLR(fd_, &readSet); + + resultFds--; + } + + return 1; +} + +// +// Perform flush on descriptors selected for write. +// + +int Proxy::handleFlush(int &resultFds, fd_set &writeSet) +{ + #ifdef DEBUG + *logofs << "Proxy: Checking descriptors selected for write.\n" + << logofs_flush; + #endif + + if (resultFds > 0 && FD_ISSET(fd_, &writeSet)) + { + #ifdef TEST + *logofs << "Proxy: FLUSH! Proxy descriptor FD#" << fd_ + << " reported to be writable.\n" + << logofs_flush; + #endif + + if (handleFlush() < 0) + { + #ifdef TEST + *logofs << "Proxy: Failure flushing the writable " + << "proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + return -1; + } + + #ifdef DEBUG + *logofs << "Proxy: Clearing the write descriptor " + << "for proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + FD_CLR(fd_, &writeSet); + + resultFds--; + } + + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + resultFds > 0 && j != channelList.end(); j++) + { + #ifdef DEBUG + *logofs << "Proxy: Looping with current channel " + << *j << ".\n" << logofs_flush; + #endif + + int fd = getFd(*j); + + if (fd >= 0 && FD_ISSET(fd, &writeSet)) + { + #ifdef TEST + *logofs << "Proxy: X descriptor FD#" << fd + << " reported to be writable.\n" + << logofs_flush; + #endif + + // + // It can happen that, in handling reads, we + // have destroyed the buffer associated to a + // closed socket, so don't complain about + // the errors. + // + + handleFlush(fd); + + // + // Clear the descriptor from the mask so + // we don't confuse the agent if it's + // not checking only its own descriptors. + // + + #ifdef DEBUG + *logofs << "Proxy: Clearing the write descriptor " + << "for FD#" << fd << ".\n" + << logofs_flush; + #endif + + FD_CLR(fd, &writeSet); + + resultFds--; + } + } + + return 1; +} + +int Proxy::handleRead() +{ + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Decoding data from proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + // + // Decode all the available messages from + // the remote proxy until is not possible + // to read more. + // + + for (;;) + { + int result = readBuffer_.readMessage(); + + #if defined(TEST) || defined(DEBUG) || defined(INFO) + *logofs << "Proxy: Read result on proxy FD#" << fd_ + << " is " << result << ".\n" + << logofs_flush; + #endif + + if (result < 0) + { + if (shutdown_ == 0) + { + if (finish_ == 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Failure reading from the " + << "peer proxy on FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure reading from the " + << "peer proxy.\n"; + } + } + #ifdef TEST + else + { + *logofs << "Proxy: Closure of the proxy link detected " + << "after clean shutdown.\n" << logofs_flush; + } + #endif + + priority_ = 0; + finish_ = 1; + congestion_ = 0; + + return -1; + } + else if (result == 0) + { + #if defined(TEST) || defined(DEBUG) || defined(INFO) + *logofs << "Proxy: No data read from proxy FD#" + << fd_ << "\n" << logofs_flush; + #endif + + return 0; + } + + // + // We read some data from the remote. If we set + // the congestion flag because we couldn't read + // before the timeout and have tokens available, + // then reset the congestion flag. + // + + if (congestion_ == 1 && + tokens_[token_control].remaining > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Exiting congestion due to " + << "proxy data with " << tokens_[token_control].remaining + << " tokens.\n" << logofs_flush; + #endif + + congestion_ = 0; + } + + // + // Set the timestamp of the last read + // operation from the remote proxy and + // enable again showing the 'no data + // received' dialog at the next timeout. + // + + timeouts_.readTs = getTimestamp(); + + if (alert_ != 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Displacing the dialog " + << "for proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + HandleAlert(DISPLACE_MESSAGE_ALERT, 1); + } + + timeouts_.alertTs = nullTimestamp(); + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Getting messages from proxy FD#" << fd_ + << " with " << readBuffer_.getLength() << " bytes " + << "in the read buffer.\n" << logofs_flush; + #endif + + unsigned int controlLength; + unsigned int dataLength; + + const unsigned char *message; + + while ((message = readBuffer_.getMessage(controlLength, dataLength)) != NULL) + { + statistics -> addFrameIn(); + + if (controlLength == 3 && *message == 0 && + *(message + 1) < code_last_tag) + { + if (handleControlFromProxy(message) < 0) + { + return -1; + } + } + else if (operation_ == operation_in_messages) + { + int channelId = inputChannel_; + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Identified message of " << dataLength + << " bytes for FD#" << getFd(channelId) << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + if (channelId >= 0 && channelId < CONNECTIONS_LIMIT && + channels_[channelId] != NULL) + { + int finish = channels_[channelId] -> getFinish(); + + #ifdef WARNING + + if (finish == 1) + { + *logofs << "Proxy: WARNING! Handling data for finishing " + << "FD#" << getFd(channelId) << " channel ID#" + << channelId << ".\n" << logofs_flush; + } + + #endif + + // + // We need to decode all the data to preserve + // the consistency of the cache, so can't re- + // turn as soon as the first error is encount- + // ered. Check if this is the first time that + // the failure is detected. + // + + int result = channels_[channelId] -> handleWrite(message, dataLength); + + if (result < 0 && finish == 0) + { + #ifdef TEST + *logofs << "Proxy: Failed to write proxy data to FD#" + << getFd(channelId) << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + if (handleFinish(channelId) < 0) + { + return -1; + } + } + + // + // Check if we have splits or motion + // events to send. + // + + setSplitTimeout(channelId); + setMotionTimeout(channelId); + } + #ifdef WARNING + else + { + *logofs << "Proxy: WARNING! Received data for " + << "invalid channel ID#" << channelId + << ".\n" << logofs_flush; + } + #endif + } + else if (operation_ == operation_in_statistics) + { + #ifdef TEST + *logofs << "Proxy: Received statistics data from remote proxy.\n" + << logofs_flush; + #endif + + if (handleStatisticsFromProxy(message, dataLength) < 0) + { + return -1; + } + + operation_ = operation_in_messages; + } + else if (operation_ == operation_in_negotiation) + { + #ifdef TEST + *logofs << "Proxy: Received new negotiation data from remote proxy.\n" + << logofs_flush; + #endif + + if (handleNegotiationFromProxy(message, dataLength) < 0) + { + return -1; + } + } + + // + // if (controlLength == 3 && *message == 0 && ...) ... + // else if (operation_ == operation_in_statistics) ... + // else if (operation_ == operation_in_messages) ... + // else if (operation_ == operation_in_negotiation) ... + // else ... + // + + else + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Unrecognized message received on proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unrecognized message received on proxy FD#" + << fd_ << ".\n"; + + return -1; + } + + } // while ((message = readBuffer_.getMessage(controlLength, dataLength)) != NULL) ... + + // + // Reset the read buffer. + // + + readBuffer_.fullReset(); + + // + // Give up if no data is readable. + // + + if (transport_ -> readable() == 0) + { + break; + } + + } // End of for (;;) ... + + return 1; +} + +int Proxy::handleControlFromProxy(const unsigned char *message) +{ + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Received message '" << DumpControl(*(message + 1)) + << "' at " << strMsTimestamp() << " with data ID#" + << (int) *(message + 2) << ".\n" << logofs_flush; + #endif + + T_channel_type channelType = channel_none; + + switch (*(message + 1)) + { + case code_switch_connection: + { + int channelId = *(message + 2); + + // + // If channel is invalid further messages will + // be ignored. The acknowledged shutdown of + // channels should prevent this. + // + + inputChannel_ = channelId; + + break; + } + case code_begin_congestion: + { + // + // Set the congestion state for the + // channel reported by the remote. + // + + int channelId = *(message + 2); + + if (channels_[channelId] != NULL) + { + congestions_[channelId] = 1; + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Received a begin congestion " + << "for channel id ID#" << channelId + << ".\n" << logofs_flush; + #endif + + if (channelId == agent_ && congestions_[agent_] != 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Forcing an update of the congestion " + << "counter with agent congested.\n" + << logofs_flush; + #endif + + statistics -> updateCongestion(-tokens_[token_control].remaining, + tokens_[token_control].limit); + } + } + #ifdef WARNING + else + { + *logofs << "Proxy: WARNING! Received a begin congestion " + << "for invalid channel id ID#" << channelId + << ".\n" << logofs_flush; + } + #endif + + break; + } + case code_end_congestion: + { + // + // Attend again to the channel. + // + + int channelId = *(message + 2); + + if (channels_[channelId] != NULL) + { + congestions_[channelId] = 0; + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Received an end congestion " + << "for channel id ID#" << channelId + << ".\n" << logofs_flush; + #endif + + if (channelId == agent_ && congestions_[agent_] != 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Forcing an update of the congestion " + << "counter with agent decongested.\n" + << logofs_flush; + #endif + + statistics -> updateCongestion(tokens_[token_control].remaining, + tokens_[token_control].limit); + } + } + #ifdef WARNING + else + { + *logofs << "Proxy: WARNING! Received an end congestion " + << "for invalid channel id ID#" << channelId + << ".\n" << logofs_flush; + } + #endif + + break; + } + case code_control_token_request: + { + T_proxy_token &token = tokens_[token_control]; + + if (handleTokenFromProxy(token, *(message + 2)) < 0) + { + return -1; + } + + break; + } + case code_split_token_request: + { + T_proxy_token &token = tokens_[token_split]; + + if (handleTokenFromProxy(token, *(message + 2)) < 0) + { + return -1; + } + + break; + } + case code_data_token_request: + { + T_proxy_token &token = tokens_[token_data]; + + if (handleTokenFromProxy(token, *(message + 2)) < 0) + { + return -1; + } + + break; + } + case code_control_token_reply: + { + T_proxy_token &token = tokens_[token_control]; + + if (handleTokenReplyFromProxy(token, *(message + 2)) < 0) + { + return -1; + } + + break; + } + case code_split_token_reply: + { + T_proxy_token &token = tokens_[token_split]; + + if (handleTokenReplyFromProxy(token, *(message + 2)) < 0) + { + return -1; + } + + break; + } + case code_data_token_reply: + { + T_proxy_token &token = tokens_[token_data]; + + if (handleTokenReplyFromProxy(token, *(message + 2)) < 0) + { + return -1; + } + + break; + } + case code_new_x_connection: + { + // + // Opening the channel is handled later. + // + + channelType = channel_x11; + + break; + } + case code_new_cups_connection: + { + channelType = channel_cups; + + break; + } + case code_new_aux_connection: + { + // + // Starting from version 1.5.0 we create real X + // connections for the keyboard channel. We need + // to refuse old auxiliary X connections because + // they would be unable to leverage the new fake + // authorization cookie. + // + + #ifdef WARNING + *logofs << "Proxy: WARNING! Can't open outdated auxiliary X " + << "channel for code " << *(message + 1) << ".\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Can't open outdated auxiliary X " + << "channel for code " << *(message + 1) << ".\n"; + + if (handleControl(code_drop_connection, *(message + 2)) < 0) + { + return -1; + } + + break; + } + case code_new_smb_connection: + { + channelType = channel_smb; + + break; + } + case code_new_media_connection: + { + channelType = channel_media; + + break; + } + case code_new_http_connection: + { + channelType = channel_http; + + break; + } + case code_new_font_connection: + { + channelType = channel_font; + + break; + } + case code_new_slave_connection: + { + channelType = channel_slave; + + break; + } + case code_drop_connection: + { + int channelId = *(message + 2); + + if (channelId >= 0 && channelId < CONNECTIONS_LIMIT && + channels_[channelId] != NULL) + { + handleDropFromProxy(channelId); + } + #ifdef WARNING + else + { + *logofs << "Proxy: WARNING! Received a drop message " + << "for invalid channel id ID#" << channelId + << ".\n" << logofs_flush; + } + #endif + + break; + } + case code_finish_connection: + { + int channelId = *(message + 2); + + if (channelId >= 0 && channelId < CONNECTIONS_LIMIT && + channels_[channelId] != NULL) + { + // + // Force the finish state on the channel. + // We can receive this message while in + // the read loop, so we only mark the + // channel for deletion. + // + + #ifdef TEST + *logofs << "Proxy: Received a finish message for FD#" + << getFd(channelId) << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + handleFinishFromProxy(channelId); + } + #ifdef WARNING + else + { + *logofs << "Proxy: WARNING! Received a finish message " + << "for invalid channel id ID#" << channelId + << ".\n" << logofs_flush; + } + #endif + + break; + } + case code_finish_listeners: + { + // + // This is from the main loop. + // + + #ifdef TEST + *logofs << "Proxy: Closing down all local listeners.\n" + << logofs_flush; + #endif + + CleanupListeners(); + + finish_ = 1; + + break; + } + case code_reset_request: + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Proxy reset not supported " + << "in this version.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Proxy reset not supported " + << "in this version.\n"; + + HandleCleanup(); + } + case code_shutdown_request: + { + // + // Time to rest in peace. + // + + shutdown_ = 1; + + break; + } + case code_load_request: + { + if (handleLoadFromProxy() < 0) + { + return -1; + } + + break; + } + case code_save_request: + { + // + // Don't abort the connection + // if can't write to disk. + // + + handleSaveFromProxy(); + + break; + } + case code_statistics_request: + { + int type = *(message + 2); + + if (handleStatisticsFromProxy(type) < 0) + { + return -1; + } + + break; + } + case code_statistics_reply: + { + operation_ = operation_in_statistics; + + break; + } + case code_alert_request: + { + HandleAlert(*(message + 2), 1); + + break; + } + case code_sync_request: + { + int channelId = *(message + 2); + + if (handleSyncFromProxy(channelId) < 0) + { + return -1; + } + + break; + } + case code_sync_reply: + { + // + // We are not the one that issued + // the request. + // + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: PANIC! Received an unexpected " + << "synchronization reply.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Received an unexpected " + << "synchronization reply.\n"; + + HandleCleanup(); + } + default: + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Received bad control message number " + << (unsigned int) *(message + 1) << " with attribute " + << (unsigned int) *(message + 2) << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Received bad control message number " + << (unsigned int) *(message + 1) << " with attribute " + << (unsigned int) *(message + 2) << ".\n"; + + HandleCleanup(); + } + + } // End of switch (*(message + 1)) ... + + if (channelType == channel_none) + { + return 1; + } + + // + // Handle the channel allocation that we + // left from the main switch case. + // + + int channelId = *(message + 2); + + // + // Check if the channel has been dropped. + // + + if (channels_[channelId] != NULL && + (channels_[channelId] -> getDrop() == 1 || + channels_[channelId] -> getClosing() == 1)) + { + #ifdef TEST + *logofs << "Proxy: Dropping the descriptor FD#" + << getFd(channelId) << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + handleDrop(channelId); + } + + // + // Check if the channel is in the valid + // range. + // + + int result = checkChannelMap(channelId); + + if (result >= 0) + { + result = handleNewConnectionFromProxy(channelType, channelId); + } + + if (result < 0) + { + // + // Realization of new channel failed. + // Send channel shutdown message to + // the peer proxy. + // + + if (handleControl(code_drop_connection, channelId) < 0) + { + return -1; + } + } + else + { + int fd = getFd(channelId); + + if (getReadable(fd) > 0) + { + #ifdef TEST + *logofs << "Proxy: Trying to read immediately " + << "from descriptor FD#" << fd << ".\n" + << logofs_flush; + #endif + + if (handleRead(fd) < 0) + { + return -1; + } + } + #ifdef TEST + *logofs << "Proxy: Nothing to read immediately " + << "from descriptor FD#" << fd << ".\n" + << logofs_flush; + #endif + } + + return 1; +} + +int Proxy::handleRead(int fd, const char *data, int size) +{ + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Handling data for connection on FD#" + << fd << ".\n" << logofs_flush; + #endif + + if (canRead(fd) == 0) + { + #if defined(TEST) || defined(INFO) + + if (getChannel(fd) < 0) + { + *logofs << "Proxy: PANIC! Can't read from invalid FD#" + << fd << ".\n" << logofs_flush; + + HandleCleanup(); + } + else + { + *logofs << "Proxy: WARNING! Read method called for FD#" + << fd << " but operation is not possible.\n" + << logofs_flush; + } + + #endif + + return 0; + } + + int channelId = getChannel(fd); + + // + // Let the channel object read all the new data from + // its file descriptor, isolate messages, compress + // those messages, and append the compressed form to + // the encode buffer. + // + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Reading messages from FD#" << fd + << " channel ID#" << channelId << ".\n" + << logofs_flush; + #endif + + int result = channels_[channelId] -> handleRead(encodeBuffer_, (const unsigned char *) data, + (unsigned int) size); + + // + // Even in the case of a failure, write the produced + // data to the proxy connection. To keep the stores + // synchronized, the remote side needs to decode any + // message encoded by this side, also if the X socket + // was closed in the meanwhile. If this is the case, + // the decompressed output will be silently discarded. + // + + if (result < 0) + { + #ifdef TEST + *logofs << "Proxy: Failed to read data from connection FD#" + << fd << " channel ID#" << channelId << ".\n" + << logofs_flush; + #endif + + if (handleFinish(channelId) < 0) + { + return -1; + } + } + + // + // Check if there are new splits or + // motion events to send. + // + + setSplitTimeout(channelId); + setMotionTimeout(channelId); + + return 1; +} + +int Proxy::handleEvents() +{ + #ifdef TEST + *logofs << "Proxy: Going to check the events on channels.\n" + << logofs_flush; + #endif + + // + // Check if we can safely write to the + // proxy link. + // + + int read = isTimeToRead(); + + // + // Loop on channels and send the pending + // events. We must copy the list because + // channels can be removed in the middle + // of the loop. + // + + T_list channelList = activeChannels_.copyList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] == NULL) + { + continue; + } + + // + // Check if we need to drop the channel. + // + + if (channels_[channelId] -> getDrop() == 1 || + channels_[channelId] -> getClosing() == 1) + { + #ifdef TEST + *logofs << "Proxy: Dropping the descriptor FD#" + << getFd(channelId) << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + if (handleDrop(channelId) < 0) + { + return -1; + } + + continue; + } + else if (channels_[channelId] -> getFinish() == 1) + { + #ifdef TEST + *logofs << "Proxy: Skipping finishing " + << "descriptor FD#" << getFd(channelId) + << " channel ID#" << channelId << ".\n" + << logofs_flush; + #endif + + continue; + } + + // + // If the proxy link or the channel is + // in congestion state, don't handle + // the further events. + // + + if (read == 0 || congestions_[channelId] == 1) + { + #ifdef TEST + + if (read == 0) + { + *logofs << "Proxy: Can't handle events for FD#" + << getFd(channelId) << " channel ID#" + << channelId << " with proxy not available.\n" + << logofs_flush; + } + else + { + *logofs << "Proxy: Can't handle events for FD#" + << getFd(channelId) << " channel ID#" + << channelId << " with channel congested.\n" + << logofs_flush; + } + + #endif + + continue; + } + + // + // Handle the timeouts on the channel + // operations. + // + + int result = 0; + + // + // Handle the motion events. + // + + if (result >= 0 && channels_[channelId] -> needMotion() == 1) + { + if (isTimeToMotion() == 1) + { + #if defined(TEST) || defined(INFO) || defined(FLUSH) + + *logofs << "Proxy: FLUSH! Motion timeout expired after " + << diffTimestamp(timeouts_.motionTs, getTimestamp()) + << " Ms.\n" << logofs_flush; + + #endif + + result = channels_[channelId] -> handleMotion(encodeBuffer_); + + #ifdef TEST + + if (result < 0) + { + *logofs << "Proxy: Failed to handle motion events for FD#" + << getFd(channelId) << " channel ID#" << channelId + << ".\n" << logofs_flush; + } + + #endif + + timeouts_.motionTs = nullTimestamp(); + + setMotionTimeout(channelId); + } + #if defined(TEST) || defined(INFO) + else if (isTimestamp(timeouts_.motionTs) == 1) + { + *logofs << "Proxy: Running with " + << diffTimestamp(timeouts_.motionTs, getTimestamp()) + << " Ms elapsed since the last motion.\n" + << logofs_flush; + } + #endif + } + + if (result >= 0 && channels_[channelId] -> needSplit() == 1) + { + // + // Check if it is time to send more splits + // and how many bytes are going to be sent. + // + + if (isTimeToSplit() == 1) + { + #if defined(TEST) || defined(INFO) || defined(SPLIT) + *logofs << "Proxy: SPLIT! Split timeout expired after " + << diffTimestamp(timeouts_.splitTs, getTimestamp()) + << " Ms.\n" << logofs_flush; + #endif + + #if defined(TEST) || defined(INFO) || defined(SPLIT) + + *logofs << "Proxy: SPLIT! Encoding splits for FD#" + << getFd(channelId) << " at " << strMsTimestamp() + << " with " << clientStore_ -> getSplitTotalStorageSize() + << " total bytes and " << control -> SplitDataPacketLimit + << " bytes " << "to write.\n" + << logofs_flush; + + #endif + + result = channels_[channelId] -> handleSplit(encodeBuffer_); + + #ifdef TEST + + if (result < 0) + { + *logofs << "Proxy: Failed to handle splits for FD#" + << getFd(channelId) << " channel ID#" << channelId + << ".\n" << logofs_flush; + } + + #endif + + timeouts_.splitTs = nullTimestamp(); + + setSplitTimeout(channelId); + } + #if defined(TEST) || defined(INFO) || defined(SPLIT) + else if (channels_[channelId] -> needSplit() == 1 && + isTimestamp(timeouts_.splitTs) == 0) + { + *logofs << "Proxy: SPLIT! WARNING! Channel for FD#" + << getFd(channelId) << " has split to send but " + << "there is no timeout.\n" << logofs_flush; + } + else if (isTimestamp(timeouts_.splitTs) == 1) + { + *logofs << "Proxy: SPLIT! Running with " + << diffTimestamp(timeouts_.splitTs, getTimestamp()) + << " Ms elapsed since the last split.\n" + << logofs_flush; + } + #endif + } + + if (result < 0) + { + #ifdef TEST + *logofs << "Proxy: Error handling events for FD#" + << getFd(channelId) << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + if (handleFinish(channelId) < 0) + { + return -1; + } + } + } + + return 1; +} + +int Proxy::handleFrame(T_frame_type type) +{ + // + // Write any outstanding control message, followed by the + // content of the encode buffer, to the proxy transport. + // + // This code assumes that the encode buffer data is at an + // offset several bytes from start of the buffer, so that + // the length header and any necessary control bytes can + // be inserted in front of the data already in the buffer. + // This is the easiest way to encapsulate header and data + // together in a single frame. + // + // The way framing is implemented is very efficient but + // inherently limited and does not allow for getting the + // best performance, especially when running over a fast + // link. Framing should be rewritten to include the length + // of the packets in a fixed size header and, possibly, + // to incapsulate the control messages and the channel's + // data in a pseudo X protocol message, so that the proxy + // itself would be treated like any other channel. + // + + #if defined(TEST) || defined(INFO) + + if (congestion_ == 1) + { + // + // This can happen because there may be control + // messages to send, like a proxy shutdown mes- + // sage or a statistics request. All the other + // cases should be considered an error. + // + + #ifdef WARNING + *logofs << "Proxy: WARNING! Data is to be sent while " + << "congestion is " << congestion_ << ".\n" + << logofs_flush; + #endif + } + + #endif + + // + // Check if there is any data available on + // the socket. Recent Linux kernels are very + // picky. They require that we read often or + // they assume that the process is non-inter- + // active. + // + + if (handleAsyncEvents() < 0) + { + return -1; + } + + // + // Check if this is a ping, not a data frame. + // + + if (type == frame_ping) + { + if (handleToken(frame_ping) < 0) + { + return -1; + } + } + + unsigned int dataLength = encodeBuffer_.getLength(); + + #ifdef DEBUG + *logofs << "Proxy: Data length is " << dataLength + << " control length is " << controlLength_ + << ".\n" << logofs_flush; + #endif + + if (dataLength > 0) + { + // + // If this is a generic channel we need + // to add the completion bits. Data can + // also have been encoded because of a + // statistics request, even if no output + // channel was currently selected. + // + + if (outputChannel_ != -1) + { + #if defined(TEST) || defined(INFO) + + if (channels_[outputChannel_] == NULL) + { + *logofs << "Proxy: PANIC! A new frame was requested " + << "but the channel is invalid.\n" + << logofs_flush; + + HandleCleanup(); + } + + #endif + + channels_[outputChannel_] -> handleCompletion(encodeBuffer_); + + dataLength = encodeBuffer_.getLength(); + } + } + else if (controlLength_ == 0) + { + #if defined(TEST) || defined(INFO) + + *logofs << "Proxy: PANIC! A new frame was requested " + << "but there is no data to write.\n" + << logofs_flush; + + HandleCleanup(); + + #endif + + return 0; + } + + #ifdef DEBUG + *logofs << "Proxy: Data length is now " << dataLength + << " control length is " << controlLength_ + << ".\n" << logofs_flush; + #endif + + // + // Check if this frame needs to carry a new + // token request. + // + + if (type == frame_data) + { + if (handleToken(frame_data) < 0) + { + return -1; + } + } + + #ifdef DEBUG + *logofs << "Proxy: Adding a new frame for the remote proxy.\n" + << logofs_flush; + #endif + + unsigned char temp[5]; + + unsigned int lengthLength = 0; + unsigned int shift = dataLength; + + while (shift) + { + temp[lengthLength++] = (unsigned char) (shift & 0x7f); + + shift >>= 7; + } + + unsigned char *data = encodeBuffer_.getData(); + + unsigned char *outputMessage = data - (controlLength_ + lengthLength); + + unsigned char *nextDest = outputMessage; + + for (int i = 0; i < controlLength_; i++) + { + *nextDest++ = controlCodes_[i]; + } + + for (int j = lengthLength - 1; j > 0; j--) + { + *nextDest++ = (temp[j] | 0x80); + } + + if (lengthLength) + { + *nextDest++ = temp[0]; + } + + unsigned int outputLength = dataLength + controlLength_ + lengthLength; + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Produced plain output for " << dataLength << "+" + << controlLength_ << "+" << lengthLength << " out of " + << outputLength << " bytes.\n" << logofs_flush; + #endif + + #if defined(TEST) || defined(INFO) || defined(FLUSH) || defined(TIME) + + T_timestamp nowTs = getTimestamp(); + + *logofs << "Proxy: FLUSH! Immediate with blocked " << transport_ -> + blocked() << " length " << transport_ -> length() + << " new " << outputLength << " flushable " << transport_ -> + flushable() << " tokens " << tokens_[token_control].remaining + << " after " << diffTimestamp(timeouts_.writeTs, nowTs) + << " Ms.\n" << logofs_flush; + + *logofs << "Proxy: FLUSH! Immediate flush to proxy FD#" << fd_ + << " of " << outputLength << " bytes at " << strMsTimestamp() + << " with priority " << priority_ << ".\n" << logofs_flush; + + *logofs << "Proxy: FLUSH! Current bitrate is " + << statistics -> getBitrateInShortFrame() << " with " + << statistics -> getBitrateInLongFrame() << " in the " + << "long frame and top " << statistics -> + getTopBitrate() << ".\n" << logofs_flush; + #endif + + statistics -> addWriteOut(); + + int result = transport_ -> write(write_immediate, outputMessage, outputLength); + + #ifdef TIME + + if (diffTimestamp(timeouts_.writeTs, nowTs) > 50) + { + *logofs << "Proxy: WARNING! TIME! Data written to proxy FD#" + << fd_ << " at " << strMsTimestamp() << " after " + << diffTimestamp(timeouts_.writeTs, nowTs) + << " Ms.\n" << logofs_flush; + } + + #endif + + #ifdef DUMP + *logofs << "Proxy: Sent " << outputLength << " bytes of data " + << "with checksum "; + + DumpChecksum(outputMessage, outputLength); + + *logofs << " on proxy FD#" << fd_ << ".\n" << logofs_flush; + #endif + + #ifdef DUMP + *logofs << "Proxy: Partial checksums are:\n"; + + DumpBlockChecksums(outputMessage, outputLength, 256); + + *logofs << logofs_flush; + #endif + + // + // Clean up the encode buffer and + // bring it to the initial size. + // + + encodeBuffer_.fullReset(); + + // + // Close the connection if we got + // an error. + // + + if (result < 0) + { + #ifdef TEST + *logofs << "Proxy: Failed write to proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return -1; + } + + // + // Account for the data frame and the + // framing overhead. + // + + if (dataLength > 0) + { + statistics -> addFrameOut(); + } + + statistics -> addFramingBits((controlLength_ + lengthLength) << 3); + + controlLength_ = 0; + + // + // Reset all buffers, counters and the + // priority flag. + // + + handleResetFlush(); + + // + // Check if more data became available + // after writing. + // + + if (handleAsyncEvents() < 0) + { + return -1; + } + + // + // Drain the proxy link if we are in + // congestion state. + // + // if (needDrain() == 1 && draining_ == 0) + // { + // if (handleDrain() < 0) + // { + // return -1; + // } + // } + // + + return result; +} + +int Proxy::handleFlush() +{ + // + // We can have data in the encode buffer or + // control bytes to send. In the case make + // up a new frame. + // + + if (encodeBuffer_.getLength() + controlLength_ > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Flushing data in the encode buffer.\n" + << logofs_flush; + #endif + + priority_ = 1; + + if (handleFrame(frame_data) < 0) + { + return -1; + } + } + + // + // Check if we have something to write. + // + + if (transport_ -> length() + transport_ -> flushable() == 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Nothing else to flush for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 0; + } + + #if defined(TEST) || defined(INFO) + + if (transport_ -> blocked() == 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Proxy descriptor FD#" << fd_ + << " has data to flush but the transport " + << "is not blocked.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Proxy descriptor FD#" << fd_ + << " has data to flush but the transport " + << "is not blocked.\n"; + + HandleCleanup(); + } + + #endif + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "Proxy: FLUSH! Deferred with blocked " << transport_ -> + blocked() << " length " << transport_ -> length() + << " flushable " << transport_ -> flushable() << " tokens " + << tokens_[token_control].remaining << ".\n" + << logofs_flush; + + *logofs << "Proxy: FLUSH! Deferred flush to proxy FD#" << fd_ + << " of " << transport_ -> length() + transport_ -> + flushable() << " bytes at " << strMsTimestamp() + << " with priority " << priority_ << ".\n" + << logofs_flush; + + *logofs << "Proxy: FLUSH! Current bitrate is " + << statistics -> getBitrateInShortFrame() << " with " + << statistics -> getBitrateInLongFrame() << " in the " + << "long frame and top " << statistics -> + getTopBitrate() << ".\n" << logofs_flush; + #endif + + statistics -> addWriteOut(); + + int result = transport_ -> flush(); + + if (result < 0) + { + return -1; + } + + // + // Reset the counters and update the + // timestamp of the last write. + // + + handleResetFlush(); + + return result; +} + +int Proxy::handleDrain() +{ + // + // If the proxy is run in the same process + // as SSH, we can't block or the program + // would not have a chance to read or write + // its data. + // + + if (control -> LinkEncrypted == 1) + { + return 0; + } + + if (needDrain() == 0 || draining_ == 1) + { + #if defined(TEST) || defined(INFO) + + if (draining_ == 1) + { + *logofs << "Proxy: WARNING! Already draining proxy FD#" + << fd_ << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + } + else + { + *logofs << "Proxy: WARNING! No need to drain proxy FD#" + << fd_ << " with congestion " << congestion_ + << " length " << transport_ -> length() + << " and blocked " << transport_ -> blocked() + << ".\n" << logofs_flush; + } + + #endif + + return 0; + } + + draining_ = 1; + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Going to drain the proxy FD#" << fd_ + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + int timeout = control -> PingTimeout / 2; + + T_timestamp startTs = getNewTimestamp(); + + T_timestamp nowTs = startTs; + + int remaining; + int result; + + // + // Keep draining the proxy socket while + // reading the incoming messages until + // the timeout is expired. + // + + for (;;) + { + remaining = timeout - diffTimestamp(startTs, nowTs); + + if (remaining <= 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Timeout raised while draining " + << "FD#" << fd_ << " at " << strMsTimestamp() + << " after " << diffTimestamp(startTs, nowTs) + << " Ms.\n" << logofs_flush; + #endif + + result = 0; + + goto ProxyDrainEnd; + } + + if (transport_ -> length() > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Trying to write to FD#" << fd_ + << " at " << strMsTimestamp() << " with length " + << transport_ -> length() << " and " + << remaining << " Ms remaining.\n" + << logofs_flush; + #endif + + result = transport_ -> drain(0, remaining); + + if (result == -1) + { + result = -1; + + goto ProxyDrainEnd; + } + else if (result == 0 && transport_ -> readable() > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Decoding more data from proxy FD#" + << fd_ << " at " << strMsTimestamp() << " with " + << transport_ -> length() << " bytes to write and " + << transport_ -> readable() << " readable.\n" + << logofs_flush; + #endif + + if (handleRead() < 0) + { + result = -1; + + goto ProxyDrainEnd; + } + } + #if defined(TEST) || defined(INFO) + else if (result == 1) + { + *logofs << "Proxy: Transport for proxy FD#" << fd_ + << " drained down to " << transport_ -> length() + << " bytes.\n" << logofs_flush; + } + #endif + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Waiting for more data from proxy " + << "FD#" << fd_ << " at " << strMsTimestamp() + << " with " << remaining << " Ms remaining.\n" + << logofs_flush; + #endif + + + result = transport_ -> wait(remaining); + + if (result == -1) + { + result = -1; + + goto ProxyDrainEnd; + } + else if (result > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Decoding more data from proxy FD#" + << fd_ << " at " << strMsTimestamp() << " with " + << transport_ -> readable() << " bytes readable.\n" + << logofs_flush; + #endif + + if (handleRead() < 0) + { + result = -1; + + goto ProxyDrainEnd; + } + } + } + + // + // Check if we finally got the tokens + // that would allow us to come out of + // the congestion state. + // + + if (needDrain() == 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Got decongestion for proxy FD#" + << fd_ << " at " << strMsTimestamp() << " after " + << diffTimestamp(startTs, getTimestamp()) + << " Ms.\n" << logofs_flush; + #endif + + result = 1; + + goto ProxyDrainEnd; + } + + nowTs = getNewTimestamp(); + } + +ProxyDrainEnd: + + draining_ = 0; + + return result; +} + +int Proxy::handleFlush(int fd) +{ + int channelId = getChannel(fd); + + if (channelId < 0 || channels_[channelId] == NULL) + { + #ifdef TEST + *logofs << "Proxy: WARNING! Skipping flush on invalid " + << "descriptor FD#" << fd << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (channels_[channelId] -> getFinish() == 1) + { + #ifdef TEST + *logofs << "Proxy: Skipping flush on finishing " + << "descriptor FD#" << fd << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + return 0; + } + + #ifdef TEST + *logofs << "Proxy: Going to flush FD#" << fd + << " with blocked " << transports_[channelId] -> blocked() + << " length " << transports_[channelId] -> length() + << ".\n" << logofs_flush; + #endif + + if (channels_[channelId] -> handleFlush() < 0) + { + #ifdef TEST + *logofs << "Proxy: Failed to flush data to FD#" + << getFd(channelId) << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + handleFinish(channelId); + + return -1; + } + + return 1; +} + +int Proxy::handleStatistics(int type, ostream *stream) +{ + if (stream == NULL || control -> EnableStatistics == 0) + { + #ifdef WARNING + *logofs << "Proxy: WARNING! Cannot produce statistics " + << " for proxy FD#" << fd_ << ". Invalid settings " + << "for statistics or stream.\n" << logofs_flush; + #endif + + return 0; + } + else if (currentStatistics_ != NULL) + { + // + // Need to update the stream pointer as the + // previous one could have been destroyed. + // + + #ifdef WARNING + *logofs << "Proxy: WARNING! Replacing stream while producing " + << "statistics in stream at " << currentStatistics_ + << " for proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + } + + currentStatistics_ = stream; + + // + // Get statistics of remote peer. + // + + if (handleControl(code_statistics_request, type) < 0) + { + return -1; + } + + return 1; +} + +int Proxy::handleStatisticsFromProxy(int type) +{ + if (needFlush() == 1) + { + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "Proxy: WARNING! Data for the previous " + << "channel ID#" << outputChannel_ + << " flushed in statistics.\n" + << logofs_flush; + #endif + + if (handleFrame(frame_data) < 0) + { + return -1; + } + } + + if (control -> EnableStatistics == 1) + { + // + // Allocate a buffer for the output. + // + + char *buffer = new char[STATISTICS_LENGTH]; + + *buffer = '\0'; + + if (control -> ProxyMode == proxy_client) + { + #ifdef TEST + *logofs << "Proxy: Producing " + << (type == TOTAL_STATS ? "total" : "partial") + << " client statistics for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + statistics -> getClientProtocolStats(type, buffer); + + statistics -> getClientOverallStats(type, buffer); + } + else + { + #ifdef TEST + *logofs << "Proxy: Producing " + << (type == TOTAL_STATS ? "total" : "partial") + << " server statistics for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + statistics -> getServerProtocolStats(type, buffer); + } + + if (type == PARTIAL_STATS) + { + statistics -> resetPartialStats(); + } + + unsigned int length = strlen((char *) buffer) + 1; + + encodeBuffer_.encodeValue(type, 8); + + encodeBuffer_.encodeValue(length, 32); + + #ifdef TEST + *logofs << "Proxy: Encoding " << length + << " bytes of statistics data for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + encodeBuffer_.encodeMemory((unsigned char *) buffer, length); + + // + // Account statistics data as framing bits. + // + + statistics -> addFramingBits(length << 3); + + delete [] buffer; + } + else + { + #ifdef WARNING + *logofs << "Proxy: WARNING! Got statistics request " + << "but local statistics are disabled.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Got statistics request " + << "but local statistics are disabled.\n"; + + type = NO_STATS; + + encodeBuffer_.encodeValue(type, 8); + + #ifdef TEST + *logofs << "Proxy: Sending error code to remote proxy on FD#" + << fd_ << ".\n" << logofs_flush; + #endif + } + + // + // The next write will flush the statistics + // data and the control message. + // + + if (handleControl(code_statistics_reply, type) < 0) + { + return -1; + } + + return 1; +} + +int Proxy::handleStatisticsFromProxy(const unsigned char *message, unsigned int length) +{ + if (currentStatistics_ == NULL) + { + #ifdef WARNING + *logofs << "Proxy: WARNING! Unexpected statistics data received " + << "from remote proxy on FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Unexpected statistics data received " + << "from remote proxy.\n"; + + return 0; + } + + // + // Allocate the decode buffer and at least + // the 'type' field to see if there was an + // error. + // + + DecodeBuffer decodeBuffer(message, length); + + unsigned int type; + + decodeBuffer.decodeValue(type, 8); + + if (type == NO_STATS) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Couldn't get statistics from remote " + << "proxy on FD#" << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Couldn't get statistics from remote proxy.\n"; + } + else if (type != TOTAL_STATS && type != PARTIAL_STATS) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Cannot produce statistics " + << "with qualifier '" << type << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Cannot produce statistics " + << "with qualifier '" << type << "'.\n"; + + return -1; + } + else + { + unsigned int size; + + decodeBuffer.decodeValue(size, 32); + + char *buffer = new char[STATISTICS_LENGTH]; + + *buffer = '\0'; + + if (control -> EnableStatistics == 1) + { + if (control -> ProxyMode == proxy_client) + { + #ifdef TEST + *logofs << "Proxy: Finalizing " + << (type == TOTAL_STATS ? "total" : "partial") + << " client statistics for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + statistics -> getClientCacheStats(type, buffer); + + #ifdef TEST + *logofs << "Proxy: Decoding " << size + << " bytes of statistics data for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size); + + statistics -> getClientProtocolStats(type, buffer); + + statistics -> getClientOverallStats(type, buffer); + } + else + { + #ifdef TEST + *logofs << "Proxy: Finalizing " + << (type == TOTAL_STATS ? "total" : "partial") + << " server statistics for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + statistics -> getServerCacheStats(type, buffer); + + statistics -> getServerProtocolStats(type, buffer); + + #ifdef TEST + *logofs << "Proxy: Decoding " << size + << " bytes of statistics data for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size); + } + + if (type == PARTIAL_STATS) + { + statistics -> resetPartialStats(); + } + + *currentStatistics_ << buffer; + + // + // Mark the end of text to help external parsing. + // + + *currentStatistics_ << '\4'; + + *currentStatistics_ << flush; + } + else + { + // + // It can be that statistics were enabled at the time + // we issued the request (otherwise we could not have + // set the stream), but now they have been disabled + // by user. We must decode statistics data if we want + // to keep the connection. + // + + #ifdef TEST + *logofs << "Proxy: Discarding " << size + << " bytes of statistics data for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size); + } + + delete [] buffer; + } + + currentStatistics_ = NULL; + + return 1; +} + +int Proxy::handleNegotiation(const unsigned char *message, unsigned int length) +{ + #ifdef PANIC + *logofs << "Proxy: PANIC! Writing data during proxy " + << "negotiation is not implemented.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Writing data during proxy " + << "negotiation is not implemented.\n"; + + return -1; +} + +int Proxy::handleNegotiationFromProxy(const unsigned char *message, unsigned int length) +{ + #ifdef PANIC + *logofs << "Proxy: PANIC! Reading data during proxy " + << "negotiation is not implemented.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Reading data during proxy " + << "negotiation is not implemented.\n"; + + return -1; +} + +int Proxy::handleAlert(int alert) +{ + if (handleControl(code_alert_request, alert) < 0) + { + return -1; + } + + return 1; +} + +int Proxy::handleCloseConnection(int clientFd) +{ + int channelId = getChannel(clientFd); + + if (channels_[channelId] != NULL && + channels_[channelId] -> getFinish() == 0) + { + #ifdef TEST + *logofs << "Proxy: Closing down the channel for FD#" + << clientFd << ".\n" << logofs_flush; + #endif + + if (handleFinish(channelId) < 0) + { + return -1; + } + + return 1; + } + + return 0; +} + +int Proxy::handleCloseAllXConnections() +{ + #ifdef TEST + *logofs << "Proxy: Closing down any remaining X channel.\n" + << logofs_flush; + #endif + + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] != NULL && + channels_[channelId] -> getType() == channel_x11 && + channels_[channelId] -> getFinish() == 0) + { + #ifdef TEST + *logofs << "Proxy: Closing down the channel for FD#" + << getFd(channelId) << ".\n" << logofs_flush; + #endif + + if (handleFinish(channelId) < 0) + { + return -1; + } + } + } + + return 1; +} + +int Proxy::handleCloseAllListeners() +{ + if (control -> isProtoStep7() == 1) + { + if (finish_ == 0) + { + #ifdef TEST + *logofs << "Proxy: Closing down all remote listeners.\n" + << logofs_flush; + #endif + + if (handleControl(code_finish_listeners) < 0) + { + return -1; + } + + finish_ = 1; + } + } + else + { + #ifdef TEST + *logofs << "Proxy: WARNING! Not sending unsupported " + << "'code_finish_listeners' message.\n" + << logofs_flush; + #endif + + finish_ = 1; + } + + return 1; +} + +void Proxy::handleResetAlert() +{ + if (alert_ != 0) + { + #ifdef TEST + *logofs << "Proxy: The proxy alert '" << alert_ + << "' was displaced.\n" << logofs_flush; + #endif + + alert_ = 0; + } + + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] != NULL) + { + channels_[channelId] -> handleResetAlert(); + } + } +} + +int Proxy::handleFinish(int channelId) +{ + // + // Send any outstanding encoded data and + // do any finalization needed on the + // channel. + // + + if (needFlush(channelId) == 1) + { + if (channels_[channelId] -> getFinish() == 1) + { + #ifdef WARNING + *logofs << "Proxy: WARNING! The finishing channel ID#" + << channelId << " has data to flush.\n" + << logofs_flush; + #endif + } + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "Proxy: WARNING! Flushing data for the " + << "finishing channel ID#" << channelId + << ".\n" << logofs_flush; + #endif + + if (handleFrame(frame_data) < 0) + { + return -1; + } + } + + // + // Reset the congestion state and the + // timeouts, if needed. + // + + congestions_[channelId] = 0; + + setSplitTimeout(channelId); + setMotionTimeout(channelId); + + if (channels_[channelId] -> getFinish() == 0) + { + channels_[channelId] -> handleFinish(); + + // + // Force a failure in the case somebody + // would try to read from the channel. + // + + shutdown(getFd(channelId), SHUT_RD); + + // + // If the failure was not originated by + // the remote, send a channel shutdown + // message. + // + + if (channels_[channelId] -> getClosing() == 0) + { + #ifdef TEST + *logofs << "Proxy: Finishing channel for FD#" + << getFd(channelId) << " channel ID#" + << channelId << " because of failure.\n" + << logofs_flush; + #endif + + if (handleControl(code_finish_connection, channelId) < 0) + { + return -1; + } + } + } + + return 1; +} + +int Proxy::handleFinishFromProxy(int channelId) +{ + // + // Check if this channel has pending + // data to send. + // + + if (needFlush(channelId) == 1) + { + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "Proxy: WARNING! Flushing data for the " + << "finishing channel ID#" << channelId + << ".\n" << logofs_flush; + #endif + + if (handleFrame(frame_data) < 0) + { + return -1; + } + } + + // + // Mark the channel. We will free its + // resources at the next loop and will + // send the drop message to the remote. + // + + if (channels_[channelId] -> getClosing() == 0) + { + #ifdef TEST + *logofs << "Proxy: Marking channel for FD#" + << getFd(channelId) << " channel ID#" + << channelId << " as closing.\n" + << logofs_flush; + #endif + + channels_[channelId] -> handleClosing(); + } + + if (channels_[channelId] -> getFinish() == 0) + { + #ifdef TEST + *logofs << "Proxy: Finishing channel for FD#" + << getFd(channelId) << " channel ID#" + << channelId << " because of proxy.\n" + << logofs_flush; + #endif + + channels_[channelId] -> handleFinish(); + } + + if (handleFinish(channelId) < 0) + { + return -1; + } + + return 1; +} + +int Proxy::handleDropFromProxy(int channelId) +{ + // + // Only mark the channel. + // + + #ifdef TEST + *logofs << "Proxy: Marking channel for FD#" + << getFd(channelId) << " channel ID#" + << channelId << " as being dropped.\n" + << logofs_flush; + #endif + + if (channels_[channelId] -> getDrop() == 0) + { + channels_[channelId] -> handleDrop(); + } + + return 1; +} + +// +// Close the channel and deallocate all its +// resources. +// + +int Proxy::handleDrop(int channelId) +{ + // + // Check if this channel has pending + // data to send. + // + + if (needFlush(channelId) == 1) + { + if (channels_[channelId] -> getFinish() == 1) + { + #ifdef WARNING + *logofs << "Proxy: WARNING! The dropping channel ID#" + << channelId << " has data to flush.\n" + << logofs_flush; + #endif + } + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "Proxy: WARNING! Flushing data for the " + << "dropping channel ID#" << channelId + << ".\n" << logofs_flush; + #endif + + if (handleFrame(frame_data) < 0) + { + return -1; + } + } + + #ifdef TEST + *logofs << "Proxy: Dropping channel for FD#" + << getFd(channelId) << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + if (channels_[channelId] -> getFinish() == 0) + { + #ifdef WARNING + *logofs << "Proxy: WARNING! The channel for FD#" + << getFd(channelId) << " channel ID#" + << channelId << " was not marked as " + << "finishing.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": The channel for FD#" + << getFd(channelId) << " channel ID#" + << channelId << " was not marked as " + << "finishing.\n"; + + channels_[channelId] -> handleFinish(); + } + + // + // Send the channel shutdown message + // to the peer proxy. + // + + if (channels_[channelId] -> getClosing() == 1) + { + if (handleControl(code_drop_connection, channelId) < 0) + { + return -1; + } + } + + // + // Get rid of the channel. + // + + if (channels_[channelId] -> getType() != channel_x11) + { + #ifdef TEST + *logofs << "Proxy: Closed connection to " + << getTypeName(channels_[channelId] -> getType()) + << " server.\n" << logofs_flush; + #endif + + cerr << "Info" << ": Closed connection to " + << getTypeName(channels_[channelId] -> getType()) + << " server.\n"; + } + + delete channels_[channelId]; + channels_[channelId] = NULL; + + cleanupChannelMap(channelId); + + // + // Get rid of the transport. + // + + deallocateTransport(channelId); + + congestions_[channelId] = 0; + + decreaseChannels(channelId); + + // + // Check if the channel was the + // one currently selected for + // output. + // + + if (outputChannel_ == channelId) + { + outputChannel_ = -1; + } + + return 1; +} + +// +// Send an empty message to the remote peer +// to verify if the link is alive and let +// the remote proxy detect a congestion. +// + +int Proxy::handlePing() +{ + T_timestamp nowTs = getTimestamp(); + + #if defined(DEBUG) || defined(PING) + + *logofs << "Proxy: Checking ping at " + << strMsTimestamp(nowTs) << logofs_flush; + + *logofs << " with last loop at " + << strMsTimestamp(timeouts_.loopTs) << ".\n" + << logofs_flush; + + *logofs << "Proxy: Last bytes in at " + << strMsTimestamp(timeouts_.readTs) << logofs_flush; + + *logofs << " last bytes out at " + << strMsTimestamp(timeouts_.writeTs) << ".\n" + << logofs_flush; + + *logofs << "Proxy: Last ping at " + << strMsTimestamp(timeouts_.pingTs) << ".\n" + << logofs_flush; + + #endif + + // + // Be sure we take into account any clock drift. This + // can be caused by the user changing the system timer + // or by small adjustments introduced by the operating + // system making the clock go backward. + // + + if (checkDiffTimestamp(timeouts_.loopTs, nowTs) == 0) + { + #ifdef WARNING + *logofs << "Proxy: WARNING! Detected drift in system " + << "timer. Resetting to current time.\n" + << logofs_flush; + #endif + + timeouts_.pingTs = nowTs; + timeouts_.readTs = nowTs; + timeouts_.writeTs = nowTs; + } + + // + // Check timestamp of last read from remote proxy. It can + // happen that we stayed in the main loop long enough to + // have idle timeout expired, for example if the proxy was + // stopped and restarted or because of an extremely high + // load of the system. In this case we don't complain if + // there is something new to read from the remote. + // + + int diffIn = diffTimestamp(timeouts_.readTs, nowTs); + + if (diffIn >= (control -> PingTimeout * 2) - + control -> LatencyTimeout) + { + // + // Force a read to detect whether the remote proxy + // aborted the connection. + // + + int result = handleRead(); + + if (result < 0) + { + #if defined(TEST) || defined(INFO) || defined(PING) + *logofs << "Proxy: WARNING! Detected shutdown waiting " + << "for the ping after " << diffIn / 1000 + << " seconds.\n" << logofs_flush; + #endif + + return -1; + } + else if (result > 0) + { + diffIn = diffTimestamp(timeouts_.readTs, nowTs); + + if (handleFlush() < 0) + { + return -1; + } + } + } + + if (diffIn >= (control -> PingTimeout * 2) - + control -> LatencyTimeout) + { + #if defined(TEST) || defined(INFO) || defined(PING) + *logofs << "Proxy: Detected congestion at " + << strMsTimestamp() << " with " << diffIn / 1000 + << " seconds since the last read.\n" + << logofs_flush; + #endif + + // + // There are two types of proxy congestion. The first, + // affecting the ability of the proxy to write the + // encoded data to the network, is controlled by the + // congestion_ flag. The flag is raised when no data + // is received from the remote proxy within a timeout. + // On the X client side, the flag is also raised when + // the proxy runs out of tokens. + // + + if (control -> ProxyMode == proxy_server) + { + // + // At X server side we must return to read data + // from the channels after a while, because we + // need to give a chance to the channel to read + // the key sequence CTRL+ALT+SHIFT+ESC. + // + + if (congestion_ == 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Forcibly entering congestion due to " + << "timeout with " << tokens_[token_control].remaining + << " tokens.\n" << logofs_flush; + #endif + + congestion_ = 1; + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Forcibly exiting congestion due to " + << "timeout with " << tokens_[token_control].remaining + << " tokens.\n" << logofs_flush; + #endif + + congestion_ = 0; + } + } + else + { + #if defined(TEST) || defined(INFO) + + if (congestion_ == 0) + { + *logofs << "Proxy: Entering congestion due to timeout " + << "with " << tokens_[token_control].remaining + << " tokens.\n" << logofs_flush; + } + + #endif + + congestion_ = 1; + } + + if (control -> ProxyTimeout > 0 && + diffIn >= (control -> ProxyTimeout - + control -> LatencyTimeout)) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! No data received from " + << "remote proxy on FD#" << fd_ << " within " + << (diffIn + control -> LatencyTimeout) / 1000 + << " seconds.\n" << logofs_flush; + #endif + + cerr << "Error" << ": No data received from remote " + << "proxy within " << (diffIn + control -> + LatencyTimeout) / 1000 << " seconds.\n"; + + HandleAbort(); + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: WARNING! No data received from " + << "remote proxy on FD#" << fd_ << " since " + << diffIn << " Ms.\n" << logofs_flush; + #endif + + if (control -> ProxyTimeout > 0 && + isTimestamp(timeouts_.alertTs) == 0 && + diffIn >= (control -> ProxyTimeout - + control -> LatencyTimeout) / 4) + { + // + // If we are in the middle of a shutdown + // procedure but the remote is not resp- + // onding, force the closure of the link. + // + + if (finish_ != 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! No response received from " + << "the remote proxy on FD#" << fd_ << " while " + << "waiting for the shutdown.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": No response received from remote " + << "proxy while waiting for the shutdown.\n"; + + HandleAbort(); + } + else + { + cerr << "Warning" << ": No data received from remote " + << "proxy within " << (diffIn + control -> + LatencyTimeout) / 1000 << " seconds.\n"; + + if (alert_ == 0) + { + if (control -> ProxyMode == proxy_client) + { + alert_ = CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT; + } + else + { + alert_ = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT; + } + + HandleAlert(alert_, 1); + } + + timeouts_.alertTs = nowTs; + } + } + } + } + + // + // Check if we need to update the congestion + // counter. + // + + int diffOut = diffTimestamp(timeouts_.writeTs, nowTs); + + if (agent_ != nothing && congestions_[agent_] == 0 && + statistics -> getCongestionInFrame() >= 1 && + diffOut >= (control -> IdleTimeout - + control -> LatencyTimeout * 5)) + { + #if defined(TEST) || defined(INFO) || defined(PING) + *logofs << "Proxy: Forcing an update of the " + << "congestion counter after timeout.\n" + << logofs_flush; + #endif + + statistics -> updateCongestion(tokens_[token_control].remaining, + tokens_[token_control].limit); + } + + // + // Send a new token if we didn't send any data to + // the remote for longer than the ping timeout. + // The client side sends a token, the server side + // responds with a token reply. + // + // VMWare virtual machines can have the system + // timer deadly broken. Try to send a ping regard- + // less we are the client or the server proxy to + // force a write by the remote. + // + + if (control -> ProxyMode == proxy_client || + diffIn >= (control -> PingTimeout * 4) - + control -> LatencyTimeout) + { + // + // We need to send a new ping even if we didn't + // receive anything from the remote within the + // ping timeout. The server side will respond + // to our ping, so we use the ping to force the + // remote end to send some data. + // + + if (diffIn >= (control -> PingTimeout - + control -> LatencyTimeout * 5) || + diffOut >= (control -> PingTimeout - + control -> LatencyTimeout * 5)) + { + int diffPing = diffTimestamp(timeouts_.pingTs, nowTs); + + if (diffPing < 0 || diffPing >= (control -> PingTimeout - + control -> LatencyTimeout * 5)) + { + #if defined(TEST) || defined(INFO) || defined(PING) + *logofs << "Proxy: Sending a new ping at " << strMsTimestamp() + << " with " << tokens_[token_control].remaining + << " tokens and elapsed in " << diffIn << " out " + << diffOut << " ping " << diffPing + << ".\n" << logofs_flush; + #endif + + if (handleFrame(frame_ping) < 0) + { + return -1; + } + + timeouts_.pingTs = nowTs; + } + #if defined(TEST) || defined(INFO) || defined(PING) + else + { + *logofs << "Proxy: Not sending a new ping with " + << "elapsed in " << diffIn << " out " + << diffOut << " ping " << diffPing + << ".\n" << logofs_flush; + } + #endif + } + } + + return 1; +} + +int Proxy::handleSyncFromProxy(int channelId) +{ + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: WARNING! Received a synchronization " + << "request from the remote proxy.\n" + << logofs_flush; + #endif + + if (handleControl(code_sync_reply, channelId) < 0) + { + return -1; + } + + return 1; +} + +int Proxy::handleResetStores() +{ + // + // Recreate the message stores. + // + + delete clientStore_; + delete serverStore_; + + clientStore_ = new ClientStore(compressor_); + serverStore_ = new ServerStore(compressor_); + + timeouts_.loadTs = nullTimestamp(); + + // + // Replace message stores in channels. + // + + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] != NULL) + { + if (channels_[channelId] -> setStores(clientStore_, serverStore_) < 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Failed to replace message stores in " + << "channel for FD#" << getFd(channelId) << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failed to replace message stores in " + << "channel for FD#" << getFd(channelId) << ".\n"; + + return -1; + } + #ifdef TEST + else + { + *logofs << "Proxy: Replaced message stores in channel " + << "for FD#" << getFd(channelId) << ".\n" + << logofs_flush; + } + #endif + } + } + + return 1; +} + +int Proxy::handleResetPersistentCache() +{ + char *fullName = new char[strlen(control -> PersistentCachePath) + + strlen(control -> PersistentCacheName) + 2]; + + strcpy(fullName, control -> PersistentCachePath); + strcat(fullName, "/"); + strcat(fullName, control -> PersistentCacheName); + + #ifdef TEST + *logofs << "Proxy: Going to remove persistent cache file '" + << fullName << "'\n" << logofs_flush; + #endif + + unlink(fullName); + + delete [] fullName; + + delete [] control -> PersistentCacheName; + + control -> PersistentCacheName = NULL; + + return 1; +} + +void Proxy::handleResetFlush() +{ + #ifdef TEST + *logofs << "Proxy: Going to reset flush counters " + << "for proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + // + // Reset the proxy priority flag. + // + + priority_ = 0; + + // + // Restore buffers to their initial + // size. + // + + transport_ -> partialReset(); + + // + // Update the timestamp of the last + // write operation performed on the + // socket. + // + + timeouts_.writeTs = getTimestamp(); +} + +int Proxy::handleFinish() +{ + // + // Reset the timestamps to give the proxy + // another chance to show the 'no response' + // dialog if the shutdown message doesn't + // come in time. + // + + timeouts_.readTs = getTimestamp(); + + timeouts_.alertTs = nullTimestamp(); + + finish_ = 1; + + return 1; +} + +int Proxy::handleShutdown() +{ + // + // Send shutdown message to remote proxy. + // + + shutdown_ = 1; + + handleControl(code_shutdown_request); + + #ifdef TEST + *logofs << "Proxy: Starting shutdown procedure " + << "for proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + // + // Ensure that all the data accumulated + // in the transport buffer is flushed + // to the network layer. + // + + for (int i = 0; i < 100; i++) + { + if (canFlush() == 1) + { + handleFlush(); + } + else + { + break; + } + + usleep(100000); + } + + // + // Now wait for the network layers to + // consume all the data. + // + + for (int i = 0; i < 100; i++) + { + if (transport_ -> queued() <= 0) + { + break; + } + + usleep(100000); + } + + // + // Give time to the remote end to read + // the shutdown message and close the + // connection. + // + + transport_ -> wait(10000); + + #ifdef TEST + *logofs << "Proxy: Ending shutdown procedure " + << "for proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + return 1; +} + +int Proxy::handleChannelConfiguration() +{ + if (activeChannels_.getSize() == 0) + { + #ifdef TEST + *logofs << "Proxy: Going to initialize the static " + << "members in channels for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + Channel::setReferences(); + + ClientChannel::setReferences(); + ServerChannel::setReferences(); + + GenericChannel::setReferences(); + } + + return 1; +} + +int Proxy::handleSocketConfiguration() +{ + // + // Set linger mode on proxy to correctly + // get shutdown notification. + // + + SetLingerTimeout(fd_, 30); + + // + // Set keep-alive on socket so that if remote link + // terminates abnormally (as killed hard or because + // of a power-off) process will get a SIGPIPE. In + // practice this is useless as proxies already ping + // each other every few seconds. + // + + if (control -> OptionProxyKeepAlive == 1) + { + SetKeepAlive(fd_); + } + + // + // Set 'priority' flag at TCP layer for path + // proxy-to-proxy. Look at IPTOS_LOWDELAY in + // man 7 ip. + // + + if (control -> OptionProxyLowDelay == 1) + { + SetLowDelay(fd_); + } + + // + // Update size of TCP send and receive buffers. + // + + if (control -> OptionProxySendBuffer != -1) + { + SetSendBuffer(fd_, control -> OptionProxySendBuffer); + } + + if (control -> OptionProxyReceiveBuffer != -1) + { + SetReceiveBuffer(fd_, control -> OptionProxyReceiveBuffer); + } + + // + // Update TCP_NODELAY settings. Note that on old Linux + // kernels turning off the Nagle algorithm didn't work + // when proxy was run through a PPP link. Trying to do + // so caused the kernel to stop delivering data to us + // if a serious network congestion was encountered. + // + + if (control -> ProxyMode == proxy_client) + { + if (control -> OptionProxyClientNoDelay != -1) + { + SetNoDelay(fd_, control -> OptionProxyClientNoDelay); + } + } + else + { + if (control -> OptionProxyServerNoDelay != -1) + { + SetNoDelay(fd_, control -> OptionProxyServerNoDelay); + } + } + + return 1; +} + +int Proxy::handleLinkConfiguration() +{ + #ifdef TEST + *logofs << "Proxy: Propagating parameters to " + << "channels' read buffers.\n" + << logofs_flush; + #endif + + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] != NULL) + { + channels_[channelId] -> handleConfiguration(); + } + } + + #ifdef TEST + *logofs << "Proxy: Propagating parameters to " + << "proxy buffers.\n" + << logofs_flush; + #endif + + readBuffer_.setSize(control -> ProxyInitialReadSize, + control -> ProxyMaximumBufferSize); + + encodeBuffer_.setSize(control -> TransportProxyBufferSize, + control -> TransportProxyBufferThreshold, + control -> TransportMaximumBufferSize); + + transport_ -> setSize(control -> TransportProxyBufferSize, + control -> TransportProxyBufferThreshold, + control -> TransportMaximumBufferSize); + + #ifdef TEST + *logofs << "Proxy: Configuring the proxy timeouts.\n" + << logofs_flush; + #endif + + timeouts_.split = control -> SplitTimeout; + timeouts_.motion = control -> MotionTimeout; + + #ifdef TEST + *logofs << "Proxy: Configuring the proxy tokens.\n" + << logofs_flush; + #endif + + tokens_[token_control].size = control -> TokenSize; + tokens_[token_control].limit = control -> TokenLimit; + + if (tokens_[token_control].limit < 1) + { + tokens_[token_control].limit = 1; + } + + #if defined(TEST) || defined(INFO) || defined(LIMIT) + *logofs << "Proxy: TOKEN! LIMIT! Setting token [" + << DumpToken(token_control) << "] size to " + << tokens_[token_control].size << " and limit to " + << tokens_[token_control].limit << ".\n" + << logofs_flush; + #endif + + tokens_[token_split].size = control -> TokenSize; + tokens_[token_split].limit = control -> TokenLimit / 2; + + if (tokens_[token_split].limit < 1) + { + tokens_[token_split].limit = 1; + } + + #if defined(TEST) || defined(INFO) || defined(LIMIT) + *logofs << "Proxy: TOKEN! LIMIT! Setting token [" + << DumpToken(token_split) << "] size to " + << tokens_[token_split].size << " and limit to " + << tokens_[token_split].limit << ".\n" + << logofs_flush; + #endif + + tokens_[token_data].size = control -> TokenSize; + tokens_[token_data].limit = control -> TokenLimit / 4; + + if (tokens_[token_data].limit < 1) + { + tokens_[token_data].limit = 1; + } + + #if defined(TEST) || defined(INFO) || defined(LIMIT) + *logofs << "Proxy: TOKEN! LIMIT! Setting token [" + << DumpToken(token_data) << "] size to " + << tokens_[token_data].size << " and limit to " + << tokens_[token_data].limit << ".\n" + << logofs_flush; + #endif + + for (int i = token_control; i <= token_data; i++) + { + tokens_[i].remaining = tokens_[i].limit; + } + + #if defined(TEST) || defined(INFO) || defined(LIMIT) + *logofs << "Proxy: LIMIT! Using client bitrate " + << "limit " << control -> ClientBitrateLimit + << " server bitrate limit " << control -> + ServerBitrateLimit << " with local limit " + << control -> LocalBitrateLimit << ".\n" + << logofs_flush; + #endif + + // + // Set the other parameters based on + // the token size. + // + + int base = control -> TokenSize; + + control -> SplitDataThreshold = base * 4; + control -> SplitDataPacketLimit = base / 2; + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: LIMIT! Setting split data threshold " + << "to " << control -> SplitDataThreshold + << " split packet limit to " << control -> + SplitDataPacketLimit << " with base " + << base << ".\n" << logofs_flush; + #endif + + // + // Set the number of bytes read from the + // data channels at each loop. This will + // basically determine the maximum band- + // width available for the generic chan- + // nels. + // + + control -> GenericInitialReadSize = base / 2; + control -> GenericMaximumBufferSize = base / 2; + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: LIMIT! Setting generic channel " + << "initial read size to " << control -> + GenericInitialReadSize << " maximum read " + << "size to " << control -> GenericMaximumBufferSize + << " with base " << base << ".\n" + << logofs_flush; + #endif + + return 1; +} + +int Proxy::handleCacheConfiguration() +{ + #ifdef TEST + *logofs << "Proxy: Configuring cache according to pack parameters.\n" + << logofs_flush; + #endif + + // + // Further adjust the cache parameters. If + // packing of the images is enabled, reduce + // the size available for plain images. + // + + if (control -> SessionMode == session_agent) + { + if (control -> PackMethod != NO_PACK) + { + clientStore_ -> getRequestStore(X_PutImage) -> + cacheThreshold = PUTIMAGE_CACHE_THRESHOLD_IF_PACKED; + + clientStore_ -> getRequestStore(X_PutImage) -> + cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED; + } + } + + // + // If this is a shadow session increase the + // size of the image cache. + // + + if (control -> SessionMode == session_shadow) + { + if (control -> PackMethod != NO_PACK) + { + clientStore_ -> getRequestStore(X_NXPutPackedImage) -> + cacheThreshold = PUTPACKEDIMAGE_CACHE_THRESHOLD_IF_PACKED_SHADOW; + + clientStore_ -> getRequestStore(X_NXPutPackedImage) -> + cacheLowerThreshold = PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED_SHADOW; + } + else + { + clientStore_ -> getRequestStore(X_PutImage) -> + cacheThreshold = PUTIMAGE_CACHE_THRESHOLD_IF_SHADOW; + + clientStore_ -> getRequestStore(X_PutImage) -> + cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_SHADOW; + } + } + + return 1; +} + +int Proxy::handleSaveStores() +{ + // + // Save content of stores on disk. + // + + char *cacheToAdopt = NULL; + + if (control -> PersistentCacheEnableSave) + { + #ifdef TEST + *logofs << "Proxy: Going to save content of client store.\n" + << logofs_flush; + #endif + + cacheToAdopt = handleSaveAllStores(control -> PersistentCachePath); + } + #ifdef TEST + else + { + if (control -> ProxyMode == proxy_client) + { + *logofs << "Proxy: Saving persistent cache to disk disabled.\n" + << logofs_flush; + } + else + { + *logofs << "Proxy: PANIC! Protocol violation in command save.\n" + << logofs_flush; + + cerr << "Error" << ": Protocol violation in command save.\n"; + + HandleCleanup(); + } + } + #endif + + if (cacheToAdopt != NULL) + { + // + // Do we have a cache already? + // + + if (control -> PersistentCacheName != NULL) + { + // + // Check if old and new cache are the same. + // In this case don't remove the old cache. + // + + if (strcasecmp(control -> PersistentCacheName, cacheToAdopt) != 0) + { + handleResetPersistentCache(); + } + + delete [] control -> PersistentCacheName; + } + + #ifdef TEST + *logofs << "Proxy: Setting current persistent cache file to '" + << cacheToAdopt << "'\n" << logofs_flush; + #endif + + control -> PersistentCacheName = cacheToAdopt; + + return 1; + } + #ifdef TEST + else + { + *logofs << "Proxy: No cache file produced from message stores.\n" + << logofs_flush; + } + #endif + + // + // It can be that we didn't generate a new cache + // because store was too small or persistent cache + // was disabled. This is not an error. + // + + return 0; +} + +int Proxy::handleLoadStores() +{ + // + // Restore the content of the client store + // from disk if a valid cache was negotiated + // at session startup. + // + + if (control -> PersistentCacheEnableLoad == 1 && + control -> PersistentCachePath != NULL && + control -> PersistentCacheName != NULL) + { + #ifdef TEST + *logofs << "Proxy: Going to load content of client store.\n" + << logofs_flush; + #endif + + // + // Returns the same string passed as name of + // the cache, or NULL if it was not possible + // to load the cache from disk. + // + + if (handleLoadAllStores(control -> PersistentCachePath, + control -> PersistentCacheName) == NULL) + { + // + // The corrupted cache should have been + // removed from disk. Get rid of the + // reference so we don't try to delete + // it once again. + // + + if (control -> PersistentCacheName != NULL) + { + delete [] control -> PersistentCacheName; + } + + control -> PersistentCacheName = NULL; + + return -1; + } + + // + // Set timestamp of last time cache + // was loaded from data on disk. + // + + timeouts_.loadTs = getTimestamp(); + + return 1; + } + #ifdef TEST + else + { + if (control -> ProxyMode == proxy_client) + { + *logofs << "Proxy: Loading of cache disabled or no cache file selected.\n" + << logofs_flush; + } + else + { + *logofs << "Proxy: PANIC! Protocol violation in command load.\n" + << logofs_flush; + + cerr << "Error" << ": Protocol violation in command load.\n"; + + HandleCleanup(); + } + } + #endif + + return 0; +} + +int Proxy::handleControl(T_proxy_code code, int data) +{ + // + // Send the given control messages + // to the remote proxy. + // + + #if defined(TEST) || defined(INFO) + + if (data != -1) + { + if (code == code_control_token_reply || + code == code_split_token_reply || + code == code_data_token_reply) + { + *logofs << "Proxy: TOKEN! Sending message '" << DumpControl(code) + << "' at " << strMsTimestamp() << " with count " + << data << ".\n" << logofs_flush; + } + else + { + *logofs << "Proxy: Sending message '" << DumpControl(code) + << "' at " << strMsTimestamp() << " with data ID#" + << data << ".\n" << logofs_flush; + } + } + else + { + *logofs << "Proxy: Sending message '" << DumpControl(code) + << "' at " << strMsTimestamp() << ".\n" + << logofs_flush; + } + + #endif + + // + // Add the control message and see if the + // data has to be flushed immediately. + // + + if (addControlCodes(code, data) < 0) + { + return -1; + } + + switch (code) + { + // + // Append the first data read from the opened + // channel to the control code. + // + + case code_new_x_connection: + case code_new_cups_connection: + case code_new_aux_connection: + case code_new_smb_connection: + case code_new_media_connection: + case code_new_http_connection: + case code_new_font_connection: + case code_new_slave_connection: + + // + // Do we send the token reply immediately? + // The control messages are put at the begin- + // ning of the of the encode buffer, so we may + // reply to multiple tokens before having the + // chance of handling the actual frame data. + // On the other hand, the sooner we reply, the + // sooner the remote proxy is restarted. + // + + case code_control_token_reply: + case code_split_token_reply: + case code_data_token_reply: + { + break; + } + + // + // Also send the congestion control codes + // immediately. + // + // case code_begin_congestion: + // case code_end_congestion: + // + + default: + { + priority_ = 1; + + break; + } + } + + if (priority_ == 1) + { + if (handleFrame(frame_data) < 0) + { + return -1; + } + } + + return 1; +} + +int Proxy::handleSwitch(int channelId) +{ + // + // If data is for a different channel than last + // selected for output, prepend to the data the + // new channel id. + // + + #ifdef DEBUG + *logofs << "Proxy: Requested a switch with " + << "current channel ID#" << outputChannel_ + << " new channel ID#" << channelId << ".\n" + << logofs_flush; + #endif + + if (channelId != outputChannel_) + { + if (needFlush() == 1) + { + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "Proxy: WARNING! Flushing data for the " + << "previous channel ID#" << outputChannel_ + << ".\n" << logofs_flush; + #endif + + if (handleFrame(frame_data) < 0) + { + return -1; + } + } + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Sending message '" + << DumpControl(code_switch_connection) << "' at " + << strMsTimestamp() << " with FD#" << getFd(channelId) + << " channel ID#" << channelId << ".\n" + << logofs_flush; + #endif + + if (addControlCodes(code_switch_connection, channelId) < 0) + { + return -1; + } + + outputChannel_ = channelId; + } + + return 1; +} + +int Proxy::addTokenCodes(T_proxy_token &token) +{ + #if defined(TEST) || defined(INFO) || defined(TOKEN) + *logofs << "Proxy: TOKEN! Sending token [" + << DumpToken(token.type) << "] with " + << token.bytes << " bytes accumulated size " + << token.size << " and " << token.remaining + << " available.\n" << logofs_flush; + #endif + + // + // Give a 'weight' to the token. The tokens + // remaining can become negative if we sent + // a packet that was exceptionally big. + // + + int count = 0; + + if (control -> isProtoStep7() == 1) + { + count = token.bytes / token.size; + + if (count > 255) + { + count = 255; + } + } + + // + // Force a count of 1, for example + // if this is a ping. + // + + if (count < 1) + { + count = 1; + + token.bytes = 0; + } + else + { + // + // Let the next token account for the + // remaining bytes. + // + + token.bytes %= token.size; + } + + #if defined(TEST) || defined(INFO) || defined(TOKEN) + *logofs << "Proxy: Sending message '" + << DumpControl(token.request) << "' at " + << strMsTimestamp() << " with count " << count + << ".\n" << logofs_flush; + #endif + + controlCodes_[controlLength_++] = 0; + controlCodes_[controlLength_++] = (unsigned char) token.request; + controlCodes_[controlLength_++] = (unsigned char) count; + + statistics -> addFrameOut(); + + token.remaining -= count; + + return 1; +} + +int Proxy::handleToken(T_frame_type type) +{ + #if defined(TEST) || defined(INFO) || defined(TOKEN) + *logofs << "Proxy: TOKEN! Checking tokens with " + << "frame type ["; + + *logofs << (type == frame_ping ? "frame_ping" : "frame_data"); + + *logofs << "] with stream ratio " << statistics -> + getStreamRatio() << ".\n" << logofs_flush; + #endif + + if (type == frame_data) + { + if (control -> isProtoStep7() == 1) + { + // + // Send a distinct token for each data type. + // We don't want to slow down the sending of + // the X events, X replies and split confir- + // mation events on the X server side, so + // take care only of the generic data token. + // + + if (control -> ProxyMode == proxy_client) + { + statistics -> updateControlToken(tokens_[token_control].bytes); + + if (tokens_[token_control].bytes > tokens_[token_control].size) + { + if (addTokenCodes(tokens_[token_control]) < 0) + { + return -1; + } + + #if defined(TEST) || defined(INFO) || defined(TOKEN) + + T_proxy_token &token = tokens_[token_control]; + + *logofs << "Proxy: TOKEN! Token class [" + << DumpToken(token.type) << "] has now " + << token.bytes << " bytes accumulated and " + << token.remaining << " tokens remaining.\n" + << logofs_flush; + #endif + } + + statistics -> updateSplitToken(tokens_[token_split].bytes); + + if (tokens_[token_split].bytes > tokens_[token_split].size) + { + if (addTokenCodes(tokens_[token_split]) < 0) + { + return -1; + } + + #if defined(TEST) || defined(INFO) || defined(TOKEN) + + T_proxy_token &token = tokens_[token_split]; + + *logofs << "Proxy: TOKEN! Token class [" + << DumpToken(token.type) << "] has now " + << token.bytes << " bytes accumulated and " + << token.remaining << " tokens remaining.\n" + << logofs_flush; + #endif + } + } + + statistics -> updateDataToken(tokens_[token_data].bytes); + + if (tokens_[token_data].bytes > tokens_[token_data].size) + { + if (addTokenCodes(tokens_[token_data]) < 0) + { + return -1; + } + + #if defined(TEST) || defined(INFO) || defined(TOKEN) + + T_proxy_token &token = tokens_[token_data]; + + *logofs << "Proxy: TOKEN! Token class [" + << DumpToken(token.type) << "] has now " + << token.bytes << " bytes accumulated and " + << token.remaining << " tokens remaining.\n" + << logofs_flush; + #endif + } + } + else + { + // + // Sum everything to the control token. + // + + if (control -> ProxyMode == proxy_client) + { + statistics -> updateControlToken(tokens_[token_control].bytes); + statistics -> updateSplitToken(tokens_[token_control].bytes); + statistics -> updateDataToken(tokens_[token_control].bytes); + + if (tokens_[token_control].bytes > tokens_[token_control].size) + { + if (addTokenCodes(tokens_[token_control]) < 0) + { + return -1; + } + + #if defined(TEST) || defined(INFO) || defined(TOKEN) + + T_proxy_token &token = tokens_[token_control]; + + *logofs << "Proxy: TOKEN! Token class [" + << DumpToken(token.type) << "] has now " + << token.bytes << " bytes accumulated and " + << token.remaining << " tokens remaining.\n" + << logofs_flush; + #endif + } + } + } + } + else + { + if (addTokenCodes(tokens_[token_control]) < 0) + { + return -1; + } + + // + // Reset all counters on a ping. + // + + tokens_[token_control].bytes = 0; + tokens_[token_split].bytes = 0; + tokens_[token_data].bytes = 0; + + #if defined(TEST) || defined(INFO) || defined(TOKEN) + + T_proxy_token &token = tokens_[token_control]; + + *logofs << "Proxy: TOKEN! Token class [" + << DumpToken(token.type) << "] has now " + << token.bytes << " bytes accumulated and " + << token.remaining << " tokens remaining.\n" + << logofs_flush; + #endif + } + + // + // Check if we have entered in + // congestion state. + // + + if (congestion_ == 0 && + tokens_[token_control].remaining <= 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Entering congestion with " + << tokens_[token_control].remaining + << " tokens remaining.\n" << logofs_flush; + #endif + + congestion_ = 1; + } + + statistics -> updateCongestion(tokens_[token_control].remaining, + tokens_[token_control].limit); + + return 1; +} + +int Proxy::handleTokenFromProxy(T_proxy_token &token, int count) +{ + #if defined(TEST) || defined(INFO) || defined(TOKEN) + *logofs << "Proxy: TOKEN! Received token [" + << DumpToken(token.type) << "] request at " + << strMsTimestamp() << " with count " + << count << ".\n" << logofs_flush; + #endif + + if (control -> isProtoStep7() == 0) + { + if (control -> ProxyMode == proxy_client || + token.request != code_control_token_request) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Invalid token request received from remote.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid token request received from remote.\n"; + + HandleCleanup(); + } + } + + // + // Add our token reply. + // + + if (handleControl(token.reply, count) < 0) + { + return -1; + } + + return 1; +} + +int Proxy::handleTokenReplyFromProxy(T_proxy_token &token, int count) +{ + #if defined(TEST) || defined(INFO) || defined(TOKEN) + *logofs << "Proxy: TOKEN! Received token [" + << DumpToken(token.type) << "] reply at " + << strMsTimestamp() << " with count " << count + << ".\n" << logofs_flush; + #endif + + // + // Increment the available tokens. + // + + if (control -> isProtoStep7() == 0) + { + if (token.reply != code_control_token_reply) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Invalid token reply received from remote.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Invalid token reply received from remote.\n"; + + HandleCleanup(); + } + + count = 1; + } + + token.remaining += count; + + if (token.remaining > token.limit) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Token overflow handling messages.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Token overflow handling messages.\n"; + + HandleCleanup(); + } + + #if defined(TEST) || defined(INFO) || defined(TOKEN) + *logofs << "Proxy: TOKEN! Token class [" + << DumpToken(token.type) << "] has now " << token.bytes + << " bytes accumulated and " << token.remaining + << " tokens remaining.\n" << logofs_flush; + #endif + + // + // Check if we can jump out of the + // congestion state. + // + + if (congestion_ == 1 && + tokens_[token_control].remaining > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Exiting congestion with " + << tokens_[token_control].remaining + << " tokens remaining.\n" << logofs_flush; + #endif + + congestion_ = 0; + } + + statistics -> updateCongestion(tokens_[token_control].remaining, + tokens_[token_control].limit); + + return 1; +} + +void Proxy::handleFailOnSave(const char *fullName, const char *failContext) const +{ + #ifdef WARNING + *logofs << "Proxy: WARNING! Error saving stores to cache file " + << "in context [" << failContext << "].\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Error saving stores to cache file " + << "in context [" << failContext << "].\n"; + + #ifdef WARNING + *logofs << "Proxy: WARNING! Removing invalid cache '" + << fullName << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Removing invalid cache '" + << fullName << "'.\n"; + + unlink(fullName); +} + +void Proxy::handleFailOnLoad(const char *fullName, const char *failContext) const +{ + #ifdef WARNING + *logofs << "Proxy: WARNING! Error loading stores from cache file " + << "in context [" << failContext << "].\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Error loading stores from cache file " + << "in context [" << failContext << "].\n"; + + #ifdef WARNING + *logofs << "Proxy: WARNING! Removing invalid cache '" + << fullName << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Removing invalid cache '" + << fullName << "'.\n"; + + unlink(fullName); +} + +int Proxy::handleSaveVersion(unsigned char *buffer, int &major, + int &minor, int &patch) const +{ + if (control -> isProtoStep8() == 1) + { + major = 3; + minor = 0; + patch = 0; + } + else if (control -> isProtoStep7() == 1) + { + major = 2; + minor = 0; + patch = 0; + } + else + { + major = 1; + minor = 4; + patch = 0; + } + + *(buffer + 0) = major; + *(buffer + 1) = minor; + + PutUINT(patch, buffer + 2, storeBigEndian()); + + return 1; +} + +int Proxy::handleLoadVersion(const unsigned char *buffer, int &major, + int &minor, int &patch) const +{ + major = *(buffer + 0); + minor = *(buffer + 1); + + patch = GetUINT(buffer + 2, storeBigEndian()); + + // + // Force the proxy to discard the + // incompatible caches. + // + + if (control -> isProtoStep8() == 1) + { + if (major < 3) + { + return -1; + } + } + else if (control -> isProtoStep7() == 1) + { + if (major < 2) + { + return -1; + } + } + else + { + if (major != 1 && minor != 4) + { + return -1; + } + } + + return 1; +} + +char *Proxy::handleSaveAllStores(const char *savePath) const +{ + int cumulativeSize = MessageStore::getCumulativeTotalStorageSize(); + + if (cumulativeSize < control -> PersistentCacheThreshold) + { + #ifdef TEST + *logofs << "Proxy: Cache not saved as size is " + << cumulativeSize << " with threshold set to " + << control -> PersistentCacheThreshold + << ".\n" << logofs_flush; + #endif + + return NULL; + } + else if (savePath == NULL) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! No name provided for save path.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": No name provided for save path.\n"; + + return NULL; + } + + #ifdef TEST + *logofs << "Proxy: Going to save content of message stores.\n" + << logofs_flush; + #endif + + // + // Our parent process is likely going to terminate. + // Until we finish saving cache we must ignore its + // SIGIPE. + // + + DisableSignals(); + + ofstream *cachefs = NULL; + + md5_state_t *md5StateStream = NULL; + md5_byte_t *md5DigestStream = NULL; + + md5_state_t *md5StateClient = NULL; + md5_byte_t *md5DigestClient = NULL; + + char *tempName = NULL; + + char md5String[MD5_LENGTH * 2 + 2]; + + char fullName[strlen(savePath) + MD5_LENGTH * 2 + 4]; + + if (control -> ProxyMode == proxy_client) + { + tempName = tempnam(savePath, "Z-C-"); + } + else + { + tempName = tempnam(savePath, "Z-S-"); + } + + // + // Change the mask to make the file only + // readable by the user, then restore the + // old mask. + // + + mode_t fileMode = umask(0077); + + cachefs = new ofstream(tempName, ios::out | ios::binary); + + umask(fileMode); + + if (tempName == NULL || cachefs == NULL) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Can't create temporary file in '" + << savePath << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't create temporary file in '" + << savePath << "'.\n"; + + if (tempName != NULL) + { + free(tempName); + } + + if (cachefs != NULL) + { + delete cachefs; + } + + EnableSignals(); + + return NULL; + } + + md5StateStream = new md5_state_t(); + md5DigestStream = new md5_byte_t[MD5_LENGTH]; + + md5_init(md5StateStream); + + // + // First write the proxy version. + // + + unsigned char version[4]; + + int major; + int minor; + int patch; + + handleSaveVersion(version, major, minor, patch); + + #ifdef TEST + *logofs << "Proxy: Saving cache using version '" + << major << "." << minor << "." << patch + << "'.\n" << logofs_flush; + #endif + + if (PutData(cachefs, version, 4) < 0) + { + handleFailOnSave(tempName, "A"); + + delete cachefs; + + delete md5StateStream; + delete [] md5DigestStream; + + free(tempName); + + EnableSignals(); + + return NULL; + } + + // + // Make space for the calculated MD5 so we + // can later rewind the file and write it + // at this position. + // + + if (PutData(cachefs, md5DigestStream, MD5_LENGTH) < 0) + { + handleFailOnSave(tempName, "B"); + + delete cachefs; + + delete md5StateStream; + delete [] md5DigestStream; + + free(tempName); + + EnableSignals(); + + return NULL; + } + + md5StateClient = new md5_state_t(); + md5DigestClient = new md5_byte_t[MD5_LENGTH]; + + md5_init(md5StateClient); + + #ifdef DUMP + + ofstream *cacheDump = NULL; + + ofstream *tempfs = (ofstream*) logofs; + + char cacheDumpName[DEFAULT_STRING_LENGTH]; + + if (control -> ProxyMode == proxy_client) + { + snprintf(cacheDumpName, DEFAULT_STRING_LENGTH - 1, + "%s/client-cache-dump", control -> TempPath); + } + else + { + snprintf(cacheDumpName, DEFAULT_STRING_LENGTH - 1, + "%s/server-cache-dump", control -> TempPath); + } + + *(cacheDumpName + DEFAULT_STRING_LENGTH - 1) = '\0'; + + mode_t fileMode = umask(0077); + + cacheDump = new ofstream(cacheDumpName, ios::out); + + umask(fileMode); + + logofs = cacheDump; + + #endif + + // + // Use the virtual method of the concrete proxy class. + // + + int allSaved = handleSaveAllStores(cachefs, md5StateStream, md5StateClient); + + #ifdef DUMP + + logofs = tempfs; + + delete cacheDump; + + #endif + + if (allSaved == 0) + { + handleFailOnSave(tempName, "C"); + + delete cachefs; + + delete md5StateStream; + delete [] md5DigestStream; + + delete md5StateClient; + delete [] md5DigestClient; + + free(tempName); + + EnableSignals(); + + return NULL; + } + + md5_finish(md5StateClient, md5DigestClient); + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + sprintf(md5String + (i * 2), "%02X", md5DigestClient[i]); + } + + strcpy(fullName, (control -> ProxyMode == proxy_client) ? "C-" : "S-"); + + strcat(fullName, md5String); + + md5_append(md5StateStream, (const md5_byte_t *) fullName, strlen(fullName)); + md5_finish(md5StateStream, md5DigestStream); + + // + // Go to the beginning of file plus + // the integer where we wrote our + // proxy version. + // + + cachefs -> seekp(4); + + if (PutData(cachefs, md5DigestStream, MD5_LENGTH) < 0) + { + handleFailOnSave(tempName, "D"); + + delete cachefs; + + delete md5StateStream; + delete [] md5DigestStream; + + delete md5StateClient; + delete [] md5DigestClient; + + free(tempName); + + EnableSignals(); + + return NULL; + } + + delete cachefs; + + // + // Copy the resulting cache name without + // the path so that we can return it to + // the caller. + // + + char *cacheName = new char[MD5_LENGTH * 2 + 4]; + + strcpy(cacheName, fullName); + + // + // Add the path to the full name and move + // the cache into the path. + // + + strcpy(fullName, savePath); + strcat(fullName, (control -> ProxyMode == proxy_client) ? "/C-" : "/S-"); + strcat(fullName, md5String); + + #ifdef TEST + *logofs << "Proxy: Renaming cache file from '" + << tempName << "' to '" << fullName + << "'.\n" << logofs_flush; + #endif + + rename(tempName, fullName); + + delete md5StateStream; + delete [] md5DigestStream; + + delete md5StateClient; + delete [] md5DigestClient; + + free(tempName); + + // + // Restore the original handlers. + // + + EnableSignals(); + + #ifdef TEST + *logofs << "Proxy: Successfully saved cache file '" + << cacheName << "'.\n" << logofs_flush; + #endif + + // + // This must be enabled only for test + // because it requires that client and + // server reside on the same machine. + // + + if (control -> PersistentCacheCheckOnShutdown == 1 && + control -> ProxyMode == proxy_server) + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: MATCH! Checking if the file '" + << fullName << "' matches a client cache.\n" + << logofs_flush; + #endif + + strcpy(fullName, savePath); + strcat(fullName, "/C-"); + strcat(fullName, md5String); + + struct stat fileStat; + + if (stat(fullName, &fileStat) != 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Can't find a client cache " + << "with name '" << fullName << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't find a client cache " + << "with name '" << fullName << "'.\n"; + + HandleShutdown(); + } + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: MATCH! Client cache '" << fullName + << "' matches the local cache.\n" + << logofs_flush; + #endif + } + + return cacheName; +} + +const char *Proxy::handleLoadAllStores(const char *loadPath, const char *loadName) const +{ + #ifdef TEST + *logofs << "Proxy: Going to load content of message stores.\n" + << logofs_flush; + #endif + + // + // Until we finish loading cache we + // must at least ignore any SIGIPE. + // + + DisableSignals(); + + if (loadPath == NULL || loadName == NULL) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! No path or no file name provided for cache to restore.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": No path or no file name provided for cache to restore.\n"; + + EnableSignals(); + + return NULL; + } + else if (strlen(loadName) != MD5_LENGTH * 2 + 2) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Bad file name provided for cache to restore.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Bad file name provided for cache to restore.\n"; + + EnableSignals(); + + return NULL; + } + + istream *cachefs = NULL; + char md5String[(MD5_LENGTH * 2) + 2]; + md5_byte_t md5FromFile[MD5_LENGTH]; + + char *cacheName = new char[strlen(loadPath) + strlen(loadName) + 3]; + + strcpy(cacheName, loadPath); + strcat(cacheName, "/"); + strcat(cacheName, loadName); + + #ifdef TEST + *logofs << "Proxy: Name of cache file is '" + << cacheName << "'.\n" << logofs_flush; + #endif + + cachefs = new ifstream(cacheName, ios::in | ios::binary); + + unsigned char version[4]; + + if (cachefs == NULL || GetData(cachefs, version, 4) < 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Can't read cache file '" + << cacheName << "'.\n" << logofs_flush;; + #endif + + handleFailOnLoad(cacheName, "A"); + + delete cachefs; + + delete [] cacheName; + + EnableSignals(); + + return NULL; + } + + int major; + int minor; + int patch; + + if (handleLoadVersion(version, major, minor, patch) < 0) + { + #ifdef PANIC + *logofs << "Proxy: WARNING! Incompatible version '" + << major << "." << minor << "." << patch + << "' in cache file '" << cacheName + << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Incompatible version '" + << major << "." << minor << "." << patch + << "' in cache file '" << cacheName + << "'.\n" << logofs_flush; + + if (control -> ProxyMode == proxy_server) + { + handleFailOnLoad(cacheName, "B"); + } + else + { + // + // Simply remove the cache file. + // + + unlink(cacheName); + } + + delete cachefs; + + delete [] cacheName; + + EnableSignals(); + + return NULL; + } + + #ifdef TEST + *logofs << "Proxy: Reading from cache file version '" + << major << "." << minor << "." << patch + << "'.\n" << logofs_flush; + #endif + + if (GetData(cachefs, md5FromFile, MD5_LENGTH) < 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! No checksum in cache file '" + << loadName << "'.\n" << logofs_flush; + #endif + + handleFailOnLoad(cacheName, "C"); + + delete cachefs; + + delete [] cacheName; + + EnableSignals(); + + return NULL; + } + + md5_state_t *md5StateStream = NULL; + md5_byte_t *md5DigestStream = NULL; + + md5StateStream = new md5_state_t(); + md5DigestStream = new md5_byte_t[MD5_LENGTH]; + + md5_init(md5StateStream); + + // + // Use the virtual method of the proxy class. + // + + if (handleLoadAllStores(cachefs, md5StateStream) < 0) + { + handleFailOnLoad(cacheName, "D"); + + delete cachefs; + + delete md5StateStream; + delete [] md5DigestStream; + + delete [] cacheName; + + EnableSignals(); + + return NULL; + } + + md5_append(md5StateStream, (const md5_byte_t *) loadName, strlen(loadName)); + md5_finish(md5StateStream, md5DigestStream); + + for (int i = 0; i < MD5_LENGTH; i++) + { + if (md5DigestStream[i] != md5FromFile[i]) + { + #ifdef PANIC + + *logofs << "Proxy: PANIC! Bad checksum for cache file '" + << cacheName << "'.\n" << logofs_flush; + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + sprintf(md5String + (i * 2), "%02X", md5FromFile[i]); + } + + *logofs << "Proxy: PANIC! Saved checksum is '" + << md5String << "'.\n" << logofs_flush; + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + sprintf(md5String + (i * 2),"%02X", md5DigestStream[i]); + } + + *logofs << "Proxy: PANIC! Calculated checksum is '" + << md5String << "'.\n" << logofs_flush; + + #endif + + handleFailOnLoad(cacheName, "E"); + + delete cachefs; + + delete md5StateStream; + delete [] md5DigestStream; + + delete [] cacheName; + + EnableSignals(); + + return NULL; + } + } + + delete cachefs; + + delete md5StateStream; + delete [] md5DigestStream; + + delete [] cacheName; + + // + // Restore the original handlers. + // + + EnableSignals(); + + #ifdef TEST + *logofs << "Proxy: Successfully loaded cache file '" + << loadName << "'.\n" << logofs_flush; + #endif + + // + // Return the string provided by caller. + // + + return loadName; +} + +int Proxy::allocateChannelMap(int fd) +{ + // + // We assume that the fd is lower than + // the maximum allowed number. This is + // checked at the time the connection + // is accepted. + // + + if (fd < 0 || fd >= CONNECTIONS_LIMIT) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Internal error allocating " + << "new channel with FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Internal error allocating " + << "new channel with FD#" << fd_ << ".\n"; + + HandleCleanup(); + } + + for (int channelId = 0; + channelId < CONNECTIONS_LIMIT; + channelId++) + { + if (checkLocalChannelMap(channelId) == 1 && + fdMap_[channelId] == -1) + { + fdMap_[channelId] = fd; + channelMap_[fd] = channelId; + + #ifdef TEST + *logofs << "Proxy: Allocated new channel ID#" + << channelId << " with FD#" << fd << ".\n" + << logofs_flush; + #endif + + return channelId; + } + } + + // + // No available channel is remaining. + // + + #ifdef TEST + *logofs << "Proxy: WARNING! Can't allocate a new channel " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return -1; +} + +int Proxy::checkChannelMap(int channelId) +{ + // + // To be acceptable, the channel id must + // be an id that is not possible to use + // to allocate channels at this side. + // + + if (checkLocalChannelMap(channelId) == 1) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Can't open a new channel " + << "with invalid ID#" << channelId << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't open a new channel " + << "with invalid ID#" << channelId << ".\n"; + + return -1; + } + else if (channels_[channelId] != NULL) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Can't open a new channel " + << "over an existing ID#" << channelId + << " with FD#" << getFd(channelId) + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't open a new channel " + << "over an existing ID#" << channelId + << " with FD#" << getFd(channelId) + << ".\n"; + + return -1; + } + + return 1; +} + +int Proxy::assignChannelMap(int channelId, int fd) +{ + // + // We assume that the fd is lower than + // the maximum allowed number. This is + // checked at the time the connection + // is accepted. + // + + if (channelId < 0 || channelId >= CONNECTIONS_LIMIT || + fd < 0 || fd >= CONNECTIONS_LIMIT) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Internal error assigning " + << "new channel with FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Internal error assigning " + << "new channel with FD#" << fd_ << ".\n"; + + HandleCleanup(); + } + + fdMap_[channelId] = fd; + channelMap_[fd] = channelId; + + return 1; +} + +void Proxy::cleanupChannelMap(int channelId) +{ + int fd = fdMap_[channelId]; + + if (fd != -1) + { + fdMap_[channelId] = -1; + channelMap_[fd] = -1; + } +} + +int Proxy::addControlCodes(T_proxy_code code, int data) +{ + // + // Flush the encode buffer plus all the outstanding + // control codes if there is not enough space for + // the new control message. We need to ensure that + // there are further bytes available, in the case + // we will need to add more token control messages. + // + + if (controlLength_ + 3 > CONTROL_CODES_THRESHOLD) + { + #ifdef WARNING + *logofs << "Proxy: WARNING! Flushing control messages " + << "while sending code '" << DumpControl(code) + << "'.\n" << logofs_flush; + #endif + + if (handleFlush() < 0) + { + return -1; + } + } + + controlCodes_[controlLength_++] = 0; + controlCodes_[controlLength_++] = (unsigned char) code; + controlCodes_[controlLength_++] = (unsigned char) (data == -1 ? 0 : data); + + // + // Account for the control frame. + // + + statistics -> addFrameOut(); + + return 1; +} + +void Proxy::setSplitTimeout(int channelId) +{ + int needed = channels_[channelId] -> needSplit(); + + if (needed != isTimestamp(timeouts_.splitTs)) + { + if (needed == 1) + { + #if defined(TEST) || defined(INFO) || defined(SPLIT) + *logofs << "Proxy: SPLIT! Allocating split timestamp at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + timeouts_.splitTs = getTimestamp(); + } + else + { + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] != NULL && + channels_[channelId] -> needSplit() == 1) + { + #ifdef TEST + *logofs << "Proxy: SPLIT! Channel for FD#" + << getFd(channelId) << " still needs splits.\n" + << logofs_flush; + #endif + + return; + } + } + + #if defined(TEST) || defined(INFO) || defined(SPLIT) + *logofs << "Proxy: SPLIT! Resetting split timestamp at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + timeouts_.splitTs = nullTimestamp(); + } + } +} + +void Proxy::setMotionTimeout(int channelId) +{ + int needed = channels_[channelId] -> needMotion(); + + if (needed != isTimestamp(timeouts_.motionTs)) + { + if (channels_[channelId] -> needMotion() == 1) + { + #if defined(TEST) || defined(INFO) || defined(SPLIT) + *logofs << "Proxy: Allocating motion timestamp at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + timeouts_.motionTs = getTimestamp(); + } + else + { + T_list &channelList = activeChannels_.getList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] != NULL && + channels_[channelId] -> needMotion() == 1) + { + #ifdef TEST + *logofs << "Proxy: SPLIT! Channel for FD#" + << getFd(channelId) << " still needs motions.\n" + << logofs_flush; + #endif + + return; + } + } + + #if defined(TEST) || defined(INFO) || defined(SPLIT) + *logofs << "Proxy: Resetting motion timestamp at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + timeouts_.motionTs = nullTimestamp(); + } + } +} + +void Proxy::increaseChannels(int channelId) +{ + #ifdef TEST + *logofs << "Proxy: Adding channel " << channelId + << " to the list of active channels.\n" + << logofs_flush; + #endif + + activeChannels_.add(channelId); + + #ifdef TEST + *logofs << "Proxy: There are " << activeChannels_.getSize() + << " allocated channels for proxy FD#" << fd_ + << ".\n" << logofs_flush; + #endif +} + +void Proxy::decreaseChannels(int channelId) +{ + #ifdef TEST + *logofs << "Proxy: Removing channel " << channelId + << " from the list of active channels.\n" + << logofs_flush; + #endif + + activeChannels_.remove(channelId); + + #ifdef TEST + *logofs << "Proxy: There are " << activeChannels_.getSize() + << " allocated channels for proxy FD#" << fd_ + << ".\n" << logofs_flush; + #endif +} + +int Proxy::allocateTransport(int channelFd, int channelId) +{ + if (transports_[channelId] == NULL) + { + transports_[channelId] = new Transport(channelFd); + + if (transports_[channelId] == NULL) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Can't allocate transport for " + << "channel id " << channelId << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate transport for " + << "channel id " << channelId << ".\n"; + + return -1; + } + } + else if (transports_[channelId] -> + getType() != transport_agent) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Transport for channel id " + << channelId << " should be null.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Transport for channel id " + << channelId << " should be null.\n"; + + return -1; + } + + return 1; +} + +int Proxy::deallocateTransport(int channelId) +{ + // + // Transport for the agent connection + // is passed from the outside when + // creating the channel. + // + + if (transports_[channelId] -> + getType() != transport_agent) + { + delete transports_[channelId]; + } + + transports_[channelId] = NULL; + + return 1; +} + +int Proxy::handleNewGenericConnection(int clientFd, T_channel_type type, const char *label) +{ + int channelId = allocateChannelMap(clientFd); + + if (channelId == -1) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Maximum mumber of available " + << "channels exceeded.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Maximum mumber of available " + << "channels exceeded.\n"; + + return -1; + } + + #ifdef TEST + *logofs << "Proxy: Channel for " << label << " descriptor " + << "FD#" << clientFd << " mapped to ID#" + << channelId << ".\n" + << logofs_flush; + #endif + + // + // Turn queuing off for path server-to-proxy. + // + + SetNoDelay(clientFd, 1); + + if (allocateTransport(clientFd, channelId) < 0) + { + return -1; + } + + switch (type) + { + case channel_cups: + { + channels_[channelId] = new CupsChannel(transports_[channelId], compressor_); + + break; + } + case channel_smb: + { + channels_[channelId] = new SmbChannel(transports_[channelId], compressor_); + + break; + } + case channel_media: + { + channels_[channelId] = new MediaChannel(transports_[channelId], compressor_); + + break; + } + case channel_http: + { + channels_[channelId] = new HttpChannel(transports_[channelId], compressor_); + + break; + } + case channel_font: + { + channels_[channelId] = new FontChannel(transports_[channelId], compressor_); + + break; + } + default: + { + channels_[channelId] = new SlaveChannel(transports_[channelId], compressor_); + + break; + } + } + + if (channels_[channelId] == NULL) + { + deallocateTransport(channelId); + + return -1; + } + + #ifdef TEST + *logofs << "Proxy: Accepted new connection to " + << label << " server.\n" << logofs_flush; + #endif + + cerr << "Info" << ": Accepted new connection to " + << label << " server.\n"; + + increaseChannels(channelId); + + switch (type) + { + case channel_cups: + { + if (handleControl(code_new_cups_connection, channelId) < 0) + { + return -1; + } + + break; + } + case channel_smb: + { + if (handleControl(code_new_smb_connection, channelId) < 0) + { + return -1; + } + + break; + } + case channel_media: + { + if (handleControl(code_new_media_connection, channelId) < 0) + { + return -1; + } + + break; + } + case channel_http: + { + if (handleControl(code_new_http_connection, channelId) < 0) + { + return -1; + } + + break; + } + case channel_font: + { + if (handleControl(code_new_font_connection, channelId) < 0) + { + return -1; + } + + break; + } + default: + { + if (handleControl(code_new_slave_connection, channelId) < 0) + { + return -1; + } + + break; + } + } + + channels_[channelId] -> handleConfiguration(); + + return 1; +} + +int Proxy::handleNewSlaveConnection(int clientFd) +{ + if (control -> isProtoStep7() == 1) + { + return handleNewGenericConnection(clientFd, channel_slave, "slave"); + } + else + { + #ifdef TEST + *logofs << "Proxy: WARNING! Not sending unsupported " + << "'code_new_slave_connection' message.\n" + << logofs_flush; + #endif + + return -1; + } +} + +int Proxy::handleNewGenericConnectionFromProxy(int channelId, T_channel_type type, + const char *hostname, int port, const char *label) +{ + if (port <= 0) + { + // + // This happens when user has disabled + // forwarding of the specific service. + // + + #ifdef WARNING + *logofs << "Proxy: WARNING! Refusing attempted connection " + << "to " << label << " server.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Refusing attempted connection " + << "to " << label << " server.\n"; + + return -1; + } + + const char *serverHost = hostname; + int serverAddrFamily = AF_INET; + sockaddr *serverAddr = NULL; + unsigned int serverAddrLength = 0; + + #ifdef TEST + *logofs << "Proxy: Connecting to " << label + << " server '" << serverHost << "' on TCP port '" + << port << "'.\n" << logofs_flush; + #endif + + int serverIPAddr = GetHostAddress(serverHost); + + if (serverIPAddr == 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Unknown " << label + << " server host '" << serverHost << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Unknown " << label + << " server host '" << serverHost + << "'.\n"; + + return -1; + } + + sockaddr_in *serverAddrTCP = new sockaddr_in; + + serverAddrTCP -> sin_family = AF_INET; + serverAddrTCP -> sin_port = htons(port); + serverAddrTCP -> sin_addr.s_addr = serverIPAddr; + + serverAddr = (sockaddr *) serverAddrTCP; + serverAddrLength = sizeof(sockaddr_in); + + // + // Connect to the requested server. + // + + int serverFd = socket(serverAddrFamily, SOCK_STREAM, PF_UNSPEC); + + if (serverFd < 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Call to socket failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + delete serverAddrTCP; + + return -1; + } + else if (connect(serverFd, serverAddr, serverAddrLength) < 0) + { + #ifdef WARNING + *logofs << "Proxy: WARNING! Connection to " << label + << " server '" << serverHost << ":" << port + << "' failed with error '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Connection to " << label + << " server '" << serverHost << ":" << port + << "' failed with error '" << ESTR() << "'.\n"; + + close(serverFd); + + delete serverAddrTCP; + + return -1; + } + + delete serverAddrTCP; + + if (handlePostConnectionFromProxy(channelId, serverFd, type, label) < 0) + { + return -1; + } + + #ifdef TEST + *logofs << "Proxy: Forwarded new connection to " + << label << " server on port '" << port + << "'.\n" << logofs_flush; + #endif + + cerr << "Info" << ": Forwarded new connection to " + << label << " server on port '" << port + << "'.\n"; + + return 1; +} + +int Proxy::handleNewGenericConnectionFromProxy(int channelId, T_channel_type type, + const char *hostname, const char *path, const char *label) +{ + if (path == NULL || *path == '\0' ) + { + // + // This happens when user has disabled + // forwarding of the specific service. + // + + #ifdef WARNING + *logofs << "Proxy: WARNING! Refusing attempted connection " + << "to " << label << " server.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Refusing attempted connection " + << "to " << label << " server.\n"; + + return -1; + } + + sockaddr_un serverAddrUnix; + + unsigned int serverAddrLength = sizeof(sockaddr_un); + + int serverAddrFamily = AF_UNIX; + + serverAddrUnix.sun_family = AF_UNIX; + + const int serverAddrNameLength = 108; + + strncpy(serverAddrUnix.sun_path, path, serverAddrNameLength); + + *(serverAddrUnix.sun_path + serverAddrNameLength - 1) = '\0'; + + #ifdef TEST + *logofs << "Proxy: Connecting to " << label << " server " + << "on Unix port '" << path << "'.\n" << logofs_flush; + #endif + + // + // Connect to the requested server. + // + + int serverFd = socket(serverAddrFamily, SOCK_STREAM, PF_UNSPEC); + + if (serverFd < 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! Call to socket failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + return -1; + } + else if (connect(serverFd, (sockaddr *) &serverAddrUnix, serverAddrLength) < 0) + { + #ifdef WARNING + *logofs << "Proxy: WARNING! Connection to " << label + << " server on Unix port '" << path << "' failed " + << "with error " << EGET() << ", '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Connection to " << label + << " server on Unix port '" << path << "' failed " + << "with error " << EGET() << ", '" << ESTR() << "'.\n"; + + close(serverFd); + + return -1; + } + + if (handlePostConnectionFromProxy(channelId, serverFd, type, label) < 0) + { + return -1; + } + + #ifdef TEST + *logofs << "Proxy: Forwarded new connection to " + << label << " server on Unix port '" << path + << "'.\n" << logofs_flush; + #endif + + cerr << "Info" << ": Forwarded new connection to " + << label << " server on Unix port '" << path + << "'.\n"; + + return 1; +} + +int Proxy::handleNewSlaveConnectionFromProxy(int channelId) +{ + // + // Implementation is incomplete. Opening a + // slave channel should let the proxy fork + // a new client and pass to it the channel + // descriptors. For now we make the channel + // fail immediately. + // + // #include <fcntl.h> + // #include <sys/types.h> + // #include <sys/stat.h> + // + // char *slaveServer = "/dev/null"; + // + // #ifdef TEST + // *logofs << "Proxy: Opening file '" << slaveServer + // << "'.\n" << logofs_flush; + // #endif + // + // int serverFd = open(slaveServer, O_RDWR); + // + // if (handlePostConnectionFromProxy(channelId, serverFd, channel_slave, "slave") < 0) + // { + // return -1; + // } + // + + #ifdef WARNING + *logofs << "Proxy: Refusing new slave connection for " + << "channel ID#" << channelId << "\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Refusing new slave connection for " + << "channel ID#" << channelId << "\n"; + + return -1; +} + +int Proxy::handlePostConnectionFromProxy(int channelId, int serverFd, + T_channel_type type, const char *label) +{ + // + // Turn queuing off for path proxy-to-server. + // + + SetNoDelay(serverFd, 1); + + assignChannelMap(channelId, serverFd); + + #ifdef TEST + *logofs << "Proxy: Descriptor FD#" << serverFd + << " mapped to channel ID#" << channelId << ".\n" + << logofs_flush; + #endif + + if (allocateTransport(serverFd, channelId) < 0) + { + return -1; + } + + switch (type) + { + case channel_cups: + { + channels_[channelId] = new CupsChannel(transports_[channelId], compressor_); + break; + } + case channel_smb: + { + channels_[channelId] = new SmbChannel(transports_[channelId], compressor_); + + break; + } + case channel_media: + { + channels_[channelId] = new MediaChannel(transports_[channelId], compressor_); + + break; + } + case channel_http: + { + channels_[channelId] = new HttpChannel(transports_[channelId], compressor_); + + break; + } + case channel_font: + { + channels_[channelId] = new FontChannel(transports_[channelId], compressor_); + + break; + } + default: + { + channels_[channelId] = new SlaveChannel(transports_[channelId], compressor_); + + break; + } + } + + if (channels_[channelId] == NULL) + { + deallocateTransport(channelId); + + return -1; + } + + increaseChannels(channelId); + + channels_[channelId] -> handleConfiguration(); + + return 1; +} diff --git a/nxcomp/Proxy.h b/nxcomp/Proxy.h new file mode 100644 index 000000000..3d6c62c54 --- /dev/null +++ b/nxcomp/Proxy.h @@ -0,0 +1,1263 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Proxy_H +#define Proxy_H + +#include <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" + +// +// Forward declaration as we +// need a pointer. +// + +class Agent; + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Log the important tracepoints related +// to writing packets to the peer proxy. +// + +#undef FLUSH + +// +// Codes used for control messages in +// proxy-to-proxy protocol. +// +// The following codes are currently +// unused. +// +// code_alert_reply, +// code_reset_request, +// code_reset_reply, +// code_load_reply, +// code_save_reply, +// code_shutdown_reply, +// code_configuration_request, +// code_configuration_reply. +// +// These are for compatibility with +// old versions. +// +// code_sync_request, +// code_sync_reply, +// +// The code_new_aux_connection should not +// be used anymore. Auxiliary X connections +// are treated as normal X channels since +// version 1.5.0. +// + +typedef enum +{ + code_new_x_connection, + code_new_cups_connection, + code_new_aux_connection, + code_new_smb_connection, + code_new_media_connection, + code_switch_connection, + code_drop_connection, + code_finish_connection, + code_begin_congestion, + code_end_congestion, + code_alert_request, + code_alert_reply, + code_reset_request, + code_reset_reply, + code_load_request, + code_load_reply, + code_save_request, + code_save_reply, + code_shutdown_request, + code_shutdown_reply, + code_control_token_request, + code_control_token_reply, + code_configuration_request, + code_configuration_reply, + code_statistics_request, + code_statistics_reply, + code_new_http_connection, + code_sync_request, + code_sync_reply, + code_new_font_connection, + code_new_slave_connection, + code_finish_listeners, + code_split_token_request, + code_split_token_reply, + code_data_token_request, + code_data_token_reply, + code_last_tag + +} T_proxy_code; + +typedef enum +{ + operation_in_negotiation, + operation_in_messages, + operation_in_configuration, + operation_in_statistics, + operation_last_tag + +} T_proxy_operation; + +typedef enum +{ + frame_ping, + frame_data, + +} T_frame_type; + +typedef enum +{ + token_control, + token_split, + token_data + +} T_token_type; + +typedef enum +{ + load_if_any, + load_if_first + +} T_load_type; + +class Proxy +{ + public: + + // + // Maximum number of supported channels. + // + + static const int CONNECTIONS_LIMIT = 256; + + // + // Numboer of token types. + // + + static const int TOKEN_TYPES = 3; + + // + // Lenght of buffer we use to add our + // control messages plus the length of + // the data frame. + // + + static const int CONTROL_CODES_LENGTH = ENCODE_BUFFER_PREFIX_SIZE - 5; + + static const int CONTROL_CODES_THRESHOLD = CONTROL_CODES_LENGTH - 9; + + Proxy(int fd); + + virtual ~Proxy(); + + // + // Inform the proxy that the negotiation phase is + // completed and that it can start handling binary + // messages. + // + + int setOperational(); + + int getOperational() + { + return (operation_ != operation_in_negotiation); + } + + int setReadDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax); + + int setWriteDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax); + + // + // Perform the operation on the proxy + // link or its own channels. + // + + int handleRead(int &resultFds, fd_set &fdSet); + + int handleFlush(int &resultFds, fd_set &fdSet); + + int handleRead(); + + int handleRead(int fd, const char *data = NULL, int size = 0); + + int handleEvents(); + + int handleFlush(); + + int handleFlush(int fd); + + int handlePing(); + + int handleFinish(); + + int handleShutdown(); + + int handleStatistics(int type, ostream *statofs); + + int handleAlert(int alert); + + int handleRotate() + { + activeChannels_.rotate(); + + return 1; + } + + int handleChannelConfiguration(); + + int handleSocketConfiguration(); + + int handleLinkConfiguration(); + + int handleCacheConfiguration(); + + // + // These must be called just after initialization to + // tell to the proxy where the network connections + // have to be forwarded. + // + + virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily, + sockaddr * xServerAddr, unsigned int xServerAddrLength) = 0; + + virtual void handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort, + int httpServerPort, const char *fontServerPort) = 0; + + // + // Create new tunneled channels. + // + + virtual int handleNewConnection(T_channel_type type, int clientFd) = 0; + + virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId) = 0; + + virtual int handleNewAgentConnection(Agent *agent) = 0; + + virtual int handleNewXConnection(int clientFd) = 0; + + virtual int handleNewXConnectionFromProxy(int channelId) = 0; + + int handleNewGenericConnection(int clientFd, T_channel_type type, const char *label); + + int handleNewGenericConnectionFromProxy(int channelId, T_channel_type type, + const char *hostname, int port, const char *label); + + int handleNewGenericConnectionFromProxy(int channelId, T_channel_type type, + const char *hostname, const char *path, const char *label); + + int handleNewSlaveConnection(int clientFd); + + int handleNewSlaveConnectionFromProxy(int channelId); + + // + // Force closure of channels. + // + + int handleCloseConnection(int clientFd); + + int handleCloseAllXConnections(); + + int handleCloseAllListeners(); + + // + // Called when the loop has replaced + // or closed a previous alert. + // + + void handleResetAlert(); + + // + // Handle the persistent cache. + // + + virtual int handleLoad(T_load_type type) = 0; + + virtual int handleSave() = 0; + + protected: + + // + // Timeout related data: + // + // flush + // split + // motion + // + // Timeouts in milliseconds after which the + // proxy will have to perform the operation. + // + // readTs, writeTs + // + // Timestamp of last packet received or sent + // to remote proxy. Used to detect lost con- + // nection. + // + // loopTs + // + // Timestamp of last loop completed by the + // proxy + // + // pingTs + // + // Timestamp of last ping request sent to the + // remote peer. + // + // alertTs + // + // Timestamp of last 'no data received' alert + // dialog shown to the user. + // + // loadTs + // + // Were message stores populated from data on + // disk. + // + // splitTs + // motionTs + // + // Timestamps of the last operation of this + // kind handled by the proxy. + // + + typedef struct + { + int split; + int motion; + + T_timestamp readTs; + T_timestamp writeTs; + + T_timestamp loopTs; + T_timestamp pingTs; + T_timestamp alertTs; + T_timestamp loadTs; + + T_timestamp splitTs; + T_timestamp motionTs; + + } T_proxy_timeouts; + + // + // Bytes accumulated so far while waiting + // to send the next token, number of tokens + // remaining for each token type and other + // token related information. + // + + typedef struct + { + int size; + int limit; + + int bytes; + int remaining; + + T_proxy_code request; + T_proxy_code reply; + + T_token_type type; + + } T_proxy_token; + + int handlePostConnectionFromProxy(int channelId, int serverFd, + T_channel_type type, const char *label); + + int handleDrain(); + + int handleFrame(T_frame_type type); + + int handleFinish(int channelId); + + int handleDrop(int channelId); + + int handleFinishFromProxy(int channelId); + + int handleDropFromProxy(int channelId); + + int handleStatisticsFromProxy(int type); + + int handleStatisticsFromProxy(const unsigned char *message, unsigned int length); + + int handleNegotiation(const unsigned char *message, unsigned int length); + + int handleNegotiationFromProxy(const unsigned char *message, unsigned int length); + + int handleToken(T_frame_type type); + + int handleTokenFromProxy(T_proxy_token &token, int count); + + int handleTokenReplyFromProxy(T_proxy_token &token, int count); + + int handleSyncFromProxy(int channelId); + + int handleSwitch(int channelId); + + int handleControl(T_proxy_code code, int data = -1); + + int handleControlFromProxy(const unsigned char *message); + + // + // Interleave reads of the X server + // events while writing data to the + // remote proxy. + // + + virtual int handleAsyncEvents() = 0; + + // + // Timer related functions. + // + + protected: + + void setTimer(int value) + { + SetTimer(value); + } + + void resetTimer() + { + ResetTimer(); + + timer_ = 0; + } + + public: + + void handleTimer() + { + timer_ = 1; + } + + int getTimer() + { + return timer_; + } + + // + // Can the channel consume data and the + // proxy produce more output? + // + + int canRead(int fd) const + { + return (isTimeToRead() == 1 && + isTimeToRead(getChannel(fd)) == 1); + } + + // + // Can the proxy read more data from its + // peer? + // + + int canRead() const + { + return (transport_ -> readable() != 0); + } + + int canFlush() const + { + return (encodeBuffer_.getLength() + + controlLength_ + transport_ -> length() + + transport_ -> flushable() > 0); + } + + int needFlush(int channelId) const + { + return (outputChannel_ == channelId && + encodeBuffer_.getLength() > 0); + } + + int needFlush() const + { + return (encodeBuffer_.getLength() > 0); + } + + int shouldFlush() const + { + if ((int) ((encodeBuffer_.getLength() + + controlLength_) / statistics -> + getStreamRatio()) >= control -> TokenSize) + { + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "Proxy: FLUSH! Requesting a flush with " + << (encodeBuffer_.getLength() + controlLength_) + << " bytes and stream ratio " << (double) statistics -> + getStreamRatio() << ".\n" << logofs_flush; + #endif + + return 1; + } + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "Proxy: Not requesting a flush with " + << (encodeBuffer_.getLength() + controlLength_) + << " bytes and stream ratio " << (double) statistics -> + getStreamRatio() << ".\n" << logofs_flush; + #endif + + return 0; + } + + int needDrain() const + { + return (congestion_ == 1 || transport_ -> length() > + control -> TransportProxyBufferThreshold); + } + + int getFd() const + { + return fd_; + } + + int getFlushable(int fd) const + { + if (fd == fd_) + { + return (encodeBuffer_.getLength() + controlLength_ + + transport_ -> flushable()); + } + + return 0; + } + + int getSplitSize() + { + return (clientStore_ != NULL ? clientStore_ -> + getSplitTotalSize() : 0); + } + + int getSplitStorageSize() + { + return (clientStore_ != NULL ? clientStore_ -> + getSplitTotalStorageSize() : 0); + } + + // + // Returns the number of active channels + // that currently managed by this proxy. + // + + int getChannels(T_channel_type type = channel_none); + + // + // If descriptor corresponds to a valid + // channel, returns the type of traffic + // carried by it. + // + + T_channel_type getType(int fd); + + // + // Given a channel type, returns the + // literal name. + // + + const char *getTypeName(T_channel_type type); + + // + // Get a convenient name for 'localhost'. + // + + const char *getComputerName(); + + // + // Set if we have initiated the shutdown + // procedure and if the shutdown request + // has been received from the remote. + // + + int getFinish() const + { + return finish_; + } + + int getShutdown() const + { + return shutdown_; + } + + // + // Interfaces to the transport buffers. + // + + int getLength(int fd) const + { + if (fd == fd_) + { + return transport_ -> length(); + } + + int channelId = getChannel(fd); + + if (channelId < 0 || channels_[channelId] == NULL) + { + return 0; + } + + return transports_[channelId] -> length(); + } + + int getPending(int fd) const + { + if (fd == fd_) + { + return transport_ -> pending(); + } + + int channelId = getChannel(fd); + + if (channelId < 0 || channels_[channelId] == NULL) + { + return 0; + } + + return transports_[channelId] -> pending(); + } + + // + // Check if the proxy or the given channel + // has data in the buffer because of a + // blocking write. + // + + int getBlocked(int fd) const + { + if (fd == fd_) + { + return transport_ -> blocked(); + } + + int channelId = getChannel(fd); + + if (channelId < 0 || channels_[channelId] == NULL) + { + return 0; + } + + return transports_[channelId] -> blocked(); + } + + // + // Check if the proxy or the given channel has + // data to read. + // + + int getReadable(int fd) const + { + if (fd == fd_) + { + return transport_ -> readable(); + } + + int channelId = getChannel(fd); + + if (channelId < 0 || channels_[channelId] == NULL) + { + return 0; + } + + return transports_[channelId] -> readable(); + } + + // + // Return a vale between 0 and 9 in the case + // of the proxy descriptor. + // + + int getCongestion(int fd) const + { + if (fd == fd_) + { + return (agent_ != nothing && congestions_[agent_] == 1 ? 9 : + (int) statistics -> getCongestionInFrame()); + } + + int channelId = getChannel(fd); + + if (channelId < 0 || channels_[channelId] == NULL) + { + return 0; + } + + return channels_[channelId] -> getCongestion(); + } + + // + // Let the statistics class get info + // from the message stores. + // + + const ClientStore * const getClientStore() const + { + return clientStore_; + } + + const ServerStore * const getServerStore() const + { + return serverStore_; + } + + // + // These can be called asynchronously by + // channels during their read or write + // loop. + // + + int handleAsyncRead(int fd) + { + return handleRead(fd); + } + + int handleAsyncCongestion(int fd) + { + int channelId = getChannel(fd); + + return handleControl(code_begin_congestion, channelId); + } + + int handleAsyncDecongestion(int fd) + { + int channelId = getChannel(fd); + + return handleControl(code_end_congestion, channelId); + } + + int handleAsyncSplit(int fd, Split *split) + { + return channels_[getChannel(fd)] -> + handleSplitEvent(encodeBuffer_, split); + } + + int handleAsyncInit() + { + return handleFlush(); + } + + int handleAsyncPriority() + { + if (control -> FlushPriority == 1) + { + return handleFlush(); + } + + return 0; + } + + int canAsyncFlush() const + { + return shouldFlush(); + } + + int handleAsyncFlush() + { + return handleFlush(); + } + + int handleAsyncSwitch(int fd) + { + return handleSwitch(getChannel(fd)); + } + + int handleAsyncKeeperCallback() + { + KeeperCallback(); + + return 1; + } + + // + // Internal interfaces used to verify the + // availability of channels and the proxy + // link. + // + + protected: + + int isTimeToRead() const + { + if (congestion_ == 0 && transport_ -> + blocked() == 0) + { + return 1; + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Can't read from channels " + << "with congestion " << congestion_ + << " and blocked " << transport_ -> + blocked() << ".\n" << logofs_flush; + #endif + + return 0; + } + } + + int isTimeToRead(int channelId) const + { + if (channelId >= 0 && channelId < CONNECTIONS_LIMIT && + channels_[channelId] != NULL && + congestions_[channelId] == 0) + { + if (channels_[channelId] -> getType() == channel_x11 || + tokens_[token_data].remaining > 0 || + channels_[channelId] -> + getFinish() == 1) + { + return 1; + } + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: Can't read from generic " + << "descriptor FD#" << getFd(channelId) + << " channel ID#" << channelId << " with " + << tokens_[token_data].remaining + << " data tokens remaining.\n" + << logofs_flush; + #endif + + return 0; + } + + #if defined(TEST) || defined(INFO) + + if (channelId < 0 || channelId >= CONNECTIONS_LIMIT || + channels_[channelId] == NULL) + { + *logofs << "Proxy: WARNING! No valid channel for ID#" + << channelId << ".\n" << logofs_flush; + } + else if (channels_[channelId] -> getFinish()) + { + *logofs << "Proxy: Can't read from finishing " + << "descriptor FD#" << getFd(channelId) + << " channel ID#" << channelId << ".\n" + << logofs_flush; + } + else if (congestions_[channelId] == 1) + { + *logofs << "Proxy: Can't read from congested " + << "descriptor FD#" << getFd(channelId) + << " channel ID#" << channelId << ".\n" + << logofs_flush; + } + + #endif + + return 0; + } + + // + // Handle the flush and split timeouts. + // All these functions should round up + // to the value of the latency timeout + // to save a further loop. + // + + protected: + + int isTimeToSplit() const + { + if (isTimestamp(timeouts_.splitTs) && + getTimeToNextSplit() <= control -> + LatencyTimeout) + { + if (tokens_[token_split].remaining > 0) + { + return 1; + } + + #if defined(TEST) || defined(INFO) + *logofs << "Proxy: WARNING! Can't encode splits " + << "with " << tokens_[token_split].remaining + << " split tokens remaining.\n" + << logofs_flush; + #endif + } + + return 0; + } + + int isTimeToMotion() const + { + return (isTimestamp(timeouts_.motionTs) && + getTimeToNextMotion() <= control -> + LatencyTimeout); + } + + int getTimeToNextSplit() const + { + #if defined(TEST) || defined(INFO) || defined(FLUSH) + + if (isTimestamp(timeouts_.splitTs) == 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! No split timeout was set " + << "for proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": No split timeout was set " + << "for proxy FD#" << fd_ << ".\n"; + + HandleCleanup(); + } + + #endif + + int diffTs = timeouts_.split - + diffTimestamp(timeouts_.splitTs, + getTimestamp()); + + return (diffTs > 0 ? diffTs : 0); + } + + int getTimeToNextMotion() const + { + #if defined(TEST) || defined(INFO) || defined(FLUSH) + + if (isTimestamp(timeouts_.motionTs) == 0) + { + #ifdef PANIC + *logofs << "Proxy: PANIC! No motion timeout was set " + << "for proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": No motion timeout was set " + << "for proxy FD#" << fd_ << ".\n"; + + HandleCleanup(); + } + + #endif + + int diffTs = timeouts_.motion - + diffTimestamp(timeouts_.motionTs, + getTimestamp()); + + return (diffTs > 0 ? diffTs : 0); + } + + protected: + + // + // Implement persistence of cache on disk. + // + + virtual int handleLoadFromProxy() = 0; + virtual int handleSaveFromProxy() = 0; + + int handleLoadStores(); + int handleSaveStores(); + + char *handleSaveAllStores(const char *savePath) const; + + virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient) const = 0; + + int handleSaveVersion(unsigned char *buffer, int &major, int &minor, int &patch) const; + + void handleFailOnSave(const char *fullName, const char *failContext) const; + + const char *handleLoadAllStores(const char *loadPath, const char *loadName) const; + + virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const = 0; + + int handleLoadVersion(const unsigned char *buffer, int &major, int &minor, int &patch) const; + + void handleFailOnLoad(const char *fullName, const char *failContext) const; + + // + // Prepare for a new persistent cache. + // + + int handleResetPersistentCache(); + + // + // Reset the stores in the case of a + // failure loading the cache. + // + + int handleResetStores(); + + // + // Reset the transport buffer and the + // other counters. + // + + void handleResetFlush(); + + // + // Utility functions used to map file + // descriptors to channel ids. + // + + protected: + + int allocateChannelMap(int fd); + int checkChannelMap(int channelId); + int assignChannelMap(int channelId, int fd); + + void cleanupChannelMap(int channelId); + + virtual int checkLocalChannelMap(int channelId) = 0; + + int addControlCodes(T_proxy_code code, int data); + int addTokenCodes(T_proxy_token &token); + + int getFd(int channelId) const + { + if (channelId >= 0 && channelId < CONNECTIONS_LIMIT) + { + return fdMap_[channelId]; + } + + return -1; + } + + int getChannel(int fd) const + { + if (fd >= 0 && fd < CONNECTIONS_LIMIT) + { + return channelMap_[fd]; + } + + return -1; + } + + protected: + + void setSplitTimeout(int channelId); + void setMotionTimeout(int channelId); + + void increaseChannels(int channelId); + void decreaseChannels(int channelId); + + int allocateTransport(int channelFd, int channelId); + int deallocateTransport(int channelId); + + // + // The proxy has its own transport. + // + + ProxyTransport *transport_; + + // + // The static compressor is shared among + // channels and all the message stores. + // + + StaticCompressor *compressor_; + + // + // Map NX specific opcodes. + // + + OpcodeStore *opcodeStore_; + + // + // Stores are shared between channels. + // + + ClientStore *clientStore_; + ServerStore *serverStore_; + + // + // Client and server caches are shared + // between channels. + // + + ClientCache *clientCache_; + ServerCache *serverCache_; + + // + // The proxy's file descriptor. + // + + int fd_; + + // + // Channels currently selected for I/O + // operations. + // + + int inputChannel_; + int outputChannel_; + + // + // List of active channels. + // + + List activeChannels_; + + // + // Used to read data sent from peer proxy. + // + + ProxyReadBuffer readBuffer_; + + // + // Used to send data to peer proxy. + // + + EncodeBuffer encodeBuffer_; + + // + // Control messages' array. + // + + int controlLength_; + + unsigned char controlCodes_[CONTROL_CODES_LENGTH]; + + // + // Table of channel classes taking + // care of open X connections. + // + + Channel *channels_[CONNECTIONS_LIMIT]; + + // + // Table of open sockets. + // + + Transport *transports_[CONNECTIONS_LIMIT]; + + // + // Timeout related data. + // + + T_proxy_timeouts timeouts_; + + // + // Proxy can be decoding messages, + // handling a link reconfiguration, + // or decoding statistics. + // + + int operation_; + + // + // True if we are currently draining + // the proxy link. + // + + int draining_; + + // + // Force flush because of prioritized + // control messages to send. + // + + int priority_; + + // + // Set if we have initiated the close + // down procedure. + // + + int finish_; + + // + // Remote peer requested the shutdown. + // + + int shutdown_; + + // + // We are in the middle of a network + // congestion in the path to remote + // proxy. + // + + int congestion_; + + // + // Channels at the remote end that + // are not consuming their data. + // + + int congestions_[CONNECTIONS_LIMIT]; + + // + // Is the timer expired? + // + + int timer_; + + // + // Did the proxy request an alert? + // + + int alert_; + + // + // The channel id of the agent. + // + + int agent_; + + // + // Token related data. + // + + T_proxy_token tokens_[TOKEN_TYPES]; + + // + // Pointer to stream descriptor where + // proxy is producing statistics. + // + + ostream *currentStatistics_; + + private: + + // + // Map channel ids to file descriptors. + // + + int channelMap_[CONNECTIONS_LIMIT]; + int fdMap_[CONNECTIONS_LIMIT]; +}; + +#endif /* Proxy_H */ diff --git a/nxcomp/ProxyReadBuffer.cpp b/nxcomp/ProxyReadBuffer.cpp new file mode 100644 index 000000000..b0de14921 --- /dev/null +++ b/nxcomp/ProxyReadBuffer.cpp @@ -0,0 +1,199 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ProxyReadBuffer.h" + +#include "Transport.h" + +// +// Set the verbosity level. You also +// need to define DUMP in Misc.cpp +// if DUMP is defined here. +// + +#define WARNING +#define PANIC +#undef TEST +#undef DEBUG +#undef DUMP + +unsigned int ProxyReadBuffer::suggestedLength(unsigned int pendingLength) +{ + // + // Always read all the data that + // is available. + // + + int readable = transport_ -> readable(); + + unsigned int readLength = (readable == -1 ? 0 : (unsigned int) readable); + + if (readLength < pendingLength) + { + readLength = pendingLength; + } + + // + // Even if the readable data is not + // enough to make a complete message, + // resize the buffer to accomodate + // it all. + // + + if (pendingLength < remaining_) + { + readLength = remaining_; + } + + return readLength; +} + +int ProxyReadBuffer::locateMessage(const unsigned char *start, + const unsigned char *end, + unsigned int &controlLength, + unsigned int &dataLength, + unsigned int &trailerLength) +{ + unsigned int lengthLength = 0; + const unsigned char *nextSrc = start; + unsigned char next; + + dataLength = 0; + + #ifdef TEST + *logofs << "ProxyReadBuffer: Locating message for FD#" + << transport_ -> fd() << " with " << end - start + << " bytes.\n" << logofs_flush; + #endif + + // + // Use something like the following if + // you are looking for errors. + // + + #ifdef DUMP + if (control -> ProxyMode == proxy_server && start < end && + transport_ -> fd() == 6 || transport_ -> fd() == 11) + { + *logofs << "ProxyReadBuffer: Partial checksums are:\n"; + + DumpBlockChecksums(start, end - start, 256); + + *logofs << logofs_flush; + } + #endif + + do + { + if (nextSrc >= end) + { + remaining_ = 1; + + #ifdef TEST + *logofs << "ProxyReadBuffer: No message was located " + << "with remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 0; + } + + next = *nextSrc++; + + dataLength <<= 7; + dataLength |= (unsigned int) (next & 0x7f); + + lengthLength++; + } + while (next & 0x80); + + unsigned int totalLength; + + if (dataLength == 0) + { + trailerLength = 0; + controlLength = 3; + totalLength = controlLength; + } + else + { + trailerLength = lengthLength; + controlLength = 0; + totalLength = dataLength + trailerLength; + } + + if (start + totalLength > end) + { + // + // When having to decompress a ZLIB stream, + // a single byte can be enough to complete + // the frame. + // + + if (control -> RemoteStreamCompression == 0) + { + remaining_ = totalLength - (end - start); + } + else + { + remaining_ = 1; + } + + #ifdef TEST + *logofs << "ProxyReadBuffer: No message was located " + << "with remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 0; + } + else + { + #ifdef DUMP + *logofs << "ProxyReadBuffer: Received " << totalLength << " bytes of data " + << "with checksum "; + + DumpChecksum(start, totalLength); + + *logofs << " on proxy FD#" << transport_ -> fd() << ".\n" << logofs_flush; + #endif + + #if defined(TEST) || defined(INFO) + *logofs << "ProxyReadBuffer: Produced plain input for " << dataLength + << "+" << trailerLength << "+" << controlLength << " bytes out of " + << totalLength << " bytes.\n" << logofs_flush; + #endif + + #ifdef DUMP + *logofs << "ProxyReadBuffer: Partial checksums are:\n"; + + DumpBlockChecksums(start, totalLength, 256); + + *logofs << logofs_flush; + #endif + + remaining_ = 0; + + #ifdef TEST + *logofs << "ProxyReadBuffer: Located message with " + << "remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 1; + } +} diff --git a/nxcomp/ProxyReadBuffer.h b/nxcomp/ProxyReadBuffer.h new file mode 100644 index 000000000..b87b215bf --- /dev/null +++ b/nxcomp/ProxyReadBuffer.h @@ -0,0 +1,49 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ProxyReadBuffer_H +#define ProxyReadBuffer_H + +#include "ReadBuffer.h" +#include "Control.h" + +class ProxyReadBuffer : public ReadBuffer +{ + public: + + ProxyReadBuffer(Transport *transport) + + : ReadBuffer(transport) + { + } + + virtual ~ProxyReadBuffer() + { + } + + protected: + + virtual unsigned int suggestedLength(unsigned int pendingLength); + + virtual int locateMessage(const unsigned char *start, + const unsigned char *end, + unsigned int &controlLength, + unsigned int &dataLength, + unsigned int &trailerLength); +}; + +#endif /* ProxyReadBuffer_H */ diff --git a/nxcomp/PutImage.cpp b/nxcomp/PutImage.cpp new file mode 100644 index 000000000..325a4aa66 --- /dev/null +++ b/nxcomp/PutImage.cpp @@ -0,0 +1,405 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "PutImage.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +PutImageStore::PutImageStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = PUTIMAGE_ENABLE_CACHE; + enableData = PUTIMAGE_ENABLE_DATA; + enableSplit = PUTIMAGE_ENABLE_SPLIT; + enableCompress = PUTIMAGE_ENABLE_COMPRESS; + + if (control -> isProtoStep7() == 1) + { + enableCompress = PUTIMAGE_ENABLE_COMPRESS_IF_PROTO_STEP_7; + } + + dataLimit = PUTIMAGE_DATA_LIMIT; + dataOffset = PUTIMAGE_DATA_OFFSET; + + cacheSlots = PUTIMAGE_CACHE_SLOTS; + cacheThreshold = PUTIMAGE_CACHE_THRESHOLD; + cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD; + + if (control -> isProtoStep8() == 1) + { + enableSplit = PUTIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8; + } + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +PutImageStore::~PutImageStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int PutImageStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; + #endif + + encodeBuffer.encodeValue(GetUINT(buffer + 2, bigEndian), 16, 8); + + encodeBuffer.encodeValue((unsigned int) buffer[1], 2); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> drawableCache); + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> gcCache); + + unsigned int width = GetUINT(buffer + 12, bigEndian); + encodeBuffer.encodeCachedValue(width, 16, + clientCache -> putImageWidthCache, 8); + + unsigned int height = GetUINT(buffer + 14, bigEndian); + encodeBuffer.encodeCachedValue(height, 16, + clientCache -> putImageHeightCache, 8); + + unsigned int x = GetUINT(buffer + 16, bigEndian); + int xDiff = x - clientCache -> putImageLastX; + clientCache -> putImageLastX = x; + encodeBuffer.encodeCachedValue(xDiff, 16, + clientCache -> putImageXCache, 8); + + unsigned int y = GetUINT(buffer + 18, bigEndian); + int yDiff = y - clientCache -> putImageLastY; + clientCache -> putImageLastY = y; + encodeBuffer.encodeCachedValue(yDiff, 16, + clientCache -> putImageYCache, 8); + + encodeBuffer.encodeCachedValue(buffer[20], 8, + clientCache -> putImageLeftPadCache); + + encodeBuffer.encodeCachedValue(buffer[21], 8, + clientCache -> depthCache); + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int PutImageStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; + #endif + + unsigned int value; + unsigned char cValue; + + decodeBuffer.decodeValue(value, 16, 8); + + size = (value << 2); + + buffer = writeBuffer -> addMessage(size); + + decodeBuffer.decodeValue(value, 2); + buffer[1] = (unsigned char) value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + PutULONG(value, buffer + 4, bigEndian); + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + PutULONG(value, buffer + 8, bigEndian); + + unsigned int width; + decodeBuffer.decodeCachedValue(width, 16, + clientCache -> putImageWidthCache, 8); + PutUINT(width, buffer + 12, bigEndian); + + unsigned int height; + decodeBuffer.decodeCachedValue(height, 16, + clientCache -> putImageHeightCache, 8); + PutUINT(height, buffer + 14, bigEndian); + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageXCache, 8); + clientCache -> putImageLastX += value; + clientCache -> putImageLastX &= 0xffff; + PutUINT(clientCache -> putImageLastX, buffer + 16, bigEndian); + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageYCache, 8); + clientCache -> putImageLastY += value; + clientCache -> putImageLastY &= 0xffff; + PutUINT(clientCache -> putImageLastY, buffer + 18, bigEndian); + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> putImageLeftPadCache); + buffer[20] = cValue; + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + buffer[21] = cValue; + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int PutImageStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PutImageMessage *putImage = (PutImageMessage *) message; + + // + // Here is the fingerprint. + // + + putImage -> format = *(buffer + 1); + putImage -> depth = *(buffer + 21); + putImage -> left_pad = *(buffer + 20); + + putImage -> width = GetUINT(buffer + 12, bigEndian); + putImage -> height = GetUINT(buffer + 14, bigEndian); + putImage -> pos_x = GetUINT(buffer + 16, bigEndian); + putImage -> pos_y = GetUINT(buffer + 18, bigEndian); + + putImage -> drawable = GetULONG(buffer + 4, bigEndian); + putImage -> gcontext = GetULONG(buffer + 8, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int PutImageStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PutImageMessage *putImage = (PutImageMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = putImage -> format; + + PutULONG(putImage -> drawable, buffer + 4, bigEndian); + PutULONG(putImage -> gcontext, buffer + 8, bigEndian); + + PutUINT(putImage -> width, buffer + 12, bigEndian); + PutUINT(putImage -> height, buffer + 14, bigEndian); + PutUINT(putImage -> pos_x, buffer + 16, bigEndian); + PutUINT(putImage -> pos_y, buffer + 18, bigEndian); + + *(buffer + 20) = (unsigned char) putImage -> left_pad; + *(buffer + 21) = (unsigned char) putImage -> depth; + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void PutImageStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + PutImageMessage *putImage = (PutImageMessage *) message; + + *logofs << name() << ": Identity format " << (unsigned) putImage -> format + << ", depth " << (unsigned) putImage -> depth << ", left_pad " + << (unsigned) putImage -> left_pad << ", width " << putImage -> width + << ", height " << putImage -> height << ", pos_x " << putImage -> pos_x + << ", pos_y " << putImage -> pos_y << ", drawable " << putImage -> drawable + << ", gcontext " << putImage -> gcontext << ", size " << putImage -> size_ + << ".\n" << logofs_flush; + + #endif +} + +void PutImageStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // Fields format, width, height, left_pad, depth. + // + + md5_append(md5_state_, buffer + 1, 1); + md5_append(md5_state_, buffer + 12, 4); + md5_append(md5_state_, buffer + 20, 2); +} + +void PutImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + // + // Encode the variant part. + // + + PutImageMessage *putImage = (PutImageMessage *) message; + PutImageMessage *cachedPutImage = (PutImageMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << putImage -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(putImage -> drawable, clientCache -> drawableCache); + + cachedPutImage -> drawable = putImage -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << putImage -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(putImage -> gcontext, clientCache -> gcCache); + + cachedPutImage -> gcontext = putImage -> gcontext; + + #ifdef TEST + *logofs << name() << ": Encoding value " << putImage -> pos_x + << " as " << "pos_x" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_x = putImage -> pos_x - cachedPutImage -> pos_x; + + encodeBuffer.encodeCachedValue(diff_x, 16, + clientCache -> putImageXCache, 8); + + cachedPutImage -> pos_x = putImage -> pos_x; + + #ifdef TEST + *logofs << name() << ": Encoding value " << putImage -> pos_y + << " as " << "pos_y" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_y = putImage -> pos_y - cachedPutImage -> pos_y; + + encodeBuffer.encodeCachedValue(diff_y, 16, + clientCache -> putImageYCache, 8); + + cachedPutImage -> pos_y = putImage -> pos_y; +} + +void PutImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + // + // Decode the variant part. + // + + PutImageMessage *putImage = (PutImageMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + putImage -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << putImage -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + putImage -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << putImage -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageXCache, 8); + + putImage -> pos_x += value; + putImage -> pos_x &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << putImage -> pos_x + << " as pos_x field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageYCache, 8); + + putImage -> pos_y += value; + putImage -> pos_y &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << putImage -> pos_y + << " as pos_y field.\n" << logofs_flush; + #endif +} diff --git a/nxcomp/PutImage.h b/nxcomp/PutImage.h new file mode 100644 index 000000000..e9535cb29 --- /dev/null +++ b/nxcomp/PutImage.h @@ -0,0 +1,166 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PutImage_H +#define PutImage_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define PUTIMAGE_ENABLE_CACHE 1 +#define PUTIMAGE_ENABLE_DATA 1 +#define PUTIMAGE_ENABLE_SPLIT 1 +#define PUTIMAGE_ENABLE_COMPRESS 1 + +#define PUTIMAGE_DATA_LIMIT 262144 - 24 +#define PUTIMAGE_DATA_OFFSET 24 + +#define PUTIMAGE_CACHE_SLOTS 6000 +#define PUTIMAGE_CACHE_THRESHOLD 70 +#define PUTIMAGE_CACHE_LOWER_THRESHOLD 50 + +#define PUTIMAGE_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 + +#define PUTIMAGE_CACHE_THRESHOLD_IF_PACKED 10 +#define PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED 5 + +#define PUTIMAGE_CACHE_THRESHOLD_IF_SHADOW 97 +#define PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_SHADOW 90 + +#define PUTIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8 0 + +// +// The message class. +// + +class PutImageMessage : public Message +{ + friend class PutImageStore; + + public: + + PutImageMessage() + { + } + + ~PutImageMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char format; + unsigned char depth; + unsigned char left_pad; + unsigned short width; + unsigned short height; + unsigned int drawable; + unsigned int gcontext; + unsigned short pos_x; + unsigned short pos_y; +}; + +class PutImageStore : public MessageStore +{ + public: + + PutImageStore(StaticCompressor *compressor); + + virtual ~PutImageStore(); + + virtual const char *name() const + { + return "PutImage"; + } + + virtual unsigned char opcode() const + { + return X_PutImage; + } + + virtual unsigned int storage() const + { + return sizeof(PutImageMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new PutImageMessage(); + } + + virtual Message *create(const Message &message) const + { + return new PutImageMessage((const PutImageMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (PutImageMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* PutImage_H */ diff --git a/nxcomp/PutPackedImage.cpp b/nxcomp/PutPackedImage.cpp new file mode 100644 index 000000000..eae16a16f --- /dev/null +++ b/nxcomp/PutPackedImage.cpp @@ -0,0 +1,595 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "PutPackedImage.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +PutPackedImageStore::PutPackedImageStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = PUTPACKEDIMAGE_ENABLE_CACHE; + enableData = PUTPACKEDIMAGE_ENABLE_DATA; + enableSplit = PUTPACKEDIMAGE_ENABLE_SPLIT; + enableCompress = PUTPACKEDIMAGE_ENABLE_COMPRESS; + + dataLimit = PUTPACKEDIMAGE_DATA_LIMIT; + dataOffset = PUTPACKEDIMAGE_DATA_OFFSET; + + cacheSlots = PUTPACKEDIMAGE_CACHE_SLOTS; + cacheThreshold = PUTPACKEDIMAGE_CACHE_THRESHOLD; + cacheLowerThreshold = PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD; + + if (control -> isProtoStep8() == 1) + { + enableSplit = PUTPACKEDIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8; + } + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +PutPackedImageStore::~PutPackedImageStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int PutPackedImageStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; + #endif + + // Client. + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + clientCache -> resourceCache); + + // Size. + encodeBuffer.encodeValue(GetUINT(buffer + 2, bigEndian), 16, 10); + + // Drawable. + encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> drawableCache); + // GC. + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> gcCache); + // Method. + encodeBuffer.encodeCachedValue(*(buffer + 12), 8, + clientCache -> methodCache); + // Format. + encodeBuffer.encodeValue(*(buffer + 13), 2); + + // SrcDepth. + encodeBuffer.encodeCachedValue(*(buffer + 14), 8, + clientCache -> depthCache); + // DstDepth. + encodeBuffer.encodeCachedValue(*(buffer + 15), 8, + clientCache -> depthCache); + // SrcLength. + encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 24, + clientCache -> putPackedImageSrcLengthCache); + // DstLength. + encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 24, + clientCache -> putPackedImageDstLengthCache); + // SrcX. + unsigned int x = GetUINT(buffer + 24, bigEndian); + int xDiff = x - clientCache -> putImageLastX; + clientCache -> putImageLastX = x; + encodeBuffer.encodeCachedValue(xDiff, 16, + clientCache -> putImageXCache, 8); + // SrcY. + unsigned int y = GetUINT(buffer + 26, bigEndian); + int yDiff = y - clientCache -> putImageLastY; + clientCache -> putImageLastY = y; + encodeBuffer.encodeCachedValue(yDiff, 16, + clientCache -> putImageYCache, 8); + // SrcWidth. + encodeBuffer.encodeCachedValue(GetUINT(buffer + 28, bigEndian), 16, + clientCache -> putImageWidthCache, 8); + // SrcHeight. + encodeBuffer.encodeCachedValue(GetUINT(buffer + 30, bigEndian), 16, + clientCache -> putImageHeightCache, 8); + // DstX. + x = GetUINT(buffer + 32, bigEndian); + xDiff = x - clientCache -> putImageLastX; + clientCache -> putImageLastX = x; + encodeBuffer.encodeCachedValue(xDiff, 16, + clientCache -> putImageXCache, 8); + // DstY. + y = GetUINT(buffer + 34, bigEndian); + yDiff = y - clientCache -> putImageLastY; + clientCache -> putImageLastY = y; + encodeBuffer.encodeCachedValue(yDiff, 16, + clientCache -> putImageYCache, 8); + // DstWidth. + encodeBuffer.encodeCachedValue(GetUINT(buffer + 36, bigEndian), 16, + clientCache -> putImageWidthCache, 8); + // DstHeight. + encodeBuffer.encodeCachedValue(GetUINT(buffer + 38, bigEndian), 16, + clientCache -> putImageHeightCache, 8); + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int PutPackedImageStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; + #endif + + unsigned int value; + unsigned char cValue; + + // Client. + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> resourceCache); + + // Size. + decodeBuffer.decodeValue(size, 16, 10); + + size <<= 2; + + buffer = writeBuffer -> addMessage(size); + + *(buffer + 1) = cValue; + + // Drawable. + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + PutULONG(value, buffer + 4, bigEndian); + + // GC. + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + PutULONG(value, buffer + 8, bigEndian); + + // Method. + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> methodCache); + *(buffer + 12) = cValue; + + // Format. + decodeBuffer.decodeValue(value, 2); + *(buffer + 13) = value; + + // SrcDepth. + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + *(buffer + 14) = cValue; + + // DstDepth. + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + *(buffer + 15) = cValue; + + // SrcLength. + decodeBuffer.decodeCachedValue(value, 24, + clientCache -> putPackedImageSrcLengthCache); + PutULONG(value, buffer + 16, bigEndian); + + // DstLength. + decodeBuffer.decodeCachedValue(value, 24, + clientCache -> putPackedImageDstLengthCache); + PutULONG(value, buffer + 20, bigEndian); + + // SrcX. + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageXCache, 8); + clientCache -> putImageLastX += value; + clientCache -> putImageLastX &= 0xffff; + PutUINT(clientCache -> putImageLastX, buffer + 24, bigEndian); + + // SrcY. + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageYCache, 8); + clientCache -> putImageLastY += value; + clientCache -> putImageLastY &= 0xffff; + PutUINT(clientCache -> putImageLastY, buffer + 26, bigEndian); + + // SrcWidth. + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageWidthCache, 8); + PutUINT(value, buffer + 28, bigEndian); + + // SrcHeight. + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageHeightCache, 8); + PutUINT(value, buffer + 30, bigEndian); + + // DstX. + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageXCache, 8); + clientCache -> putImageLastX += value; + clientCache -> putImageLastX &= 0xffff; + PutUINT(clientCache -> putImageLastX, buffer + 32, bigEndian); + + // DstY. + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageYCache, 8); + clientCache -> putImageLastY += value; + clientCache -> putImageLastY &= 0xffff; + PutUINT(clientCache -> putImageLastY, buffer + 34, bigEndian); + + // DstWidth. + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageWidthCache, 8); + PutUINT(value, buffer + 36, bigEndian); + + // DstHeight. + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageHeightCache, 8); + PutUINT(value, buffer + 38, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int PutPackedImageStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message; + + // + // Here is the fingerprint. + // + + putPackedImage -> client = *(buffer + 1); + + putPackedImage -> drawable = GetULONG(buffer + 4, bigEndian); + putPackedImage -> gcontext = GetULONG(buffer + 8, bigEndian); + + putPackedImage -> method = *(buffer + 12); + + putPackedImage -> format = *(buffer + 13); + putPackedImage -> src_depth = *(buffer + 14); + putPackedImage -> dst_depth = *(buffer + 15); + + putPackedImage -> src_length = GetULONG(buffer + 16, bigEndian); + putPackedImage -> dst_length = GetULONG(buffer + 20, bigEndian); + + putPackedImage -> src_x = GetUINT(buffer + 24, bigEndian); + putPackedImage -> src_y = GetUINT(buffer + 26, bigEndian); + putPackedImage -> src_width = GetUINT(buffer + 28, bigEndian); + putPackedImage -> src_height = GetUINT(buffer + 30, bigEndian); + + putPackedImage -> dst_x = GetUINT(buffer + 32, bigEndian); + putPackedImage -> dst_y = GetUINT(buffer + 34, bigEndian); + putPackedImage -> dst_width = GetUINT(buffer + 36, bigEndian); + putPackedImage -> dst_height = GetUINT(buffer + 38, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int PutPackedImageStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = putPackedImage -> client; + + PutULONG(putPackedImage -> drawable, buffer + 4, bigEndian); + PutULONG(putPackedImage -> gcontext, buffer + 8, bigEndian); + + *(buffer + 12) = putPackedImage -> method; + + *(buffer + 13) = putPackedImage -> format; + *(buffer + 14) = putPackedImage -> src_depth; + *(buffer + 15) = putPackedImage -> dst_depth; + + PutULONG(putPackedImage -> src_length, buffer + 16, bigEndian); + PutULONG(putPackedImage -> dst_length, buffer + 20, bigEndian); + + PutUINT(putPackedImage -> src_x, buffer + 24, bigEndian); + PutUINT(putPackedImage -> src_y, buffer + 26, bigEndian); + PutUINT(putPackedImage -> src_width, buffer + 28, bigEndian); + PutUINT(putPackedImage -> src_height, buffer + 30, bigEndian); + + PutUINT(putPackedImage -> dst_x, buffer + 32, bigEndian); + PutUINT(putPackedImage -> dst_y, buffer + 34, bigEndian); + PutUINT(putPackedImage -> dst_width, buffer + 36, bigEndian); + PutUINT(putPackedImage -> dst_height, buffer + 38, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void PutPackedImageStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message; + + *logofs << name() << ": Identity format " + + << "drawable " << putPackedImage -> drawable << ", " + << "gcontext " << putPackedImage -> gcontext << ", " + + << "format " << (unsigned int) putPackedImage -> format << ", " + << "method " << (unsigned int) putPackedImage -> method << ", " + + << "src_depth " << (unsigned int) putPackedImage -> src_depth << ", " + << "dst_depth " << (unsigned int) putPackedImage -> dst_depth << ", " + + << "src_length " << putPackedImage -> src_length << ", " + << "dst_length " << putPackedImage -> dst_length << ", " + + << "src_x " << putPackedImage -> src_x << ", " + << "src_y " << putPackedImage -> src_y << ", " + << "src_width " << putPackedImage -> src_width << ", " + << "src_height " << putPackedImage -> src_height << ", " + + << "dst_x " << putPackedImage -> dst_x << ", " + << "dst_y " << putPackedImage -> dst_y << ", " + << "dst_width " << putPackedImage -> dst_width << ", " + << "dst_height " << putPackedImage -> dst_height << ", " + + << "size " << putPackedImage -> size_ << ".\n" + << logofs_flush; + + #endif +} + +void PutPackedImageStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // Fields method, format, src_depth, dst_depth, + // src_length, dst_length, src_x, src_y, src_width, + // src_height. + // + // + // TODO: We should better investigate the effect of + // having fields src_x and src_y in identity instead + // of keeping them in differences. + // + + md5_append(md5_state_, buffer + 12, 20); +} + +void PutPackedImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + // + // Encode the variant part. + // + + PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message; + PutPackedImageMessage *cachedPutPackedImage = (PutPackedImageMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " + << (unsigned int) putPackedImage -> client + << " as client field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(putPackedImage -> client, 8, + clientCache -> resourceCache); + + cachedPutPackedImage -> client = putPackedImage -> client; + + #ifdef TEST + *logofs << name() << ": Encoding value " << putPackedImage -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(putPackedImage -> drawable, clientCache -> drawableCache); + + cachedPutPackedImage -> drawable = putPackedImage -> drawable; + + #ifdef TEST + *logofs << name() << ": Encoding value " << putPackedImage -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(putPackedImage -> gcontext, clientCache -> gcCache); + + cachedPutPackedImage -> gcontext = putPackedImage -> gcontext; + + #ifdef TEST + *logofs << name() << ": Encoding value " << putPackedImage -> dst_x + << " as " << "dst_x" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_x = putPackedImage -> dst_x - cachedPutPackedImage -> dst_x; + + encodeBuffer.encodeCachedValue(diff_x, 16, + clientCache -> putImageXCache, 8); + + cachedPutPackedImage -> dst_x = putPackedImage -> dst_x; + + #ifdef TEST + *logofs << name() << ": Encoding value " << putPackedImage -> dst_y + << " as " << "dst_y" << " field.\n" << logofs_flush; + #endif + + unsigned short int diff_y = putPackedImage -> dst_y - cachedPutPackedImage -> dst_y; + + encodeBuffer.encodeCachedValue(diff_y, 16, + clientCache -> putImageYCache, 8); + + cachedPutPackedImage -> dst_y = putPackedImage -> dst_y; + + #ifdef TEST + *logofs << name() << ": Encoding value " << putPackedImage -> dst_width + << " as " << "dst_width" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(putPackedImage -> dst_width, 16, + clientCache -> putImageWidthCache, 8); + + cachedPutPackedImage -> dst_width = putPackedImage -> dst_width; + + #ifdef TEST + *logofs << name() << ": Encoding value " << putPackedImage -> dst_height + << " as " << "dst_height" << " field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(putPackedImage -> dst_height, 16, + clientCache -> putImageHeightCache, 8); + + cachedPutPackedImage -> dst_height = putPackedImage -> dst_height; +} + +void PutPackedImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + // + // Decode the variant part. + // + + PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeCachedValue(putPackedImage -> client, 8, + clientCache -> resourceCache); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " + << (unsigned int) putPackedImage -> client + << " as client field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> drawableCache); + + putPackedImage -> drawable = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << putPackedImage -> drawable + << " as drawable field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + putPackedImage -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << putPackedImage -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageXCache, 8); + + putPackedImage -> dst_x += value; + putPackedImage -> dst_x &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << putPackedImage -> dst_x + << " as dst_x field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageYCache, 8); + + putPackedImage -> dst_y += value; + putPackedImage -> dst_y &= 0xffff; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << putPackedImage -> dst_y + << " as dst_y field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageWidthCache, 8); + + putPackedImage -> dst_width = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << putPackedImage -> dst_width + << " as dst_width field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> putImageHeightCache, 8); + + putPackedImage -> dst_height = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << putPackedImage -> dst_height + << " as dst_height field.\n" << logofs_flush; + #endif +} + diff --git a/nxcomp/PutPackedImage.h b/nxcomp/PutPackedImage.h new file mode 100644 index 000000000..5287a5b57 --- /dev/null +++ b/nxcomp/PutPackedImage.h @@ -0,0 +1,211 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef PutPackedImage_H +#define PutPackedImage_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define PUTPACKEDIMAGE_ENABLE_CACHE 1 +#define PUTPACKEDIMAGE_ENABLE_DATA 1 +#define PUTPACKEDIMAGE_ENABLE_SPLIT 1 +#define PUTPACKEDIMAGE_ENABLE_COMPRESS 0 + +// +// We can't exceed lenght of 262144 bytes. +// + +#define PUTPACKEDIMAGE_DATA_LIMIT 262144 - 40 +#define PUTPACKEDIMAGE_DATA_OFFSET 40 + +#define PUTPACKEDIMAGE_CACHE_SLOTS 6000 +#define PUTPACKEDIMAGE_CACHE_THRESHOLD 70 +#define PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD 50 + +#define PUTPACKEDIMAGE_CACHE_THRESHOLD_IF_PACKED_SHADOW 97 +#define PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED_SHADOW 90 + +#define PUTPACKEDIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8 0 + +// +// The message class. +// + +class PutPackedImageMessage : public Message +{ + friend class PutPackedImageStore; + + public: + + PutPackedImageMessage() + { + } + + ~PutPackedImageMessage() + { + } + + // + // Here are the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char client; + + unsigned int drawable; + unsigned int gcontext; + + unsigned char format; + unsigned char method; + + unsigned char src_depth; + unsigned char dst_depth; + + unsigned int src_length; + unsigned int dst_length; + + short int src_x; + short int src_y; + unsigned short src_width; + unsigned short src_height; + + short int dst_x; + short int dst_y; + unsigned short dst_width; + unsigned short dst_height; +}; + +class PutPackedImageStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + PutPackedImageStore(StaticCompressor *compressor); + + virtual ~PutPackedImageStore(); + + virtual const char *name() const + { + return "PutPackedImage"; + } + + virtual unsigned char opcode() const + { + return X_NXPutPackedImage; + } + + virtual unsigned int storage() const + { + return sizeof(PutPackedImageMessage); + } + + // + // Very special of this class. + // + + int getPackedSize(const int position) const + { + PutPackedImageMessage *message = (PutPackedImageMessage *) (*messages_)[position]; + + if (message == NULL) + { + return 0; + } + + return dataOffset + message -> src_length; + } + + int getUnpackedSize(const int position) const + { + PutPackedImageMessage *message = (PutPackedImageMessage *) (*messages_)[position]; + + if (message == NULL) + { + return 0; + } + + return dataOffset + message -> dst_length; + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new PutPackedImageMessage(); + } + + virtual Message *create(const Message &message) const + { + return new PutPackedImageMessage((const PutPackedImageMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (PutPackedImageMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* PutPackedImage_H */ diff --git a/nxcomp/QueryFontReply.cpp b/nxcomp/QueryFontReply.cpp new file mode 100644 index 000000000..2d42587f7 --- /dev/null +++ b/nxcomp/QueryFontReply.cpp @@ -0,0 +1,145 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "QueryFontReply.h" + +#include "ServerCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +QueryFontReplyStore::QueryFontReplyStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = QUERYFONTREPLY_ENABLE_CACHE; + enableData = QUERYFONTREPLY_ENABLE_DATA; + enableSplit = QUERYFONTREPLY_ENABLE_SPLIT; + enableCompress = QUERYFONTREPLY_ENABLE_COMPRESS; + + if (control -> isProtoStep7() == 1) + { + enableCompress = QUERYFONTREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7; + } + + dataLimit = QUERYFONTREPLY_DATA_LIMIT; + dataOffset = QUERYFONTREPLY_DATA_OFFSET; + + cacheSlots = QUERYFONTREPLY_CACHE_SLOTS; + cacheThreshold = QUERYFONTREPLY_CACHE_THRESHOLD; + cacheLowerThreshold = QUERYFONTREPLY_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +QueryFontReplyStore::~QueryFontReplyStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int QueryFontReplyStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // Clear the padding bytes. + // + + unsigned char *pad = (unsigned char *) buffer; + + if (size >= 24) + { + PutULONG(0, pad + 20, bigEndian); + } + + if (size >= 40) + { + PutULONG(0, pad + 36, bigEndian); + } + + // + // TODO: This doesn't work. Probably these + // padding bytes are not padding anymore. + // This is to be investigated. + // + // pad += 60; + // + // while (pad + 16 <= (buffer + size)) + // { + // PutULONG(0, pad + 12, bigEndian); + // + // pad += 16; + // } + // + + #ifdef DEBUG + *logofs << name() << ": Cleaned padding bytes of " + << "message at " << message << ".\n" + << logofs_flush; + #endif + + return 1; +} + +int QueryFontReplyStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + return 1; +} + +void QueryFontReplyStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + QueryFontReplyMessage *queryFontReply = (QueryFontReplyMessage *) message; + + *logofs << name() << ": Identity size " << queryFontReply -> size_ << ".\n"; + + #endif +} + +void QueryFontReplyStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ +} diff --git a/nxcomp/QueryFontReply.h b/nxcomp/QueryFontReply.h new file mode 100644 index 000000000..537da4e63 --- /dev/null +++ b/nxcomp/QueryFontReply.h @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef QueryFontReply_H +#define QueryFontReply_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// +#define QUERYFONTREPLY_ENABLE_CACHE 1 +#define QUERYFONTREPLY_ENABLE_DATA 1 +#define QUERYFONTREPLY_ENABLE_SPLIT 0 +#define QUERYFONTREPLY_ENABLE_COMPRESS 1 + +#define QUERYFONTREPLY_DATA_LIMIT 1048576 - 32 +#define QUERYFONTREPLY_DATA_OFFSET 8 + +#define QUERYFONTREPLY_CACHE_SLOTS 200 +#define QUERYFONTREPLY_CACHE_THRESHOLD 20 +#define QUERYFONTREPLY_CACHE_LOWER_THRESHOLD 5 + +#define QUERYFONTREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 + +// +// The message class. +// + +class QueryFontReplyMessage : public Message +{ + friend class QueryFontReplyStore; + + public: + + QueryFontReplyMessage() + { + } + + ~QueryFontReplyMessage() + { + } +}; + +class QueryFontReplyStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + QueryFontReplyStore(StaticCompressor *compressor); + + virtual ~QueryFontReplyStore(); + + virtual const char *name() const + { + return "QueryFontReply"; + } + + virtual unsigned char opcode() const + { + return X_QueryFont; + } + + virtual unsigned int storage() const + { + return sizeof(QueryFontReplyMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new QueryFontReplyMessage(); + } + + virtual Message *create(const Message &message) const + { + return new QueryFontReplyMessage((const QueryFontReplyMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (QueryFontReplyMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* QueryFontReply_H */ diff --git a/nxcomp/README b/nxcomp/README new file mode 100644 index 000000000..f35cce6b5 --- /dev/null +++ b/nxcomp/README @@ -0,0 +1,21 @@ +README +------ + +Building +-------- + +1. To compile: + + > tar zxvf nxcomp-X.Y.Z-N.tar.gz + > cd nxcomp + > ./configure + > make + + You'll have to run gmake under Solaris. + +2. The 'make install' target is not currently supported + in the Makefile, but it should be simple to fix. + +You need at least nxproxy and nxagent packages to enjoy this code. Check the +NoMachine website at http://www.nomachine.com to get the latest release. + diff --git a/nxcomp/README-IPAQ b/nxcomp/README-IPAQ new file mode 100644 index 000000000..f9418635c --- /dev/null +++ b/nxcomp/README-IPAQ @@ -0,0 +1,21 @@ +README-IPAQ +----------- + +1. Install a cross-compiler for ARM. You can find detailed + informations at: + + http://www.ailis.de/~k/knowledge/crosscompiling/toolchain.php + + There are also binaries needed to install the cross-compiler. + +2. Configure and compile libXcomp using: + + $ ./configure --with-ipaq + $ make + + After compilation type: + + $ arm-linux-strip libXcomp.* + +3. Remember that you also need nxproxy to actually run your NX X + session. diff --git a/nxcomp/ReadBuffer.cpp b/nxcomp/ReadBuffer.cpp new file mode 100644 index 000000000..13122e278 --- /dev/null +++ b/nxcomp/ReadBuffer.cpp @@ -0,0 +1,627 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ReadBuffer.h" + +#include "Transport.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +ReadBuffer::ReadBuffer(Transport *transport) + + : transport_(transport) +{ + // + // The read buffer will grow until + // reaching the maximum buffer size + // and then will remain stable at + // that size. + // + + initialReadSize_ = READ_BUFFER_DEFAULT_SIZE; + maximumBufferSize_ = READ_BUFFER_DEFAULT_SIZE; + + size_ = 0; + buffer_ = NULL; + + owner_ = 1; + length_ = 0; + start_ = 0; + + remaining_ = 0; +} + +ReadBuffer::~ReadBuffer() +{ + if (owner_ == 1) + { + delete [] buffer_; + } +} + +void ReadBuffer::readMessage(const unsigned char *message, unsigned int length) +{ + // + // To be here we must be the real owner + // of the buffer and there must not be + // pending bytes in the transport. + // + + #ifdef TEST + + if (owner_ == 0) + { + *logofs << "ReadBuffer: PANIC! Class for FD#" + << transport_ -> fd() << " doesn't " + << "appear to be the owner of the buffer " + << "while borrowing from the caller.\n" + << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // Be sure that any outstanding data from + // the transport is appended to our own + // byffer. + // + + if (transport_ -> pending() != 0) + { + #ifdef WARNING + *logofs << "ReadBuffer: WARNING! Class for FD#" + << transport_ -> fd() << " has pending " + << "data in the transport while " + << "borrowing from the caller.\n" + << logofs_flush; + #endif + + readMessage(); + + if (owner_ == 0) + { + convertBuffer(); + } + } + + // + // Can't borrow the buffer if there is data + // from a partial message. In this case add + // the new data to the end of our buffer. + // + + if (length_ == 0) + { + #ifdef TEST + *logofs << "ReadBuffer: Borrowing " << length + << " bytes from the caller for FD#" + << transport_ -> fd() << " with " + << length_ << " bytes in the buffer.\n" + << logofs_flush; + #endif + + delete [] buffer_; + + buffer_ = (unsigned char *) message; + size_ = length; + + length_ = length; + + owner_ = 0; + start_ = 0; + } + else + { + #ifdef TEST + *logofs << "ReadBuffer: Appending " << length + << " bytes from the caller for FD#" + << transport_ -> fd() << " with " + << length_ << " bytes in the buffer.\n" + << logofs_flush; + #endif + + appendBuffer(message, length); + } +} + +int ReadBuffer::readMessage() +{ + int pendingLength = transport_ -> pending(); + + if (pendingLength > 0) + { + // + // Can't move the data in the borrowed buffer, + // so use the tansport buffer only if we don't + // have any partial message. This can happen + // with the proxy where we need to deflate the + // stream. + // + + if (length_ == 0) + { + unsigned char *newBuffer; + + length_ = transport_ -> getPending(newBuffer); + + if (newBuffer == NULL) + { + #ifdef PANIC + *logofs << "ReadBuffer: PANIC! Failed to borrow " + << length_ << " bytes of memory for buffer " + << "in context [A].\n" << logofs_flush; + #endif + + cerr << "Error" << ": Failed to borrow memory for " + << "read buffer in context [A].\n"; + + HandleCleanup(); + } + + delete [] buffer_; + + buffer_ = newBuffer; + size_ = length_; + + owner_ = 0; + start_ = 0; + + #ifdef TEST + *logofs << "ReadBuffer: Borrowed " << length_ + << " pending bytes for FD#" << transport_ -> + fd() << ".\n" << logofs_flush; + #endif + + return length_; + } + #ifdef TEST + else + { + *logofs << "ReadBuffer: WARNING! Cannot borrow " + << pendingLength << " bytes for FD#" + << transport_ -> fd() << " with " + << length_ << " bytes in the buffer.\n" + << logofs_flush; + } + #endif + } + + unsigned int readLength = suggestedLength(pendingLength); + + #ifdef DEBUG + *logofs << "ReadBuffer: Requested " << readLength + << " bytes for FD#" << transport_ -> fd() + << " with readable " << transport_ -> readable() + << " remaining " << remaining_ << " pending " + << transport_ -> pending() << ".\n" + << logofs_flush; + #endif + + if (readLength < initialReadSize_) + { + readLength = initialReadSize_; + } + + #ifdef DEBUG + *logofs << "ReadBuffer: Buffer size is " << size_ + << " length " << length_ << " and start " + << start_ << ".\n" << logofs_flush; + #endif + + // + // We can't use the transport buffer + // to read our own data in it. + // + + #ifdef TEST + + if (owner_ == 0) + { + *logofs << "ReadBuffer: PANIC! Class for FD#" + << transport_ -> fd() << " doesn't " + << "appear to be the owner of the buffer " + << "while reading.\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // Be sure that we have enough space + // to store all the requested data. + // + + if (buffer_ == NULL || length_ + readLength > size_) + { + unsigned int newSize = length_ + readLength; + + #ifdef TEST + *logofs << "ReadBuffer: Resizing buffer for FD#" + << transport_ -> fd() << " in read from " + << size_ << " to " << newSize << " bytes.\n" + << logofs_flush; + #endif + + unsigned char *newBuffer = allocateBuffer(newSize); + + memcpy(newBuffer, buffer_ + start_, length_); + + delete [] buffer_; + + buffer_ = newBuffer; + size_ = newSize; + + transport_ -> pendingReset(); + + owner_ = 1; + } + else if (start_ != 0 && length_ != 0) + { + // + // If any bytes are left due to a partial + // message, shift them to the beginning + // of the buffer. + // + + #ifdef TEST + *logofs << "ReadBuffer: Moving " << length_ + << " bytes of data " << "at beginning of " + << "the buffer for FD#" << transport_ -> fd() + << ".\n" << logofs_flush; + #endif + + memmove(buffer_, buffer_ + start_, length_); + } + + start_ = 0; + + #ifdef DEBUG + *logofs << "ReadBuffer: Buffer size is now " << size_ + << " length is " << length_ << " and start is " + << start_ << ".\n" << logofs_flush; + #endif + + unsigned char *readData = buffer_ + length_; + + #ifdef DEBUG + *logofs << "ReadBuffer: Going to read " << readLength + << " bytes from FD#" << transport_ -> fd() << ".\n" + << logofs_flush; + #endif + + int bytesRead = transport_ -> read(readData, readLength); + + if (bytesRead > 0) + { + #ifdef TEST + *logofs << "ReadBuffer: Read " << bytesRead + << " bytes from FD#" << transport_ -> fd() + << ".\n" << logofs_flush; + #endif + + length_ += bytesRead; + } + else if (bytesRead < 0) + { + // + // Check if there is more data pending than the + // size of the provided buffer. After reading + // the requested amount, in fact, the transport + // may have decompressed the data and produced + // more input. This trick allows us to always + // borrow the buffer from the transport, even + // when the partial read would have prevented + // that. + // + + if (transport_ -> pending() > 0) + { + #ifdef TEST + *logofs << "ReadBuffer: WARNING! Trying to read some " + << "more with " << transport_ -> pending() + << " bytes pending for FD#" << transport_ -> + fd() << ".\n" << logofs_flush; + #endif + + return readMessage(); + } + + #ifdef TEST + *logofs << "ReadBuffer: Error detected reading " + << "from FD#" << transport_ -> fd() + << ".\n" << logofs_flush; + #endif + + return -1; + } + #ifdef TEST + else + { + *logofs << "ReadBuffer: No data read from FD#" + << transport_ -> fd() << " with remaining " + << remaining_ << ".\n" << logofs_flush; + } + #endif + + return bytesRead; +} + +const unsigned char *ReadBuffer::getMessage(unsigned int &controlLength, + unsigned int &dataLength) +{ + #ifdef TEST + + if (transport_ -> pending() > 0) + { + *logofs << "ReadBuffer: PANIC! The transport " + << "appears to have data pending.\n" + << logofs_flush; + + HandleCleanup(); + } + + #endif + + if (length_ == 0) + { + #ifdef DEBUG + *logofs << "ReadBuffer: No message can be located " + << "for FD#" << transport_ -> fd() << ".\n" + << logofs_flush; + #endif + + if (owner_ == 0) + { + buffer_ = NULL; + size_ = 0; + + transport_ -> pendingReset(); + + owner_ = 1; + start_ = 0; + } + + return NULL; + } + + unsigned int trailerLength; + + #ifdef DEBUG + *logofs << "ReadBuffer: Going to locate message with " + << "start at " << start_ << " and length " + << length_ << " for FD#" << transport_ -> fd() + << ".\n" << logofs_flush; + #endif + + int located = locateMessage(buffer_ + start_, buffer_ + start_ + length_, + controlLength, dataLength, trailerLength); + + if (located == 0) + { + // + // No more complete messages are in + // the buffer. + // + + #ifdef DEBUG + *logofs << "ReadBuffer: No message was located " + << "for FD#" << transport_ -> fd() + << ".\n" << logofs_flush; + #endif + + if (owner_ == 0) + { + // + // Must move the remaining bytes in + // our own buffer. + // + + convertBuffer(); + } + + return NULL; + } + else + { + const unsigned char *result = buffer_ + start_; + + if (dataLength > 0) + { + // + // Message contains data, so go to the + // first byte of payload. + // + + result += trailerLength; + + start_ += (dataLength + trailerLength); + length_ -= (dataLength + trailerLength); + } + else + { + // + // It is a control message. + // + + start_ += (controlLength + trailerLength); + length_ -= (controlLength + trailerLength); + } + + #ifdef DEBUG + *logofs << "ReadBuffer: Located message for FD#" + << transport_ -> fd() << " with control length " + << controlLength << " and data length " + << dataLength << ".\n" << logofs_flush; + #endif + + remaining_ = 0; + + return result; + } +} + +int ReadBuffer::setSize(int initialReadSize, int maximumBufferSize) +{ + initialReadSize_ = initialReadSize; + maximumBufferSize_ = maximumBufferSize; + + #ifdef TEST + *logofs << "ReadBuffer: WARNING! Set buffer parameters to " + << initialReadSize_ << "/" << maximumBufferSize_ + << " for object at "<< this << ".\n" + << logofs_flush; + #endif + + return 1; +} + +void ReadBuffer::fullReset() +{ + #ifdef TEST + + if (owner_ == 0) + { + *logofs << "ReadBuffer: PANIC! Class for FD#" + << transport_ -> fd() << " doesn't " + << "appear to be the owner of the buffer " + << "in reset.\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + if (length_ == 0 && size_ > maximumBufferSize_) + { + #ifdef TEST + *logofs << "ReadBuffer: Resizing buffer for FD#" + << transport_ -> fd() << " in reset from " + << size_ << " to " << maximumBufferSize_ + << " bytes.\n" << logofs_flush; + #endif + + delete [] buffer_; + + int newSize = maximumBufferSize_; + + unsigned char *newBuffer = allocateBuffer(newSize); + + buffer_ = newBuffer; + size_ = newSize; + + transport_ -> pendingReset(); + + owner_ = 1; + start_ = 0; + } +} + +unsigned char *ReadBuffer::allocateBuffer(unsigned int newSize) +{ + unsigned char *newBuffer = new unsigned char[newSize]; + + if (newBuffer == NULL) + { + #ifdef PANIC + *logofs << "ReadBuffer: PANIC! Can't allocate " + << newSize << " bytes of memory for buffer " + << "in context [B].\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "read buffer in context [B].\n"; + + HandleCleanup(); + } + + #ifdef VALGRIND + + memset(newBuffer, '\0', newSize); + + #endif + + return newBuffer; +} + +void ReadBuffer::appendBuffer(const unsigned char *message, unsigned int length) +{ + if (start_ + length_ + length > size_) + { + unsigned int newSize = length_ + length + initialReadSize_; + + #ifdef TEST + *logofs << "ReadBuffer: WARNING! Resizing buffer " + << "for FD#" << transport_ -> fd() + << " from " << size_ << " to " << newSize + << " bytes.\n" << logofs_flush; + #endif + + unsigned char *newBuffer = allocateBuffer(newSize); + + memcpy(newBuffer, buffer_ + start_, length_); + + delete [] buffer_; + + buffer_ = newBuffer; + size_ = newSize; + + start_ = 0; + } + + memcpy(buffer_ + start_ + length_, message, length); + + length_ += length; + + transport_ -> pendingReset(); + + owner_ = 1; +} + +void ReadBuffer::convertBuffer() +{ + unsigned int newSize = length_ + initialReadSize_; + + #ifdef TEST + *logofs << "ReadBuffer: WARNING! Converting " + << length_ << " bytes to own buffer " + << "for FD#" << transport_ -> fd() + << " with new size " << newSize + << " bytes.\n" << logofs_flush; + #endif + + unsigned char *newBuffer = allocateBuffer(newSize); + + memcpy(newBuffer, buffer_ + start_, length_); + + buffer_ = newBuffer; + size_ = newSize; + + transport_ -> pendingReset(); + + owner_ = 1; + start_ = 0; +} diff --git a/nxcomp/ReadBuffer.h b/nxcomp/ReadBuffer.h new file mode 100644 index 000000000..20130120a --- /dev/null +++ b/nxcomp/ReadBuffer.h @@ -0,0 +1,120 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ReadBuffer_H +#define ReadBuffer_H + +#include "Misc.h" +#include "Timestamp.h" +#include "Transport.h" + +#define READ_BUFFER_DEFAULT_SIZE 8192 + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +class ReadBuffer +{ + public: + + ReadBuffer(Transport *transport); + + virtual ~ReadBuffer(); + + int readMessage(); + + void readMessage(const unsigned char *message, unsigned int length); + + const unsigned char *getMessage(unsigned int &dataLength) + { + unsigned int controlLength; + + return getMessage(controlLength, dataLength); + } + + const unsigned char *getMessage(unsigned int &controlLength, unsigned int &dataLength); + + unsigned int getLength() const + { + return length_; + } + + unsigned int getRemaining() const + { + return remaining_; + } + + int setSize(int initialReadSize, int initialbufferSize); + + void fullReset(); + + // + // Check if there is a complete + // message in the buffer. + // + + int checkMessage() + { + if (length_ == 0) + { + return 0; + } + else + { + unsigned int controlLength; + unsigned int dataLength; + unsigned int trailerLength; + + return (locateMessage(buffer_ + start_, buffer_ + start_ + length_, + controlLength, dataLength, trailerLength)); + } + } + + protected: + + virtual unsigned int suggestedLength(unsigned int pendingLength) = 0; + + virtual int locateMessage(const unsigned char *start, + const unsigned char *end, + unsigned int &controlLength, + unsigned int &dataLength, + unsigned int &trailerLength) = 0; + + unsigned char *allocateBuffer(unsigned int newSize); + + void appendBuffer(const unsigned char *message, unsigned int length); + + void convertBuffer(); + + Transport *transport_; + + unsigned char *buffer_; + + unsigned int length_; + unsigned int size_; + unsigned int start_; + unsigned int remaining_; + + int owner_; + + unsigned int initialReadSize_; + unsigned int maximumBufferSize_; +}; + +#endif /* ReadBuffer_H */ diff --git a/nxcomp/RenderAddGlyphs.cpp b/nxcomp/RenderAddGlyphs.cpp new file mode 100644 index 000000000..1d53ec0f8 --- /dev/null +++ b/nxcomp/RenderAddGlyphs.cpp @@ -0,0 +1,221 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderAddGlyphs.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 4, bigEndian), 29, + clientCache -> renderGlyphSetCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 32, + clientCache -> renderNumGlyphsCache, 8); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeCachedValue(value, 29, + clientCache -> renderGlyphSetCache); + + PutULONG(value, buffer + 4, bigEndian); + + decodeBuffer.decodeCachedValue(value, 32, + clientCache -> renderNumGlyphsCache, 8); + + PutULONG(value, buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.add_glyphs.type = *(buffer + 1); + + renderExtension -> data.add_glyphs.set_id = GetULONG(buffer + 4, bigEndian); + renderExtension -> data.add_glyphs.num_elm = GetULONG(buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.add_glyphs.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.add_glyphs.type; + + PutULONG(renderExtension -> data.add_glyphs.set_id, buffer + 4, bigEndian); + PutULONG(renderExtension -> data.add_glyphs.num_elm, buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.add_glyphs.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); + md5_append(md5_state, buffer + 8, 4); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(renderExtension -> data.add_glyphs.set_id, 29, + clientCache -> renderGlyphSetCache); + + cachedRenderExtension -> data.add_glyphs.set_id = + renderExtension -> data.add_glyphs.set_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.add_glyphs.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(renderExtension -> data.add_glyphs.set_id, 29, + clientCache -> renderGlyphSetCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.add_glyphs.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderAddGlyphs.h b/nxcomp/RenderAddGlyphs.h new file mode 100644 index 000000000..918a70c8d --- /dev/null +++ b/nxcomp/RenderAddGlyphs.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderAddGlyphs_H +#define RenderAddGlyphs_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderAddGlyphs" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderAddGlyphsStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 12 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderAddGlyphs_H */ diff --git a/nxcomp/RenderChangePicture.cpp b/nxcomp/RenderChangePicture.cpp new file mode 100644 index 000000000..5dbe39d52 --- /dev/null +++ b/nxcomp/RenderChangePicture.cpp @@ -0,0 +1,226 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderChangePicture.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> renderSrcPictureCache); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + #ifdef DEBUG + + if (size == MESSAGE_OFFSET + 4) + { + *logofs << name() << ": Mask is " << GetULONG(buffer + 8, bigEndian) + << " value is " << GetULONG(buffer + 12, bigEndian) + << ".\n" << logofs_flush; + } + else + { + *logofs << name() << ": WARNING! Unexpected size. Mask is " + << GetULONG(buffer + 8, bigEndian) << ".\n" + << logofs_flush; + } + + #endif + + encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.change_picture.type = *(buffer + 1); + + renderExtension -> data.change_picture.src_id = GetULONG(buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.change_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.change_picture.type; + + PutULONG(renderExtension -> data.change_picture.src_id, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.change_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(renderExtension -> data.change_picture.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.change_picture.src_id = + renderExtension -> data.change_picture.src_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.change_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.change_picture.src_id, + clientCache -> renderSrcPictureCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.change_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderChangePicture.h b/nxcomp/RenderChangePicture.h new file mode 100644 index 000000000..e6a89a610 --- /dev/null +++ b/nxcomp/RenderChangePicture.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderChangePicture_H +#define RenderChangePicture_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderChangePicture" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderChangePictureStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 8 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderChangePicture_H */ diff --git a/nxcomp/RenderComposite.cpp b/nxcomp/RenderComposite.cpp new file mode 100644 index 000000000..e3c121b48 --- /dev/null +++ b/nxcomp/RenderComposite.cpp @@ -0,0 +1,388 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderComposite.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), + clientCache -> renderMaskPictureCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 16, bigEndian), + clientCache -> renderDstPictureCache); + + // + // Src X and Y. + // + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian), + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian), + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + // + // Mask X and Y. + // + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 24, bigEndian), + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 26, bigEndian), + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + // + // Dst X and Y. + // + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 28, bigEndian), + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 30, bigEndian), + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + // + // Width and height. + // + + encodeBuffer.encodeCachedValue(GetUINT(buffer + 32, bigEndian), 16, + clientCache -> renderWidthCache, 11); + + encodeBuffer.encodeCachedValue(GetUINT(buffer + 34, bigEndian), 16, + clientCache -> renderHeightCache, 11); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeXidValue(value, clientCache -> renderMaskPictureCache); + + PutULONG(value, buffer + 12, bigEndian); + + decodeBuffer.decodeXidValue(value, clientCache -> renderDstPictureCache); + + PutULONG(value, buffer + 16, bigEndian); + + // + // Src X and Y. + // + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian); + + // + // Mask X and Y. + // + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + PutUINT(clientCache -> renderLastX, buffer + 24, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + PutUINT(clientCache -> renderLastY, buffer + 26, bigEndian); + + // + // Dst X and Y. + // + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + PutUINT(clientCache -> renderLastX, buffer + 28, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + PutUINT(clientCache -> renderLastY, buffer + 30, bigEndian); + + // + // Width and height. + // + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> renderWidthCache, 11); + + PutUINT(value, buffer + 32, bigEndian); + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> renderHeightCache, 11); + + PutUINT(value, buffer + 34, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.composite.type = *(buffer + 1); + renderExtension -> data.composite.op = *(buffer + 4); + + renderExtension -> data.composite.src_id = GetULONG(buffer + 8, bigEndian); + renderExtension -> data.composite.msk_id = GetULONG(buffer + 12, bigEndian); + renderExtension -> data.composite.dst_id = GetULONG(buffer + 16, bigEndian); + + renderExtension -> data.composite.src_x = GetUINT(buffer + 20, bigEndian); + renderExtension -> data.composite.src_y = GetUINT(buffer + 22, bigEndian); + + renderExtension -> data.composite.msk_x = GetUINT(buffer + 24, bigEndian); + renderExtension -> data.composite.msk_y = GetUINT(buffer + 26, bigEndian); + + renderExtension -> data.composite.dst_x = GetUINT(buffer + 28, bigEndian); + renderExtension -> data.composite.dst_y = GetUINT(buffer + 30, bigEndian); + + renderExtension -> data.composite.width = GetUINT(buffer + 32, bigEndian); + renderExtension -> data.composite.height = GetUINT(buffer + 34, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.composite.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.composite.type; + *(buffer + 4) = renderExtension -> data.composite.op; + + PutULONG(renderExtension -> data.composite.src_id, buffer + 8, bigEndian); + PutULONG(renderExtension -> data.composite.msk_id, buffer + 12, bigEndian); + PutULONG(renderExtension -> data.composite.dst_id, buffer + 16, bigEndian); + + PutUINT(renderExtension -> data.composite.src_x, buffer + 20, bigEndian); + PutUINT(renderExtension -> data.composite.src_y, buffer + 22, bigEndian); + + PutUINT(renderExtension -> data.composite.msk_x, buffer + 24, bigEndian); + PutUINT(renderExtension -> data.composite.msk_y, buffer + 26, bigEndian); + + PutUINT(renderExtension -> data.composite.dst_x, buffer + 28, bigEndian); + PutUINT(renderExtension -> data.composite.dst_y, buffer + 30, bigEndian); + + PutUINT(renderExtension -> data.composite.width, buffer + 32, bigEndian); + PutUINT(renderExtension -> data.composite.height, buffer + 34, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.composite.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + // + // Include the minor opcode and size in the + // identity, plus the operator, the x and y + // of the source and mask and the width and + // height of the destination. + // + + md5_append(md5_state, buffer + 1, 4); + md5_append(md5_state, buffer + 20, 8); + md5_append(md5_state, buffer + 32, 4); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Source " << renderExtension -> data.composite.src_id + << " mask " << renderExtension -> data.composite.msk_id + << " destination " << renderExtension -> data.composite.msk_id + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(renderExtension -> data.composite.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.composite.src_id = + renderExtension -> data.composite.src_id; + + encodeBuffer.encodeXidValue(renderExtension -> data.composite.msk_id, + clientCache -> renderMaskPictureCache); + + cachedRenderExtension -> data.composite.msk_id = + renderExtension -> data.composite.msk_id; + + encodeBuffer.encodeXidValue(renderExtension -> data.composite.dst_id, + clientCache -> renderDstPictureCache); + + cachedRenderExtension -> data.composite.dst_id = + renderExtension -> data.composite.dst_id; + + // + // Dst X and Y. + // + + unsigned int value; + unsigned int previous; + + value = renderExtension -> data.composite.dst_x; + previous = cachedRenderExtension -> data.composite.dst_x; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderXCache, 11); + + cachedRenderExtension -> data.composite.dst_x = value; + + value = renderExtension -> data.composite.dst_y; + previous = cachedRenderExtension -> data.composite.dst_y; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderYCache, 11); + + cachedRenderExtension -> data.composite.dst_y = value; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.composite.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.composite.src_id, + clientCache -> renderSrcPictureCache); + + decodeBuffer.decodeXidValue(renderExtension -> data.composite.msk_id, + clientCache -> renderMaskPictureCache); + + decodeBuffer.decodeXidValue(renderExtension -> data.composite.dst_id, + clientCache -> renderDstPictureCache); + + // + // Dst X and Y. + // + + unsigned int value; + unsigned int previous; + + previous = renderExtension -> data.composite.dst_x; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderXCache, 11); + + renderExtension -> data.composite.dst_x = value; + + previous = renderExtension -> data.composite.dst_y; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderYCache, 11); + + renderExtension -> data.composite.dst_y = value; + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.composite.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderComposite.h b/nxcomp/RenderComposite.h new file mode 100644 index 000000000..91fa30aaa --- /dev/null +++ b/nxcomp/RenderComposite.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderComposite_H +#define RenderComposite_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderComposite" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderCompositeStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 36 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 0 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 0 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderComposite_H */ diff --git a/nxcomp/RenderCompositeCompat.cpp b/nxcomp/RenderCompositeCompat.cpp new file mode 100644 index 000000000..5a1eff213 --- /dev/null +++ b/nxcomp/RenderCompositeCompat.cpp @@ -0,0 +1,320 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderCompositeCompat.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + // + // Strictly speaking this request doesn't have + // a data part. We just encode the field from + // offset 24 to 36 as they were data using an + // int cache. + // + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + size = MESSAGE_OFFSET + 12; + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 16, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian), + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian), + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 12, bigEndian); + + decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 16, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + for (unsigned int i = MESSAGE_OFFSET, c = 0; i < size; i += 4) + { + #ifdef DEBUG + *logofs << name() << ": Encoding long value " + << GetULONG(buffer + i, bigEndian) << " with i = " + << i << " c = " << c << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(GetULONG(buffer + i, bigEndian), 32, + *clientCache -> renderCompositeDataCache[c]); + + if (++c == 3) c = 0; + } + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + for (unsigned int i = MESSAGE_OFFSET, c = 0; i < size; i += 4) + { + decodeBuffer.decodeCachedValue(value, 32, + *clientCache -> renderCompositeDataCache[c]); + + #ifdef DEBUG + *logofs << name() << ": Decoded long value " << value + << " with i = " << i << " c = " << c << ".\n" + << logofs_flush; + #endif + + PutULONG(value, buffer + i, bigEndian); + + if (++c == 3) c = 0; + } + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.composite.type = *(buffer + 1); + renderExtension -> data.composite.op = *(buffer + 4); + + renderExtension -> data.composite.src_id = GetULONG(buffer + 8, bigEndian); + renderExtension -> data.composite.msk_id = GetULONG(buffer + 12, bigEndian); + renderExtension -> data.composite.dst_id = GetULONG(buffer + 16, bigEndian); + + renderExtension -> data.composite.src_x = GetUINT(buffer + 20, bigEndian); + renderExtension -> data.composite.src_y = GetUINT(buffer + 22, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.composite.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.composite.type; + *(buffer + 4) = renderExtension -> data.composite.op; + + PutULONG(renderExtension -> data.composite.src_id, buffer + 8, bigEndian); + PutULONG(renderExtension -> data.composite.msk_id, buffer + 12, bigEndian); + PutULONG(renderExtension -> data.composite.dst_id, buffer + 16, bigEndian); + + PutUINT(renderExtension -> data.composite.src_x, buffer + 20, bigEndian); + PutUINT(renderExtension -> data.composite.src_y, buffer + 22, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.composite.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + // + // Include minor opcode, size and + // operator in the identity, plus + // the x and y of the source. + // + + md5_append(md5_state, buffer + 1, 4); + md5_append(md5_state, buffer + 20, 4); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Source " << renderExtension -> + data.composite.src_id << " mask " << renderExtension -> + data.composite.msk_id << " destination " << renderExtension -> + data.composite.msk_id << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(renderExtension -> data.composite.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.composite.src_id = + renderExtension -> data.composite.src_id; + + encodeBuffer.encodeXidValue(renderExtension -> data.composite.msk_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.composite.msk_id = + renderExtension -> data.composite.msk_id; + + encodeBuffer.encodeXidValue(renderExtension -> data.composite.dst_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.composite.dst_id = + renderExtension -> data.composite.dst_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.composite.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.composite.src_id, + clientCache -> renderSrcPictureCache); + + decodeBuffer.decodeXidValue(renderExtension -> data.composite.msk_id, + clientCache -> renderSrcPictureCache); + + decodeBuffer.decodeXidValue(renderExtension -> data.composite.dst_id, + clientCache -> renderSrcPictureCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.composite.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderCompositeCompat.h b/nxcomp/RenderCompositeCompat.h new file mode 100644 index 000000000..a26db35ba --- /dev/null +++ b/nxcomp/RenderCompositeCompat.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderCompositeCompat_H +#define RenderCompositeCompat_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderCompositeCompat" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderCompositeCompatStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 24 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderCompositeCompat_H */ diff --git a/nxcomp/RenderCompositeGlyphs.cpp b/nxcomp/RenderCompositeGlyphs.cpp new file mode 100644 index 000000000..1135633ff --- /dev/null +++ b/nxcomp/RenderCompositeGlyphs.cpp @@ -0,0 +1,679 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderCompositeGlyphs.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding value " + << ((size - MESSAGE_OFFSET) >> 2) << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << size + << ".\n" << logofs_flush; + #endif + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), + clientCache -> renderDstPictureCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, + clientCache -> renderFormatCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 29, + clientCache -> renderGlyphSetCache); + + unsigned int src_x = GetUINT(buffer + 24, bigEndian); + unsigned int src_y = GetUINT(buffer + 26, bigEndian); + + if (control -> isProtoStep8() == 1) + { + encodeBuffer.encodeDiffCachedValue(src_x, + clientCache -> renderGlyphX, 16, + clientCache -> renderGlyphXCache, 11); + + encodeBuffer.encodeDiffCachedValue(src_y, + clientCache -> renderGlyphY, 16, + clientCache -> renderGlyphYCache, 11); + } + else + { + encodeBuffer.encodeDiffCachedValue(src_x, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + encodeBuffer.encodeDiffCachedValue(src_y, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + } + + #ifdef TEST + *logofs << name() << ": Encoded source X " + << GetUINT(buffer + 24, bigEndian) << " source Y " + << GetUINT(buffer + 26, bigEndian) << ".\n" + << logofs_flush; + #endif + + // + // Bytes from 28 to 36 contain in the order: + // + // 1 byte for the length of the first string. + // 3 bytes of padding. + // 2 bytes for the X offset. + // 2 bytes for the Y offset. + // + // Encode these bytes differentially to match + // all the strings that have equal glyphs. + // + // Only manage the first string of glyphs. The + // others strings should match, if they contain + // the same glyphs, since the offset are rela- + // tive to the first offset coordinates. + // + + if (control -> isProtoStep8() == 1 && + size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) + { + unsigned int numGlyphs = *(buffer + 28); + + encodeBuffer.encodeCachedValue(numGlyphs, 8, + clientCache -> renderNumGlyphsCache); + + unsigned int offset_x = GetUINT(buffer + 32, bigEndian); + unsigned int offset_y = GetUINT(buffer + 34, bigEndian); + + if (offset_x == src_x && offset_y == src_y) + { + encodeBuffer.encodeBoolValue(0); + + #ifdef TEST + *logofs << name() << ": Matched offset X " + << GetUINT(buffer + 32, bigEndian) << " offset Y " + << GetUINT(buffer + 34, bigEndian) << ".\n" + << logofs_flush; + #endif + } + else + { + encodeBuffer.encodeBoolValue(1); + + encodeBuffer.encodeDiffCachedValue(offset_x, + clientCache -> renderGlyphX, 16, + clientCache -> renderGlyphXCache, 11); + + encodeBuffer.encodeDiffCachedValue(offset_y, + clientCache -> renderGlyphY, 16, + clientCache -> renderGlyphYCache, 11); + + #ifdef TEST + *logofs << name() << ": Missed offset X " + << GetUINT(buffer + 32, bigEndian) << " offset Y " + << GetUINT(buffer + 34, bigEndian) << ".\n" + << logofs_flush; + #endif + } + } + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + decodeBuffer.decodeXidValue(value, + clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeXidValue(value, + clientCache -> renderDstPictureCache); + + PutULONG(value, buffer + 12, bigEndian); + + decodeBuffer.decodeCachedValue(value, 32, + clientCache -> renderFormatCache); + + PutULONG(value, buffer + 16, bigEndian); + + decodeBuffer.decodeCachedValue(value, 29, + clientCache -> renderGlyphSetCache); + + PutULONG(value, buffer + 20, bigEndian); + + unsigned int src_x; + unsigned int src_y; + + if (control -> isProtoStep8() == 1) + { + decodeBuffer.decodeDiffCachedValue(src_x, + clientCache -> renderGlyphX, 16, + clientCache -> renderGlyphXCache, 11); + + decodeBuffer.decodeDiffCachedValue(src_y, + clientCache -> renderGlyphY, 16, + clientCache -> renderGlyphYCache, 11); + } + else + { + decodeBuffer.decodeDiffCachedValue(src_x, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + decodeBuffer.decodeDiffCachedValue(src_y, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + } + + PutUINT(src_x, buffer + 24, bigEndian); + PutUINT(src_y, buffer + 26, bigEndian); + + if (control -> isProtoStep8() == 1 && + size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) + { + decodeBuffer.decodeCachedValue(value, 8, + clientCache -> renderNumGlyphsCache); + + *(buffer + 28) = value; + + decodeBuffer.decodeBoolValue(value); + + if (value == 0) + { + PutUINT(src_x, buffer + 32, bigEndian); + PutUINT(src_y, buffer + 34, bigEndian); + } + else + { + decodeBuffer.decodeDiffCachedValue(src_x, + clientCache -> renderGlyphX, 16, + clientCache -> renderGlyphXCache, 11); + + PutUINT(src_x, buffer + 32, bigEndian); + + decodeBuffer.decodeDiffCachedValue(src_y, + clientCache -> renderGlyphY, 16, + clientCache -> renderGlyphYCache, 11); + + PutUINT(src_y, buffer + 34, bigEndian); + } + } + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + if (control -> isProtoStep8() == 1 && + size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) + { + encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET_IF_PROTO_STEP_8, + size, bigEndian, channelCache); + } + else if (size > MESSAGE_OFFSET) + { + encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + } + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of text data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + if (control -> isProtoStep8() == 1 && + size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) + { + decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET_IF_PROTO_STEP_8, + size, bigEndian, channelCache); + } + else if (size > MESSAGE_OFFSET) + { + decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + } + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.composite_glyphs.type = *(buffer + 1); + renderExtension -> data.composite_glyphs.op = *(buffer + 4); + + renderExtension -> data.composite_glyphs.src_id = GetULONG(buffer + 8, bigEndian); + renderExtension -> data.composite_glyphs.dst_id = GetULONG(buffer + 12, bigEndian); + + renderExtension -> data.composite_glyphs.format = GetULONG(buffer + 16, bigEndian); + renderExtension -> data.composite_glyphs.set_id = GetULONG(buffer + 20, bigEndian); + + renderExtension -> data.composite_glyphs.src_x = GetUINT(buffer + 24, bigEndian); + renderExtension -> data.composite_glyphs.src_y = GetUINT(buffer + 26, bigEndian); + + if (control -> isProtoStep8() == 1 && + size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) + { + renderExtension -> data.composite_glyphs.num_elm = *(buffer + 28); + + renderExtension -> data.composite_glyphs.offset_x = GetUINT(buffer + 32, bigEndian); + renderExtension -> data.composite_glyphs.offset_y = GetUINT(buffer + 34, bigEndian); + } + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.composite_glyphs.type + << " size is " << renderExtension -> size_ << " identity size " + << renderExtension -> i_size_ << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.composite_glyphs.type; + *(buffer + 4) = renderExtension -> data.composite_glyphs.op; + + PutULONG(renderExtension -> data.composite_glyphs.src_id, buffer + 8, bigEndian); + PutULONG(renderExtension -> data.composite_glyphs.dst_id, buffer + 12, bigEndian); + + PutULONG(renderExtension -> data.composite_glyphs.format, buffer + 16, bigEndian); + PutULONG(renderExtension -> data.composite_glyphs.set_id, buffer + 20, bigEndian); + + PutUINT(renderExtension -> data.composite_glyphs.src_x, buffer + 24, bigEndian); + PutUINT(renderExtension -> data.composite_glyphs.src_y, buffer + 26, bigEndian); + + if (control -> isProtoStep8() == 1 && + size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) + { + *(buffer + 28) = renderExtension -> data.composite_glyphs.num_elm; + + PutUINT(renderExtension -> data.composite_glyphs.offset_x, buffer + 32, bigEndian); + PutUINT(renderExtension -> data.composite_glyphs.offset_y, buffer + 34, bigEndian); + } + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.composite_glyphs.type + << " size is " << renderExtension -> size_ << " identity size " + << renderExtension -> i_size_ << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + // + // Include minor opcode, size and + // the composite operator in the + // identity. + // + + md5_append(md5_state, buffer + 1, 4); + + // + // Include the format. + // + + md5_append(md5_state, buffer + 16, 4); + + // + // Also include the length of the + // first string. + // + + if (control -> isProtoStep8() == 1 && + size >= MESSAGE_OFFSET_IF_PROTO_STEP_8) + { + md5_append(md5_state, buffer + 28, 1); + } +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.composite_glyphs.src_id = + renderExtension -> data.composite_glyphs.src_id; + + encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs.dst_id, + clientCache -> renderDstPictureCache); + + cachedRenderExtension -> data.composite_glyphs.dst_id = + renderExtension -> data.composite_glyphs.dst_id; + + encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs.set_id, 29, + clientCache -> renderGlyphSetCache); + + cachedRenderExtension -> data.composite_glyphs.set_id = + renderExtension -> data.composite_glyphs.set_id; + + // + // Src X and Y. + // + // The source X and Y coordinates are + // encoded as differerences in respect + // to the cached message. + // + + unsigned int value; + unsigned int previous; + + if (control -> isProtoStep8() == 1) + { + value = renderExtension -> data.composite_glyphs.src_x; + previous = cachedRenderExtension -> data.composite_glyphs.src_x; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderGlyphXCache, 11); + + cachedRenderExtension -> data.composite_glyphs.src_x = value; + + value = renderExtension -> data.composite_glyphs.src_y; + previous = cachedRenderExtension -> data.composite_glyphs.src_y; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderGlyphYCache, 11); + + cachedRenderExtension -> data.composite_glyphs.src_y = value; + } + else + { + value = renderExtension -> data.composite_glyphs.src_x; + previous = cachedRenderExtension -> data.composite_glyphs.src_x; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderXCache, 11); + + cachedRenderExtension -> data.composite_glyphs.src_x = value; + + value = renderExtension -> data.composite_glyphs.src_y; + previous = cachedRenderExtension -> data.composite_glyphs.src_y; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderYCache, 11); + + cachedRenderExtension -> data.composite_glyphs.src_y = value; + } + + #ifdef TEST + *logofs << name() << ": Encoded source X " + << renderExtension -> data.composite_glyphs.src_x << " source Y " + << renderExtension -> data.composite_glyphs.src_y << ".\n" + << logofs_flush; + #endif + + if (control -> isProtoStep8() == 1 && + renderExtension -> size_ >= MESSAGE_OFFSET_IF_PROTO_STEP_8) + { + // + // Offset X and Y. + // + + if (renderExtension -> data.composite_glyphs.offset_x == + renderExtension -> data.composite_glyphs.src_x && + renderExtension -> data.composite_glyphs.offset_y == + renderExtension -> data.composite_glyphs.src_y) + { + encodeBuffer.encodeBoolValue(0); + + cachedRenderExtension -> data.composite_glyphs.offset_x = + renderExtension -> data.composite_glyphs.offset_x; + + cachedRenderExtension -> data.composite_glyphs.offset_y = + renderExtension -> data.composite_glyphs.offset_y; + + #ifdef TEST + *logofs << name() << ": Matched offset X " + << renderExtension -> data.composite_glyphs.offset_x << " offset Y " + << renderExtension -> data.composite_glyphs.offset_y << ".\n" + << logofs_flush; + #endif + } + else + { + encodeBuffer.encodeBoolValue(1); + + value = renderExtension -> data.composite_glyphs.offset_x; + previous = cachedRenderExtension -> data.composite_glyphs.offset_x; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderGlyphXCache, 11); + + cachedRenderExtension -> data.composite_glyphs.offset_x = value; + + value = renderExtension -> data.composite_glyphs.offset_y; + previous = cachedRenderExtension -> data.composite_glyphs.offset_y; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderGlyphYCache, 11); + + cachedRenderExtension -> data.composite_glyphs.offset_y = value; + + #ifdef TEST + *logofs << name() << ": Missed offset X " + << renderExtension -> data.composite_glyphs.offset_x << " offset Y " + << renderExtension -> data.composite_glyphs.offset_y << ".\n" + << logofs_flush; + #endif + } + } + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.composite_glyphs.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs.src_id, + clientCache -> renderSrcPictureCache); + + decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs.dst_id, + clientCache -> renderDstPictureCache); + + decodeBuffer.decodeCachedValue(renderExtension -> data.composite_glyphs.set_id, 29, + clientCache -> renderGlyphSetCache); + + // + // Src X and Y. + // + + unsigned int value; + unsigned int previous; + + if (control -> isProtoStep8() == 1) + { + previous = renderExtension -> data.composite_glyphs.src_x; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderGlyphXCache, 11); + + renderExtension -> data.composite_glyphs.src_x = value; + + previous = renderExtension -> data.composite_glyphs.src_y; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderGlyphYCache, 11); + + renderExtension -> data.composite_glyphs.src_y = value; + } + else + { + previous = renderExtension -> data.composite_glyphs.src_x; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderXCache, 11); + + renderExtension -> data.composite_glyphs.src_x = value; + + previous = renderExtension -> data.composite_glyphs.src_y; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderYCache, 11); + + renderExtension -> data.composite_glyphs.src_y = value; + } + + if (control -> isProtoStep8() == 1 && + renderExtension -> size_ >= MESSAGE_OFFSET_IF_PROTO_STEP_8) + { + // + // Offset X and Y. + // + + decodeBuffer.decodeBoolValue(value); + + if (value == 0) + { + renderExtension -> data.composite_glyphs.offset_x = + renderExtension -> data.composite_glyphs.src_x; + + renderExtension -> data.composite_glyphs.offset_y = + renderExtension -> data.composite_glyphs.src_y; + } + else + { + previous = renderExtension -> data.composite_glyphs.offset_x; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderGlyphXCache, 11); + + renderExtension -> data.composite_glyphs.offset_x = value; + + previous = renderExtension -> data.composite_glyphs.offset_y; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderGlyphYCache, 11); + + renderExtension -> data.composite_glyphs.offset_y = value; + } + } + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.composite_glyphs.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderCompositeGlyphs.h b/nxcomp/RenderCompositeGlyphs.h new file mode 100644 index 000000000..527fd3d12 --- /dev/null +++ b/nxcomp/RenderCompositeGlyphs.h @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderCompositeGlyphs_H +#define RenderCompositeGlyphs_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderCompositeGlyphs" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderCompositeGlyphsStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 28 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Encode the first 8 bytes of the +// data differentially in newer +// protocol versions. +// + +#undef MESSAGE_OFFSET_IF_PROTO_STEP_8 +#define MESSAGE_OFFSET_IF_PROTO_STEP_8 36 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + unsigned int offset = (control -> isProtoStep8() == 1 ? + MESSAGE_OFFSET_IF_PROTO_STEP_8 : + MESSAGE_OFFSET); + + return (size >= offset ? offset : size); + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderCompositeGlyphs_H */ diff --git a/nxcomp/RenderCompositeGlyphsCompat.cpp b/nxcomp/RenderCompositeGlyphsCompat.cpp new file mode 100644 index 000000000..3fe10fafb --- /dev/null +++ b/nxcomp/RenderCompositeGlyphsCompat.cpp @@ -0,0 +1,602 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderCompositeGlyphsCompat.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + // + // The offset points 8 bytes after + // the beginning of the data part. + // + + #ifdef DEBUG + *logofs << name() << ": Encoding value " + << ((size - (MESSAGE_OFFSET - 8)) >> 2) + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue((size - (MESSAGE_OFFSET - 8)) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << size + << ".\n" << logofs_flush; + #endif + + size = (MESSAGE_OFFSET - 8) + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, + clientCache -> renderFormatCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 29, + clientCache -> renderGlyphSetCache); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 24, bigEndian), + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 26, bigEndian), + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + // + // Try to save as many bits as possible by + // encoding the information about the first + // set of glyphs. + // + + if (size >= MESSAGE_OFFSET) + { + unsigned int numGlyphs = *(buffer + 28); + + encodeBuffer.encodeCachedValue(numGlyphs, 8, + clientCache -> renderNumGlyphsCache); + + encodeBuffer.encodeCachedValue(GetUINT(buffer + 32, bigEndian), 16, + clientCache -> renderWidthCache, 11); + + encodeBuffer.encodeCachedValue(GetUINT(buffer + 34, bigEndian), 16, + clientCache -> renderHeightCache, 11); + + // + // Only manage the first set of glyphs, + // that is in most cases the only one. + // + + switch (*(buffer + 1)) + { + case X_RenderCompositeGlyphs8: + { + if (numGlyphs & 0x03) + { + memset((unsigned char *) buffer + MESSAGE_OFFSET + numGlyphs, '\0', + RoundUp4(numGlyphs) - numGlyphs); + } + + break; + } + case X_RenderCompositeGlyphs16: + { + if (numGlyphs & 0x01) + { + memset((unsigned char *) buffer + MESSAGE_OFFSET + (numGlyphs * 2), '\0', + RoundUp4(numGlyphs * 2) - numGlyphs * 2); + } + + break; + } + } + + #ifdef TEST + if (*(buffer + (size - 1)) != '\0') + { + *logofs << name() << ": WARNING! Final byte is non-zero with size " + << size << " and " << (unsigned int) *(buffer + 28) + << " glyphs.\n" << logofs_flush; + } + else + { + *logofs << name() << ": Final byte is zero with size " + << size << " and " << (unsigned int) *(buffer + 28) + << " glyphs.\n" << logofs_flush; + } + #endif + } + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + decodeBuffer.decodeXidValue(value, + clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeXidValue(value, + clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 12, bigEndian); + + decodeBuffer.decodeCachedValue(value, 32, + clientCache -> renderFormatCache); + + PutULONG(value, buffer + 16, bigEndian); + + decodeBuffer.decodeCachedValue(value, 29, + clientCache -> renderGlyphSetCache); + + PutULONG(value, buffer + 20, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + PutUINT(clientCache -> renderLastX, buffer + 24, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + PutUINT(clientCache -> renderLastY, buffer + 26, bigEndian); + + if (size >= MESSAGE_OFFSET) + { + decodeBuffer.decodeCachedValue(value, 8, + clientCache -> renderNumGlyphsCache); + + *(buffer + 28) = value; + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> renderWidthCache, 11); + + PutUINT(value, buffer + 32, bigEndian); + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> renderHeightCache, 11); + + PutUINT(value, buffer + 34, bigEndian); + } + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + switch (*(buffer + 1)) + { + case X_RenderCompositeGlyphs8: + { + clientCache -> renderTextCompressor.reset(); + + const unsigned char *next = buffer + MESSAGE_OFFSET; + + for (unsigned int i = MESSAGE_OFFSET; i < size; i++) + { + #ifdef DEBUG + *logofs << name() << ": Encoding char with i = " << i + << ".\n" << logofs_flush; + #endif + + clientCache -> renderTextCompressor. + encodeChar(*next++, encodeBuffer); + } + + break; + } + case X_RenderCompositeGlyphs16: + { + for (unsigned int i = MESSAGE_OFFSET; i < size; i += 2) + { + value = GetUINT(buffer + i, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Encoding short with i = " << i + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(value, 16, + *clientCache -> renderCompositeGlyphsDataCache[clientCache -> + renderLastCompositeGlyphsData]); + + clientCache -> renderLastCompositeGlyphsData = value % 16; + } + + break; + } + default: + { + for (unsigned int i = MESSAGE_OFFSET; i < size; i += 4) + { + value = GetULONG(buffer + i, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Encoding long with i = " << i + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(value, 32, + *clientCache -> renderCompositeGlyphsDataCache[clientCache -> + renderLastCompositeGlyphsData]); + + clientCache -> renderLastCompositeGlyphsData = value % 16; + } + + break; + } + } + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + switch (*(buffer + 1)) + { + case X_RenderCompositeGlyphs8: + { + clientCache -> renderTextCompressor.reset(); + + unsigned char *next = buffer + MESSAGE_OFFSET; + + for (unsigned int i = MESSAGE_OFFSET; i < size; i++) + { + #ifdef DEBUG + *logofs << name() << ": Decoding char with i = " << i + << ".\n" << logofs_flush; + #endif + + *next++ = clientCache -> renderTextCompressor. + decodeChar(decodeBuffer); + } + + break; + } + case X_RenderCompositeGlyphs16: + { + for (unsigned int i = MESSAGE_OFFSET; i < size; i += 2) + { + #ifdef DEBUG + *logofs << name() << ": Decoding short with i = " << i + << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache -> renderCompositeGlyphsDataCache[clientCache -> + renderLastCompositeGlyphsData]); + + PutUINT(value, buffer + i, bigEndian); + + clientCache -> renderLastCompositeGlyphsData = value % 16; + } + + break; + } + default: + { + for (unsigned int i = MESSAGE_OFFSET; i < size; i += 4) + { + #ifdef DEBUG + *logofs << name() << ": Decoding long with i = " << i + << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 32, + *clientCache -> renderCompositeGlyphsDataCache[clientCache -> + renderLastCompositeGlyphsData]); + + PutULONG(value, buffer + i, bigEndian); + + clientCache -> renderLastCompositeGlyphsData = value % 16; + } + + break; + } + } + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.composite_glyphs_compat.type = *(buffer + 1); + renderExtension -> data.composite_glyphs_compat.op = *(buffer + 4); + + renderExtension -> data.composite_glyphs_compat.src_id = GetULONG(buffer + 8, bigEndian); + renderExtension -> data.composite_glyphs_compat.dst_id = GetULONG(buffer + 12, bigEndian); + + renderExtension -> data.composite_glyphs_compat.format = GetULONG(buffer + 16, bigEndian); + renderExtension -> data.composite_glyphs_compat.set_id = GetULONG(buffer + 20, bigEndian); + + renderExtension -> data.composite_glyphs_compat.src_x = GetUINT(buffer + 24, bigEndian); + renderExtension -> data.composite_glyphs_compat.src_y = GetUINT(buffer + 26, bigEndian); + + if (size >= MESSAGE_OFFSET) + { + renderExtension -> data.composite_glyphs_compat.num_elm = *(buffer + 28); + + renderExtension -> data.composite_glyphs_compat.delta_x = GetUINT(buffer + 32, bigEndian); + renderExtension -> data.composite_glyphs_compat.delta_y = GetUINT(buffer + 34, bigEndian); + } + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.composite_glyphs_compat.type + << " size is " << renderExtension -> size_ << " identity size " + << renderExtension -> i_size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.composite_glyphs_compat.type; + *(buffer + 4) = renderExtension -> data.composite_glyphs_compat.op; + + PutULONG(renderExtension -> data.composite_glyphs_compat.src_id, buffer + 8, bigEndian); + PutULONG(renderExtension -> data.composite_glyphs_compat.dst_id, buffer + 12, bigEndian); + + PutULONG(renderExtension -> data.composite_glyphs_compat.format, buffer + 16, bigEndian); + PutULONG(renderExtension -> data.composite_glyphs_compat.set_id, buffer + 20, bigEndian); + + PutUINT(renderExtension -> data.composite_glyphs_compat.src_x, buffer + 24, bigEndian); + PutUINT(renderExtension -> data.composite_glyphs_compat.src_y, buffer + 26, bigEndian); + + if (size >= MESSAGE_OFFSET) + { + *(buffer + 28) = renderExtension -> data.composite_glyphs_compat.num_elm; + + PutUINT(renderExtension -> data.composite_glyphs_compat.delta_x, buffer + 32, bigEndian); + PutUINT(renderExtension -> data.composite_glyphs_compat.delta_y, buffer + 34, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Len is " << (unsigned int) *(buffer + 28) + << " delta X is " << GetUINT(buffer + 32, bigEndian) + << " delta Y is " << GetUINT(buffer + 34, bigEndian) + << ".\n" << logofs_flush; + + *logofs << name() << ": Pad 1 is " << (unsigned int) *(buffer + 29) + << " pad 2 and 3 are " << GetUINT(buffer + 30, bigEndian) + << ".\n" << logofs_flush; + #endif + } + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.composite_glyphs_compat.type + << " size is " << renderExtension -> size_ << " identity size " + << renderExtension -> i_size_ << ".\n" << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + // + // Include minor opcode, size and + // the composite operator in the + // identity. + // + + md5_append(md5_state, buffer + 1, 4); + + // + // Include the format and the source + // x and y fields. + // + + md5_append(md5_state, buffer + 16, 4); + md5_append(md5_state, buffer + 24, 4); + + // + // Include the number of glyphs. + // + + if (size >= MESSAGE_OFFSET) + { + md5_append(md5_state, buffer + 28, 1); + } +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs_compat.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.composite_glyphs_compat.src_id = + renderExtension -> data.composite_glyphs_compat.src_id; + + encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs_compat.dst_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.composite_glyphs_compat.dst_id = + renderExtension -> data.composite_glyphs_compat.dst_id; + + encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs_compat.set_id, 29, + clientCache -> renderGlyphSetCache); + + cachedRenderExtension -> data.composite_glyphs_compat.set_id = + renderExtension -> data.composite_glyphs_compat.set_id; + + if (renderExtension -> size_ >= MESSAGE_OFFSET) + { + encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs_compat.delta_x, 16, + clientCache -> renderWidthCache, 11); + + cachedRenderExtension -> data.composite_glyphs_compat.delta_x = + renderExtension -> data.composite_glyphs_compat.delta_x; + + encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs_compat.delta_y, 16, + clientCache -> renderHeightCache, 11); + + cachedRenderExtension -> data.composite_glyphs_compat.delta_y = + renderExtension -> data.composite_glyphs_compat.delta_y; + } + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.composite_glyphs_compat.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs_compat.src_id, + clientCache -> renderSrcPictureCache); + + decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs_compat.dst_id, + clientCache -> renderSrcPictureCache); + + decodeBuffer.decodeCachedValue(renderExtension -> data.composite_glyphs_compat.set_id, 29, + clientCache -> renderGlyphSetCache); + + if (renderExtension -> size_ >= MESSAGE_OFFSET) + { + unsigned int value; + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> renderWidthCache, 11); + + renderExtension -> data.composite_glyphs_compat.delta_x = value; + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> renderHeightCache, 11); + + renderExtension -> data.composite_glyphs_compat.delta_y = value; + } + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.composite_glyphs_compat.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderCompositeGlyphsCompat.h b/nxcomp/RenderCompositeGlyphsCompat.h new file mode 100644 index 000000000..7a00608c2 --- /dev/null +++ b/nxcomp/RenderCompositeGlyphsCompat.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderCompositeGlyphsCompat_H +#define RenderCompositeGlyphsCompat_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderCompositeGlyphsCompat" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderCompositeGlyphsCompatStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 36 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size); + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderCompositeGlyphsCompat_H */ diff --git a/nxcomp/RenderCreateGlyphSet.cpp b/nxcomp/RenderCreateGlyphSet.cpp new file mode 100644 index 000000000..b9da8d7bf --- /dev/null +++ b/nxcomp/RenderCreateGlyphSet.cpp @@ -0,0 +1,173 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderCreateGlyphSet.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> renderGlyphSetCache, + clientCache -> renderFreeGlyphSetCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 32, + clientCache -> renderFormatCache); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeNewXidValue(value, + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> renderGlyphSetCache, + clientCache -> renderFreeGlyphSetCache); + + PutULONG(value, buffer + 4, bigEndian); + + decodeBuffer.decodeCachedValue(value, 32, + clientCache -> renderFormatCache); + + PutULONG(value, buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.create_set.type = *(buffer + 1); + + renderExtension -> data.create_set.set_id = GetULONG(buffer + 4, bigEndian); + renderExtension -> data.create_set.format = GetULONG(buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.create_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.create_set.type; + + PutULONG(renderExtension -> data.create_set.set_id, buffer + 4, bigEndian); + PutULONG(renderExtension -> data.create_set.format, buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.create_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); + md5_append(md5_state, buffer + 8, 4); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeNewXidValue(renderExtension -> data.create_set.set_id, + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> renderGlyphSetCache, + clientCache -> renderFreeGlyphSetCache); + + cachedRenderExtension -> data.create_set.set_id = + renderExtension -> data.create_set.set_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.create_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeNewXidValue(renderExtension -> data.create_set.set_id, + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> renderGlyphSetCache, + clientCache -> renderFreeGlyphSetCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.create_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderCreateGlyphSet.h b/nxcomp/RenderCreateGlyphSet.h new file mode 100644 index 000000000..0f14ce0c5 --- /dev/null +++ b/nxcomp/RenderCreateGlyphSet.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderCreateGlyphSet_H +#define RenderCreateGlyphSet_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderCreateGlyphSet" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderCreateGlyphSetStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 12 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 0 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 0 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderCreateGlyphSet_H */ diff --git a/nxcomp/RenderCreateGlyphSetCompat.cpp b/nxcomp/RenderCreateGlyphSetCompat.cpp new file mode 100644 index 000000000..49e9f741d --- /dev/null +++ b/nxcomp/RenderCreateGlyphSetCompat.cpp @@ -0,0 +1,231 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderCreateGlyphSetCompat.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + // + // Strictly speaking this request doesn't have + // a data part. We encode the fields past the + // offset as they were data. An improvement + // would be to encode the format field using + // the cache. + // + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + size = MESSAGE_OFFSET + 4; + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian), + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + PutULONG(value, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + #ifdef DEBUG + + *logofs << name() << ": Glyphset is " << GetULONG(buffer + 4, bigEndian) + << ".\n" << logofs_flush; + + if (size > MESSAGE_OFFSET) + { + *logofs << name() << ": Format is " << GetULONG(buffer + 8, bigEndian) + << ".\n" << logofs_flush; + } + + if (size > MESSAGE_OFFSET + 4) + { + *logofs << name() << ": WARNING! Unexpected size " << size + << ".\n" << logofs_flush; + } + + #endif + + encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.create_set.type = *(buffer + 1); + + renderExtension -> data.create_set.set_id = GetULONG(buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.create_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.create_set.type; + + PutULONG(renderExtension -> data.create_set.set_id, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.create_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeDiffCachedValue(renderExtension -> data.create_set.set_id, + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + cachedRenderExtension -> data.create_set.set_id = + renderExtension -> data.create_set.set_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.create_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeDiffCachedValue(renderExtension -> data.create_set.set_id, + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.create_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderCreateGlyphSetCompat.h b/nxcomp/RenderCreateGlyphSetCompat.h new file mode 100644 index 000000000..174313e10 --- /dev/null +++ b/nxcomp/RenderCreateGlyphSetCompat.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderCreateGlyphSetCompat_H +#define RenderCreateGlyphSetCompat_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderCreateGlyphSetCompat" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderCreateGlyphSetCompatStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 8 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderCreateGlyphSetCompat_H */ diff --git a/nxcomp/RenderCreatePicture.cpp b/nxcomp/RenderCreatePicture.cpp new file mode 100644 index 000000000..cb3d56534 --- /dev/null +++ b/nxcomp/RenderCreatePicture.cpp @@ -0,0 +1,266 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderCreatePicture.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> renderSrcPictureCache, + clientCache -> renderFreePictureCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> drawableCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 12, bigEndian), 32, + clientCache -> renderFormatCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, + clientCache -> renderValueMaskCache); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeNewXidValue(value, + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> renderSrcPictureCache, + clientCache -> renderFreePictureCache); + + PutULONG(value, buffer + 4, bigEndian); + + decodeBuffer.decodeXidValue(value, + clientCache -> drawableCache); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeCachedValue(value, 32, + clientCache -> renderFormatCache); + + PutULONG(value, buffer + 12, bigEndian); + + decodeBuffer.decodeCachedValue(value, 32, + clientCache -> renderValueMaskCache); + + PutULONG(value, buffer + 16, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.create_picture.type = *(buffer + 1); + + renderExtension -> data.create_picture.src_id = GetULONG(buffer + 4, bigEndian); + renderExtension -> data.create_picture.dst_id = GetULONG(buffer + 8, bigEndian); + + renderExtension -> data.create_picture.format = GetULONG(buffer + 12, bigEndian); + renderExtension -> data.create_picture.mask = GetULONG(buffer + 16, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.create_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.create_picture.type; + + PutULONG(renderExtension -> data.create_picture.src_id, buffer + 4, bigEndian); + PutULONG(renderExtension -> data.create_picture.dst_id, buffer + 8, bigEndian); + + PutULONG(renderExtension -> data.create_picture.format, buffer + 12, bigEndian); + PutULONG(renderExtension -> data.create_picture.mask, buffer + 16, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.create_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); + md5_append(md5_state, buffer + 12, 8); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding new id value " + << renderExtension -> data.create_picture.src_id + << ".\n"; + #endif + + encodeBuffer.encodeNewXidValue(renderExtension -> data.create_picture.src_id, + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> renderSrcPictureCache, + clientCache -> renderFreePictureCache); + + cachedRenderExtension -> data.create_picture.src_id = + renderExtension -> data.create_picture.src_id; + + encodeBuffer.encodeXidValue(renderExtension -> data.create_picture.dst_id, + clientCache -> drawableCache); + + cachedRenderExtension -> data.create_picture.dst_id = + renderExtension -> data.create_picture.dst_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.create_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeNewXidValue(renderExtension -> data.create_picture.src_id, + clientCache -> lastId, clientCache -> lastIdCache, + clientCache -> renderSrcPictureCache, + clientCache -> renderFreePictureCache); + + decodeBuffer.decodeXidValue(renderExtension -> data.create_picture.dst_id, + clientCache -> drawableCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.create_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderCreatePicture.h b/nxcomp/RenderCreatePicture.h new file mode 100644 index 000000000..35de9b86b --- /dev/null +++ b/nxcomp/RenderCreatePicture.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderCreatePicture_H +#define RenderCreatePicture_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderCreatePicture" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderCreatePictureStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 20 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderCreatePicture_H */ diff --git a/nxcomp/RenderCreatePictureCompat.cpp b/nxcomp/RenderCreatePictureCompat.cpp new file mode 100644 index 000000000..fa4dcb400 --- /dev/null +++ b/nxcomp/RenderCreatePictureCompat.cpp @@ -0,0 +1,262 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderCreatePictureCompat.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian), + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> drawableCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 12, bigEndian), 32, + clientCache -> renderFormatCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, + clientCache -> renderValueMaskCache); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + PutULONG(value, buffer + 4, bigEndian); + + decodeBuffer.decodeXidValue(value, + clientCache -> drawableCache); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeCachedValue(value, 32, + clientCache -> renderFormatCache); + + PutULONG(value, buffer + 12, bigEndian); + + decodeBuffer.decodeCachedValue(value, 32, + clientCache -> renderValueMaskCache); + + PutULONG(value, buffer + 16, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.create_picture.type = *(buffer + 1); + + renderExtension -> data.create_picture.src_id = GetULONG(buffer + 4, bigEndian); + renderExtension -> data.create_picture.dst_id = GetULONG(buffer + 8, bigEndian); + + renderExtension -> data.create_picture.format = GetULONG(buffer + 12, bigEndian); + renderExtension -> data.create_picture.mask = GetULONG(buffer + 16, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.create_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.create_picture.type; + + PutULONG(renderExtension -> data.create_picture.src_id, buffer + 4, bigEndian); + PutULONG(renderExtension -> data.create_picture.dst_id, buffer + 8, bigEndian); + + PutULONG(renderExtension -> data.create_picture.format, buffer + 12, bigEndian); + PutULONG(renderExtension -> data.create_picture.mask, buffer + 16, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.create_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); + md5_append(md5_state, buffer + 12, 8); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding new id value " + << renderExtension -> data.create_picture.src_id - + clientCache -> renderLastId << ".\n"; + #endif + + encodeBuffer.encodeDiffCachedValue(renderExtension -> data.create_picture.src_id, + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + cachedRenderExtension -> data.create_picture.src_id = + renderExtension -> data.create_picture.src_id; + + encodeBuffer.encodeXidValue(renderExtension -> data.create_picture.dst_id, + clientCache -> drawableCache); + + cachedRenderExtension -> data.create_picture.dst_id = + renderExtension -> data.create_picture.dst_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.create_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeDiffCachedValue(renderExtension -> data.create_picture.src_id, + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + decodeBuffer.decodeXidValue(renderExtension -> data.create_picture.dst_id, + clientCache -> drawableCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.create_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderCreatePictureCompat.h b/nxcomp/RenderCreatePictureCompat.h new file mode 100644 index 000000000..15c8c85b3 --- /dev/null +++ b/nxcomp/RenderCreatePictureCompat.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderCreatePictureCompat_H +#define RenderCreatePictureCompat_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderCreatePictureCompat" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderCreatePictureCompatStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 20 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderCreatePictureCompat_H */ diff --git a/nxcomp/RenderExtension.cpp b/nxcomp/RenderExtension.cpp new file mode 100644 index 000000000..79c26e64e --- /dev/null +++ b/nxcomp/RenderExtension.cpp @@ -0,0 +1,567 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "NXrender.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +#include "RenderExtension.h" + +#include "RenderGenericRequest.h" +#include "RenderCreatePicture.h" +#include "RenderChangePicture.h" +#include "RenderFreePicture.h" +#include "RenderPictureClip.h" +#include "RenderPictureTransform.h" +#include "RenderPictureFilter.h" +#include "RenderCreateGlyphSet.h" +#include "RenderFreeGlyphSet.h" +#include "RenderAddGlyphs.h" +#include "RenderComposite.h" +#include "RenderCompositeGlyphs.h" +#include "RenderFillRectangles.h" +#include "RenderTrapezoids.h" +#include "RenderTriangles.h" + +#include "RenderCreatePictureCompat.h" +#include "RenderFreePictureCompat.h" +#include "RenderPictureClipCompat.h" +#include "RenderCreateGlyphSetCompat.h" +#include "RenderCompositeCompat.h" +#include "RenderCompositeGlyphsCompat.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Constructor and destructor. +// + +RenderExtensionStore::RenderExtensionStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = RENDEREXTENSION_ENABLE_CACHE; + enableData = RENDEREXTENSION_ENABLE_DATA; + enableSplit = RENDEREXTENSION_ENABLE_SPLIT; + enableCompress = RENDEREXTENSION_ENABLE_COMPRESS; + + generic_ = new RenderGenericRequestStore(); + + for (int i = 0; i < RENDEREXTENSION_MINOR_OPCODE_LIMIT; i++) + { + minors_[i] = generic_; + } + + minors_[X_RenderChangePicture] = new RenderChangePictureStore(); + minors_[X_RenderFillRectangles] = new RenderFillRectanglesStore(); + minors_[X_RenderAddGlyphs] = new RenderAddGlyphsStore(); + + if (control -> isProtoStep7() == 1) + { + minors_[X_RenderCreatePicture] = new RenderCreatePictureStore(); + minors_[X_RenderFreePicture] = new RenderFreePictureStore(); + minors_[X_RenderSetPictureClipRectangles] = new RenderPictureClipStore(); + minors_[X_RenderCreateGlyphSet] = new RenderCreateGlyphSetStore(); + minors_[X_RenderComposite] = new RenderCompositeStore(); + minors_[X_RenderCompositeGlyphs8] = new RenderCompositeGlyphsStore(); + minors_[X_RenderCompositeGlyphs16] = new RenderCompositeGlyphsStore(); + minors_[X_RenderCompositeGlyphs32] = new RenderCompositeGlyphsStore(); + + minors_[X_RenderSetPictureTransform] = new RenderPictureTransformStore(); + minors_[X_RenderSetPictureFilter] = new RenderPictureFilterStore(); + minors_[X_RenderFreeGlyphSet] = new RenderFreeGlyphSetStore(); + minors_[X_RenderTrapezoids] = new RenderTrapezoidsStore(); + minors_[X_RenderTriangles] = new RenderTrianglesStore(); + } + else + { + minors_[X_RenderCreatePicture] = new RenderCreatePictureCompatStore(); + minors_[X_RenderFreePicture] = new RenderFreePictureCompatStore(); + minors_[X_RenderSetPictureClipRectangles] = new RenderPictureClipCompatStore(); + minors_[X_RenderCreateGlyphSet] = new RenderCreateGlyphSetCompatStore(); + minors_[X_RenderComposite] = new RenderCompositeCompatStore(); + minors_[X_RenderCompositeGlyphs8] = new RenderCompositeGlyphsCompatStore(); + minors_[X_RenderCompositeGlyphs16] = new RenderCompositeGlyphsCompatStore(); + minors_[X_RenderCompositeGlyphs32] = new RenderCompositeGlyphsCompatStore(); + } + + dataLimit = RENDEREXTENSION_DATA_LIMIT; + dataOffset = RENDEREXTENSION_DATA_OFFSET; + + if (control -> isProtoStep7() == 1) + { + cacheSlots = RENDEREXTENSION_CACHE_SLOTS_IF_PROTO_STEP_7; + } + else + { + cacheSlots = RENDEREXTENSION_CACHE_SLOTS; + } + + cacheThreshold = RENDEREXTENSION_CACHE_THRESHOLD; + cacheLowerThreshold = RENDEREXTENSION_CACHE_LOWER_THRESHOLD; + + opcode_ = X_NXInternalRenderExtension; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +RenderExtensionStore::~RenderExtensionStore() +{ + for (int i = 0; i < RENDEREXTENSION_MINOR_OPCODE_LIMIT; i++) + { + if (minors_[i] != generic_) + { + delete minors_[i]; + } + } + + delete generic_; + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +int RenderExtensionStore::validateMessage(const unsigned char *buffer, int size) +{ + #ifdef TEST + *logofs << name() << ": Encoding message OPCODE#" + << (unsigned) *buffer << " MINOR#" << (unsigned) + *(buffer + 1) << " with size " << size + << ".\n" << logofs_flush; + #endif + + return (size >= control -> MinimumMessageSize && + size <= control -> MaximumMessageSize); +} + +// +// Here are the methods to handle the messages' content. +// + +int RenderExtensionStore::identitySize(const unsigned char *buffer, unsigned int size) +{ + return minors_[*(buffer + 1)] -> identitySize(buffer, size); +} + +int RenderExtensionStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + encodeBuffer.encodeOpcodeValue(*(buffer + 1), + ((ClientCache *) channelCache) -> renderOpcodeCache); + + minors_[*(buffer + 1)] -> encodeMessage(encodeBuffer, buffer, size, + bigEndian, channelCache); + + return 1; +} + +int RenderExtensionStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + unsigned char type; + + decodeBuffer.decodeOpcodeValue(type, + ((ClientCache *) channelCache) -> renderOpcodeCache); + + minors_[type] -> decodeMessage(decodeBuffer, buffer, size, type, + bigEndian, writeBuffer, channelCache); + + return 1; +} + +int RenderExtensionStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + return minors_[*(buffer + 1)] -> parseIdentity(message, buffer, size, bigEndian); +} + +int RenderExtensionStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + return minors_[((RenderExtensionMessage *) message) -> data.any.type] -> + unparseIdentity(message, buffer, size, bigEndian); +} + +void RenderExtensionStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + minors_[*(buffer + 1)] -> identityChecksum(message, buffer, size, md5_state_, bigEndian); +} + +void RenderExtensionStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + minors_[((RenderExtensionMessage *) message) -> data.any.type] -> + updateIdentity(encodeBuffer, message, cachedMessage, channelCache); +} + +void RenderExtensionStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + minors_[((RenderExtensionMessage *) message) -> data.any.type] -> + updateIdentity(decodeBuffer, message, channelCache); +} + +void RenderExtensionStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + #ifdef WARNING + *logofs << name() << ": WARNING! Dump of identity not implemented.\n" + << logofs_flush; + #endif + + #endif +} + +// +// TODO: The following encoding and decoding functions +// could be generalized further, for example by passing +// the pointer to the data cache, the number of caches +// made available by the caller and the first cache to +// iterate through. +// + +void RenderMinorExtensionStore::encodeLongData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeLongData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - offset + << " bytes of long data.\n" << logofs_flush; + #endif + + return; + } + + ClientCache *clientCache = (ClientCache *) channelCache; + + for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 4) + { + #ifdef DEBUG + *logofs << name() << ": Encoding int with i = " << i << " c = " + << c << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(GetULONG(buffer + i, bigEndian), 32, + *clientCache -> renderDataCache[c]); + + if (++c == 16) c = 0; + } +} + +void RenderMinorExtensionStore::encodeIntData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeIntData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - offset + << " bytes of int data.\n" << logofs_flush; + #endif + + return; + } + + ClientCache *clientCache = (ClientCache *) channelCache; + + for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 2) + { + #ifdef DEBUG + *logofs << name() << ": Encoding int with i = " << i << " c = " + << c << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(GetUINT(buffer + i, bigEndian), 16, + *clientCache -> renderDataCache[c]); + + if (++c == 16) c = 0; + } +} + +void RenderMinorExtensionStore::encodeCharData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - offset + << " bytes of text data.\n" << logofs_flush; + #endif + + return; + } + + ClientCache *clientCache = (ClientCache *) channelCache; + + clientCache -> renderTextCompressor.reset(); + + const unsigned char *next = buffer + offset; + + for (unsigned int i = offset; i < size; i++) + { + #ifdef DEBUG + *logofs << name() << ": Encoding char with i = " << i + << ".\n" << logofs_flush; + #endif + + clientCache -> renderTextCompressor. + encodeChar(*next++, encodeBuffer); + } +} + +void RenderMinorExtensionStore::decodeLongData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeLongData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - offset + << " bytes of long data.\n" << logofs_flush; + #endif + + return; + } + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 4) + { + #ifdef DEBUG + *logofs << name() << ": Decoding int with i = " << i << " c = " + << c << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 32, + *clientCache -> renderDataCache[c]); + + PutULONG(value, buffer + i, bigEndian); + + if (++c == 16) c = 0; + } +} + +void RenderMinorExtensionStore::decodeIntData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeIntData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - offset + << " bytes of int data.\n" << logofs_flush; + #endif + + return; + } + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + for (unsigned int i = offset, c = (offset - 4) % 16; i < size; i += 2) + { + #ifdef DEBUG + *logofs << name() << ": Decoding int with i = " << i << " c = " + << c << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache -> renderDataCache[c]); + + PutUINT(value, buffer + i, bigEndian); + + if (++c == 16) c = 0; + } +} + +void RenderMinorExtensionStore::decodeCharData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(buffer + offset, size - offset); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - offset + << " bytes of text data.\n" << logofs_flush; + #endif + + return; + } + + ClientCache *clientCache = (ClientCache *) channelCache; + + clientCache -> renderTextCompressor.reset(); + + unsigned char *next = buffer + offset; + + for (unsigned int i = offset; i < size; i++) + { + #ifdef DEBUG + *logofs << name() << ": Decoding char with i = " << i + << ".\n" << logofs_flush; + #endif + + *next++ = clientCache -> renderTextCompressor. + decodeChar(decodeBuffer); + } +} + +void RenderMinorExtensionStore::parseIntData(const Message *message, const unsigned char *buffer, + unsigned int offset, unsigned int size, + int bigEndian) const +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_); + + for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2) + { + #ifdef DEBUG + *logofs << name() << ": Parsing int with i = " << i << " c = " + << c << ".\n" << logofs_flush; + #endif + + renderExtension -> data.any.short_data[c] = GetUINT(buffer + i, bigEndian); + + if (++c == 16) c = 0; + } +} + +void RenderMinorExtensionStore::unparseIntData(const Message *message, unsigned char *buffer, + unsigned int offset, unsigned int size, + int bigEndian) const +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_); + + for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2) + { + #ifdef DEBUG + *logofs << name() << ": Unparsing int with i = " << i << " c = " + << c << ".\n" << logofs_flush; + #endif + + PutUINT(renderExtension -> data.any.short_data[c], buffer + i, bigEndian); + + if (++c == 16) c = 0; + } +} + +void RenderMinorExtensionStore::updateIntData(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, unsigned int offset, + unsigned int size, ChannelCache *channelCache) const +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_); + + for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2) + { + #ifdef DEBUG + *logofs << name() << ": Encoding int update with i = " << i + << " c = " << c << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(renderExtension -> data.any.short_data[c], 16, + *clientCache -> renderDataCache[c]); + + cachedRenderExtension -> data.any.short_data[c] = + renderExtension -> data.any.short_data[c]; + + if (++c == 16) c = 0; + } +} + +void RenderMinorExtensionStore::updateIntData(DecodeBuffer &decodeBuffer, const Message *message, + unsigned int offset, unsigned int size, + ChannelCache *channelCache) const +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_); + + unsigned int value; + + for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2) + { + #ifdef DEBUG + *logofs << name() << ": Decoding int update with i = " << i + << " c = " << c << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache -> renderDataCache[c]); + + renderExtension -> data.any.short_data[c] = value; + + if (++c == 16) c = 0; + } +} diff --git a/nxcomp/RenderExtension.h b/nxcomp/RenderExtension.h new file mode 100644 index 000000000..275ef1c1d --- /dev/null +++ b/nxcomp/RenderExtension.h @@ -0,0 +1,504 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderExtension_H +#define RenderExtension_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Compression of data part is not enabled as +// most messages of this type are smaller than +// the current data size compression threshold. +// + +#define RENDEREXTENSION_ENABLE_CACHE 1 +#define RENDEREXTENSION_ENABLE_DATA 0 +#define RENDEREXTENSION_ENABLE_SPLIT 0 +#define RENDEREXTENSION_ENABLE_COMPRESS 0 + +#define RENDEREXTENSION_DATA_LIMIT 6144 +#define RENDEREXTENSION_DATA_OFFSET 36 + +#define RENDEREXTENSION_CACHE_SLOTS 6000 +#define RENDEREXTENSION_CACHE_THRESHOLD 20 +#define RENDEREXTENSION_CACHE_LOWER_THRESHOLD 10 + +#define RENDEREXTENSION_CACHE_SLOTS_IF_PROTO_STEP_7 8000 + +// +// Used to build the table of minor opcodes. +// + +#define RENDEREXTENSION_MINOR_OPCODE_LIMIT 256 + +// +// The message class. +// + +class RenderMinorExtensionStore; + +class RenderExtensionMessage : public Message +{ + friend class RenderExtensionStore; + friend class RenderMinorExtensionStore; + + friend class RenderGenericRequestStore; + friend class RenderCreatePictureStore; + friend class RenderChangePictureStore; + friend class RenderFreePictureStore; + friend class RenderPictureClipStore; + friend class RenderPictureTransformStore; + friend class RenderPictureFilterStore; + friend class RenderCreateGlyphSetStore; + friend class RenderFreeGlyphSetStore; + friend class RenderAddGlyphsStore; + friend class RenderCompositeStore; + friend class RenderCompositeGlyphsStore; + friend class RenderFillRectanglesStore; + friend class RenderTrapezoidsStore; + friend class RenderTrianglesStore; + + friend class RenderCreatePictureCompatStore; + friend class RenderFreePictureCompatStore; + friend class RenderPictureClipCompatStore; + friend class RenderCreateGlyphSetCompatStore; + friend class RenderCompositeCompatStore; + friend class RenderCompositeGlyphsCompatStore; + + public: + + RenderExtensionMessage() + { + } + + ~RenderExtensionMessage() + { + } + + // + // We consider for this message a data offset of 36, + // that is size of the biggest among all requests of + // this extension. The most common requests have a + // specific differential encoding, others are simply + // encoded through an array of int or char caches. + // + + private: + + union + { + struct + { + unsigned char type; + + unsigned char char_data[32]; + unsigned short short_data[16]; + unsigned short long_data[8]; + } + any; + + struct + { + unsigned char type; + + unsigned int src_id; + unsigned int dst_id; + + unsigned int format; + unsigned int mask; + } + create_picture; + + struct + { + unsigned char type; + + unsigned int src_id; + } + change_picture; + + struct + { + unsigned char type; + + unsigned int src_id; + } + free_picture; + + struct + { + unsigned char type; + + unsigned int src_id; + + unsigned short src_x; + unsigned short src_y; + } + picture_clip; + + struct + { + unsigned char type; + + unsigned int src_id; + } + picture_transform; + + struct + { + unsigned char type; + + unsigned int src_id; + unsigned int num_elm; + } + picture_filter; + + struct + { + unsigned char type; + + unsigned int set_id; + unsigned int format; + } + create_set; + + struct + { + unsigned char type; + + unsigned int set_id; + } + free_set; + + struct + { + unsigned char type; + + unsigned int set_id; + unsigned int num_elm; + } + add_glyphs; + + struct + { + unsigned char type; + + unsigned char op; + + unsigned int src_id; + unsigned int msk_id; + unsigned int dst_id; + + unsigned short src_x; + unsigned short src_y; + + unsigned short msk_x; + unsigned short msk_y; + + unsigned short dst_x; + unsigned short dst_y; + + unsigned short width; + unsigned short height; + } + composite; + + struct + { + unsigned char type; + + unsigned char op; + + unsigned char num_elm; + + unsigned int src_id; + unsigned int dst_id; + + unsigned int format; + unsigned int set_id; + + unsigned short src_x; + unsigned short src_y; + + unsigned short offset_x; + unsigned short offset_y; + } + composite_glyphs; + + struct + { + unsigned char type; + + unsigned char op; + + unsigned int dst_id; + } + fill_rectangles; + + struct + { + unsigned char type; + + unsigned char op; + + unsigned int src_id; + unsigned int dst_id; + + unsigned int format; + + unsigned short src_x; + unsigned short src_y; + } + trapezoids; + + struct + { + unsigned char type; + + unsigned char op; + + unsigned int src_id; + unsigned int dst_id; + + unsigned int format; + + unsigned short src_x; + unsigned short src_y; + } + triangles; + + struct + { + unsigned char type; + + unsigned char op; + + unsigned char num_elm; + + unsigned int src_id; + unsigned int dst_id; + + unsigned int format; + unsigned int set_id; + + unsigned short src_x; + unsigned short src_y; + + unsigned short delta_x; + unsigned short delta_y; + } + composite_glyphs_compat; + + } + data; +}; + +class RenderExtensionStore : public MessageStore +{ + public: + + RenderExtensionStore(StaticCompressor *compressor); + + virtual ~RenderExtensionStore(); + + virtual const char *name() const + { + return "RenderExtension"; + } + + virtual unsigned char opcode() const + { + return opcode_; + } + + virtual unsigned int storage() const + { + return sizeof(RenderExtensionMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new RenderExtensionMessage(); + } + + virtual Message *create(const Message &message) const + { + return new RenderExtensionMessage((const RenderExtensionMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (RenderExtensionMessage *) message; + } + + // + // Determine if the message must be stored + // in the cache. + // + + virtual int validateMessage(const unsigned char *buffer, int size); + + // + // Since protocol step 5 these methods are + // specialized in their minor opcode stores. + // + + virtual int identitySize(const unsigned char *buffer, unsigned int size); + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; + + private: + + unsigned char opcode_; + + // + // Keep pointers to specialized classes. + // + + RenderMinorExtensionStore *minors_[RENDEREXTENSION_MINOR_OPCODE_LIMIT]; + + RenderMinorExtensionStore *generic_; +}; + +class RenderMinorExtensionStore : public MinorMessageStore +{ + public: + + virtual const char *name() const = 0; + + virtual int identitySize(const unsigned char *buffer, unsigned int size) = 0; + + virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const = 0; + + virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, unsigned char type, int bigEndian, + WriteBuffer *writeBuffer, ChannelCache *channelCache) const = 0; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const = 0; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const = 0; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const = 0; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const = 0; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, md5_state_t *md5_state, + int bigEndian) const = 0; + + // + // Internal encode and decode utilities. + // + + protected: + + void encodeLongData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + void encodeIntData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + void encodeCharData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + void decodeLongData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + void decodeIntData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + void decodeCharData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int offset, unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + /* + * The following methods are only used in the + * encoding of the generic render request. To + * be removed in future. + */ + + void parseIntData(const Message *message, const unsigned char *buffer, + unsigned int offset, unsigned int size, + int bigEndian) const; + + void unparseIntData(const Message *message, unsigned char *buffer, + unsigned int offset, unsigned int size, + int bigEndian) const; + + void updateIntData(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, unsigned int offset, + unsigned int size, ChannelCache *channelCache) const; + + void updateIntData(DecodeBuffer &decodeBuffer, const Message *message, + unsigned int offset, unsigned int size, + ChannelCache *channelCache) const; +}; + +#endif /* RenderExtension_H */ diff --git a/nxcomp/RenderFillRectangles.cpp b/nxcomp/RenderFillRectangles.cpp new file mode 100644 index 000000000..6f08d97a4 --- /dev/null +++ b/nxcomp/RenderFillRectangles.cpp @@ -0,0 +1,225 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderFillRectangles.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + // + // The color structure (4 components, 2 bytes + // each) is included in the data part, so that + // it gets into the checksum. The rectangles + // are in the format x, y, width, height with + // 2 bytes per each field, so each request is + // at least 12 + 8 + 8 = 28 bytes long. + // + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> renderSrcPictureCache); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.fill_rectangles.type = *(buffer + 1); + renderExtension -> data.fill_rectangles.op = *(buffer + 4); + + renderExtension -> data.fill_rectangles.dst_id = GetULONG(buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.fill_rectangles.type << " size is " + << renderExtension -> size_ << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.fill_rectangles.type; + *(buffer + 4) = renderExtension -> data.fill_rectangles.op; + + PutULONG(renderExtension -> data.fill_rectangles.dst_id, buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.fill_rectangles.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 4); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(renderExtension -> data.fill_rectangles.dst_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.fill_rectangles.dst_id = + renderExtension -> data.fill_rectangles.dst_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.fill_rectangles.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.fill_rectangles.dst_id, + clientCache -> renderSrcPictureCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.fill_rectangles.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderFillRectangles.h b/nxcomp/RenderFillRectangles.h new file mode 100644 index 000000000..189855907 --- /dev/null +++ b/nxcomp/RenderFillRectangles.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderFillRectangles_H +#define RenderFillRectangles_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderFillRectangles" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderFillRectanglesStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 12 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderFillRectangles_H */ diff --git a/nxcomp/RenderFreeGlyphSet.cpp b/nxcomp/RenderFreeGlyphSet.cpp new file mode 100644 index 000000000..50010e381 --- /dev/null +++ b/nxcomp/RenderFreeGlyphSet.cpp @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderFreeGlyphSet.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeFreeXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> renderFreeGlyphSetCache); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + unsigned int value; + + ClientCache *clientCache = (ClientCache *) channelCache; + + *(buffer + 1) = type; + + decodeBuffer.decodeFreeXidValue(value, + clientCache -> renderFreeGlyphSetCache); + + PutULONG(value, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.free_set.type = *(buffer + 1); + + renderExtension -> data.free_set.set_id = GetULONG(buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.free_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.free_set.type; + + PutULONG(renderExtension -> data.free_set.set_id, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.free_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeFreeXidValue(renderExtension -> data.free_set.set_id, + clientCache -> renderFreeGlyphSetCache); + + cachedRenderExtension -> data.free_set.set_id = + renderExtension -> data.free_set.set_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.free_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeFreeXidValue(renderExtension -> data.free_set.set_id, + clientCache -> renderFreeGlyphSetCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.free_set.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderFreeGlyphSet.h b/nxcomp/RenderFreeGlyphSet.h new file mode 100644 index 000000000..7233031ed --- /dev/null +++ b/nxcomp/RenderFreeGlyphSet.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderFreeGlyphSet_H +#define RenderFreeGlyphSet_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderFreeGlyphSet" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderFreeGlyphSetStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 8 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 0 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 0 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderFreeGlyphSet_H */ diff --git a/nxcomp/RenderFreePicture.cpp b/nxcomp/RenderFreePicture.cpp new file mode 100644 index 000000000..b1074f33f --- /dev/null +++ b/nxcomp/RenderFreePicture.cpp @@ -0,0 +1,154 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderFreePicture.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeFreeXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> renderFreePictureCache); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + unsigned int value; + + ClientCache *clientCache = (ClientCache *) channelCache; + + *(buffer + 1) = type; + + decodeBuffer.decodeFreeXidValue(value, + clientCache -> renderFreePictureCache); + + PutULONG(value, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.free_picture.type = *(buffer + 1); + + renderExtension -> data.free_picture.src_id = GetULONG(buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.free_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.free_picture.type; + + PutULONG(renderExtension -> data.free_picture.src_id, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.free_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeFreeXidValue(renderExtension -> data.free_picture.src_id, + clientCache -> renderFreePictureCache); + + cachedRenderExtension -> data.free_picture.src_id = + renderExtension -> data.free_picture.src_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.free_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeFreeXidValue(renderExtension -> data.free_picture.src_id, + clientCache -> renderFreePictureCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.free_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderFreePicture.h b/nxcomp/RenderFreePicture.h new file mode 100644 index 000000000..2329cb4e5 --- /dev/null +++ b/nxcomp/RenderFreePicture.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderFreePicture_H +#define RenderFreePicture_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderFreePicture" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderFreePictureStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 8 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 0 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 0 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderFreePicture_H */ diff --git a/nxcomp/RenderFreePictureCompat.cpp b/nxcomp/RenderFreePictureCompat.cpp new file mode 100644 index 000000000..fb4c7ac54 --- /dev/null +++ b/nxcomp/RenderFreePictureCompat.cpp @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderFreePictureCompat.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeDiffCachedValue(GetULONG(buffer + 4, bigEndian), + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + unsigned int value; + + ClientCache *clientCache = (ClientCache *) channelCache; + + *(buffer + 1) = type; + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + PutULONG(value, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.free_picture.type = *(buffer + 1); + + renderExtension -> data.free_picture.src_id = GetULONG(buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.free_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.free_picture.type; + + PutULONG(renderExtension -> data.free_picture.src_id, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.free_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeDiffCachedValue(renderExtension -> data.free_picture.src_id, + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + cachedRenderExtension -> data.free_picture.src_id = + renderExtension -> data.free_picture.src_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.free_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeDiffCachedValue(renderExtension -> data.free_picture.src_id, + clientCache -> renderLastId, 29, + clientCache -> renderIdCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.free_picture.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderFreePictureCompat.h b/nxcomp/RenderFreePictureCompat.h new file mode 100644 index 000000000..32d613ae0 --- /dev/null +++ b/nxcomp/RenderFreePictureCompat.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderFreePictureCompat_H +#define RenderFreePictureCompat_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderFreePictureCompat" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderFreePictureCompatStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 8 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 0 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 0 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderFreePictureCompat_H */ diff --git a/nxcomp/RenderGenericRequest.cpp b/nxcomp/RenderGenericRequest.cpp new file mode 100644 index 000000000..4f979c18f --- /dev/null +++ b/nxcomp/RenderGenericRequest.cpp @@ -0,0 +1,258 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "NXrender.h" + +#include "RenderExtension.h" +#include "RenderGenericRequest.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Here are the methods to handle the messages' content. +// + +int RenderGenericRequestStore::encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message.\n" + << logofs_flush; + + unsigned char type = *(buffer + 1); + + #endif + + encodeBuffer.encodeCachedValue(size >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef DEBUG + *logofs << name() << ": Encoding full unhandled message. " + << "Type is " << (unsigned int) type << " size is " + << size << ".\n" << logofs_flush; + #endif + + encodeIntData(encodeBuffer, buffer, 4, size, + bigEndian, clientCache); + + #ifdef DEBUG + *logofs << name() << ": Encoded full message.\n" + << logofs_flush; + #endif + + return 1; +} + +int RenderGenericRequestStore::decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, unsigned char type, int bigEndian, + WriteBuffer *writeBuffer, ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message.\n" + << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + size <<= 2; + + buffer = writeBuffer -> addMessage(size); + + *(buffer + 1) = type; + + #ifdef DEBUG + *logofs << name() << ": Decoding full unhandled message. " + << "Type is " << (unsigned int) type << " size is " + << size << ".\n" << logofs_flush; + #endif + + decodeIntData(decodeBuffer, buffer, 4, size, + bigEndian, clientCache); + + #ifdef DEBUG + *logofs << name() << ": Decoded full message.\n" + << logofs_flush; + #endif + + return 1; +} + +void RenderGenericRequestStore::encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ +} + +void RenderGenericRequestStore::decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ +} + +int RenderGenericRequestStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + #ifdef DEBUG + *logofs << name() << ": Parsing identity for message at " + << this << ".\n" << logofs_flush; + #endif + + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + unsigned char type = *(buffer + 1); + + renderExtension -> data.any.type = type; + + #ifdef DEBUG + *logofs << name() << ": Parsing unhandled identity. " + << "Type is " << (unsigned int) renderExtension -> data.any.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif + + parseIntData(message, buffer, 4, size, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int RenderGenericRequestStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + #ifdef DEBUG + *logofs << name() << ": Unparsing identity for message at " + << this << ".\n" << logofs_flush; + #endif + + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + unsigned char type = renderExtension -> data.any.type; + + *(buffer + 1) = type; + + #ifdef DEBUG + *logofs << name() << ": Unparsing unhandled identity. " + << "Type is " << (unsigned int) renderExtension -> data.any.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif + + unparseIntData(message, buffer, 4, size, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void RenderGenericRequestStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, md5_state_t *md5_state, + int bigEndian) const +{ + // + // Include the minor opcode in the checksum. + // Because the data offset can be beyond the + // real end of the message, we need to include + // the size or we will match any message whose + // size is less or equal to the data offset. + // + + md5_append(md5_state, buffer + 1, 3); +} + +void RenderGenericRequestStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + // + // Encode the variant part. + // + + #ifdef DEBUG + *logofs << name() << ": Updating identity for message at " + << this << ".\n" << logofs_flush; + #endif + + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + #ifdef DEBUG + *logofs << name() << ": Encoding unhandled update. " + << "Type is " << (unsigned int) renderExtension -> data.any.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif + + updateIntData(encodeBuffer, message, cachedMessage, 4, + renderExtension -> size_, channelCache); + + #ifdef DEBUG + *logofs << name() << ": Updated identity for message at " + << this << ".\n" << logofs_flush; + #endif +} + +void RenderGenericRequestStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + #ifdef DEBUG + *logofs << name() << ": Updating identity for message at " + << this << ".\n" << logofs_flush; + #endif + + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + #ifdef DEBUG + *logofs << name() << ": Decoding unhandled update. " + << "Type is " << (unsigned int) renderExtension -> data.any.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif + + updateIntData(decodeBuffer, message, 4, + renderExtension -> size_, channelCache); + + #ifdef DEBUG + *logofs << name() << ": Updated identity for message at " + << this << ".\n" << logofs_flush; + #endif +} diff --git a/nxcomp/RenderGenericRequest.h b/nxcomp/RenderGenericRequest.h new file mode 100644 index 000000000..8fa3acb76 --- /dev/null +++ b/nxcomp/RenderGenericRequest.h @@ -0,0 +1,81 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderGenericRequest_H +#define RenderGenericRequest_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +class RenderGenericRequestStore : public RenderMinorExtensionStore +{ + public: + + virtual const char *name() const + { + return "RenderGenericRequest"; + } + + virtual int identitySize(const unsigned char *buffer, unsigned int size) + { + return RENDEREXTENSION_DATA_OFFSET; + } + + virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, unsigned char type, int bigEndian, + WriteBuffer *writeBuffer, ChannelCache *channelCache) const; + + virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, md5_state_t *md5_state, + int bigEndian) const; +}; + +#endif /* RenderGenericRequest_H */ diff --git a/nxcomp/RenderMinorExtensionHeaders.h b/nxcomp/RenderMinorExtensionHeaders.h new file mode 100644 index 000000000..dda0042d8 --- /dev/null +++ b/nxcomp/RenderMinorExtensionHeaders.h @@ -0,0 +1,34 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderMinorExtensionHeaders_H +#define RenderMinorExtensionHeaders_H + +#include "NXrender.h" + +#include "Message.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +#include "RenderExtension.h" + +#endif /* RenderMinorExtensionHeaders_H */ diff --git a/nxcomp/RenderMinorExtensionMethods.h b/nxcomp/RenderMinorExtensionMethods.h new file mode 100644 index 000000000..397f6966e --- /dev/null +++ b/nxcomp/RenderMinorExtensionMethods.h @@ -0,0 +1,73 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// This file is included multiple times, +// one for each message inheriting the +// parent class. +// + +public: + +#if MESSAGE_HAS_SIZE + +virtual void encodeSize(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + +virtual void decodeSize(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, unsigned char type, int bigEndian, + WriteBuffer *writeBuffer, ChannelCache *channelCache) const; + +#endif + +#if MESSAGE_HAS_DATA + +virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + +virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, + unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + +#endif + +virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + +virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, unsigned char type, int bigEndian, + WriteBuffer *writeBuffer, ChannelCache *channelCache) const; + +virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + +virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + +virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + +virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + +virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, md5_state_t *md5_state, + int bigEndian) const; diff --git a/nxcomp/RenderMinorExtensionTags.h b/nxcomp/RenderMinorExtensionTags.h new file mode 100644 index 000000000..1d61b103c --- /dev/null +++ b/nxcomp/RenderMinorExtensionTags.h @@ -0,0 +1,186 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderMinorExtensionTags_H +#define RenderMinorExtensionTags_H + +// +// Set in the message header file. +// + +#if MESSAGE_HAS_SIZE + +#define MESSAGE_ENCODE_SIZE encodeSize(encodeBuffer, buffer, size, bigEndian, channelCache) +#define MESSAGE_DECODE_SIZE decodeSize(decodeBuffer, buffer, size, type, bigEndian, writeBuffer, channelCache) + +#else + +#define MESSAGE_ENCODE_SIZE +#define MESSAGE_DECODE_SIZE size = MESSAGE_OFFSET; buffer = writeBuffer -> addMessage(size); + +#endif + +#if MESSAGE_HAS_DATA + +#define MESSAGE_ENCODE_DATA encodeData(encodeBuffer, buffer, size, bigEndian, channelCache) +#define MESSAGE_DECODE_DATA decodeData(decodeBuffer, buffer, size, bigEndian, channelCache) + +#else + +#define MESSAGE_ENCODE_DATA +#define MESSAGE_DECODE_DATA + +#endif + +// +// Prologue an epilogue of the message +// handling functions. +// + +#define MESSAGE_BEGIN_ENCODE_SIZE \ +\ +void MESSAGE_STORE::encodeSize(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \ + const unsigned int size, int bigEndian, \ + ChannelCache *channelCache) const \ +{ + +#define MESSAGE_END_ENCODE_SIZE \ +\ +} + +#define MESSAGE_BEGIN_DECODE_SIZE \ +\ +void MESSAGE_STORE::decodeSize(DecodeBuffer &decodeBuffer, unsigned char *&buffer, \ + unsigned int &size, unsigned char type, int bigEndian, \ + WriteBuffer *writeBuffer, ChannelCache *channelCache) const \ +{ + +#define MESSAGE_END_DECODE_SIZE \ +\ +} + +#define MESSAGE_BEGIN_ENCODE_MESSAGE \ +\ +int MESSAGE_STORE::encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \ + const unsigned int size, int bigEndian, \ + ChannelCache *channelCache) const \ +{ \ + MESSAGE_ENCODE_SIZE; + + +#define MESSAGE_END_ENCODE_MESSAGE \ +\ + MESSAGE_ENCODE_DATA; \ +\ + return 1; \ +} + +#define MESSAGE_BEGIN_DECODE_MESSAGE \ +\ +int MESSAGE_STORE::decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, \ + unsigned int &size, unsigned char type, int bigEndian, \ + WriteBuffer *writeBuffer, ChannelCache *channelCache) const \ +{ \ + MESSAGE_DECODE_SIZE; + + +#define MESSAGE_END_DECODE_MESSAGE \ +\ + MESSAGE_DECODE_DATA; \ +\ + return 1; \ +} + +#define MESSAGE_BEGIN_ENCODE_DATA \ +\ +void MESSAGE_STORE::encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \ + unsigned int size, int bigEndian, \ + ChannelCache *channelCache) const \ +{ + +#define MESSAGE_END_ENCODE_DATA \ +\ +} + +#define MESSAGE_BEGIN_DECODE_DATA \ +\ +void MESSAGE_STORE::decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, \ + unsigned int size, int bigEndian, \ + ChannelCache *channelCache) const \ +{ + +#define MESSAGE_END_DECODE_DATA \ +\ +} + +#define MESSAGE_BEGIN_PARSE_IDENTITY \ +\ +int MESSAGE_STORE::parseIdentity(Message *message, const unsigned char *buffer, \ + unsigned int size, int bigEndian) const \ +{ + +#define MESSAGE_END_PARSE_IDENTITY \ +\ + return 1; \ +\ +} + +#define MESSAGE_BEGIN_UNPARSE_IDENTITY \ +\ +int MESSAGE_STORE::unparseIdentity(const Message *message, unsigned char *buffer, \ + unsigned int size, int bigEndian) const \ +{ + +#define MESSAGE_END_UNPARSE_IDENTITY \ +\ + return 1; \ +\ +} + +#define MESSAGE_BEGIN_IDENTITY_CHECKSUM \ +\ +void MESSAGE_STORE::identityChecksum(const Message *message, const unsigned char *buffer, \ + unsigned int size, md5_state_t *md5_state, \ + int bigEndian) const \ +{ + +#define MESSAGE_END_IDENTITY_CHECKSUM \ +\ +} + +#define MESSAGE_BEGIN_ENCODE_UPDATE \ +\ +void MESSAGE_STORE::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, \ + const Message *cachedMessage, \ + ChannelCache *channelCache) const \ +{ + +#define MESSAGE_END_ENCODE_UPDATE \ +\ +} + +#define MESSAGE_BEGIN_DECODE_UPDATE \ +\ +void MESSAGE_STORE::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, \ + ChannelCache *channelCache) const \ +{ + +#define MESSAGE_END_DECODE_UPDATE \ +\ +} + +#endif /* RenderMinorExtensionTags_H */ diff --git a/nxcomp/RenderPictureClip.cpp b/nxcomp/RenderPictureClip.cpp new file mode 100644 index 000000000..7428e7219 --- /dev/null +++ b/nxcomp/RenderPictureClip.cpp @@ -0,0 +1,291 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderPictureClip.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + // + // The data is constituted by a number of + // rectangles. Each rectangle is in the + // format x, y, width, height with 2 bytes + // per each field, so each request is at + // least 12 + 8 = 20 bytes long. + // + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 8, bigEndian), + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 10, bigEndian), + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeXidValue(value, + clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 4, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + PutUINT(clientCache -> renderLastX, buffer + 8, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + PutUINT(clientCache -> renderLastY, buffer + 10, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.picture_clip.type = *(buffer + 1); + + renderExtension -> data.picture_clip.src_id = GetULONG(buffer + 4, bigEndian); + + renderExtension -> data.picture_clip.src_x = GetUINT(buffer + 8, bigEndian); + renderExtension -> data.picture_clip.src_y = GetUINT(buffer + 10, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.picture_clip.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.picture_clip.type; + + PutULONG(renderExtension -> data.picture_clip.src_id, buffer + 4, bigEndian); + + PutUINT(renderExtension -> data.picture_clip.src_x, buffer + 8, bigEndian); + PutUINT(renderExtension -> data.picture_clip.src_y, buffer + 10, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.picture_clip.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + // + // Encode the picture id and the + // source x and y differentially. + // + + md5_append(md5_state, buffer + 1, 3); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(renderExtension -> data.picture_clip.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.picture_clip.src_id = + renderExtension -> data.picture_clip.src_id; + + // + // The source x and y coordinates are + // encoded as differerences in respect + // to the previous cached value. + // + + unsigned int value; + unsigned int previous; + + value = renderExtension -> data.picture_clip.src_x; + previous = cachedRenderExtension -> data.picture_clip.src_x; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderXCache, 11); + + cachedRenderExtension -> data.picture_clip.src_x = value; + + value = renderExtension -> data.picture_clip.src_y; + previous = cachedRenderExtension -> data.picture_clip.src_y; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderYCache, 11); + + cachedRenderExtension -> data.picture_clip.src_y = value; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.picture_clip.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.picture_clip.src_id, + clientCache -> renderSrcPictureCache); + + unsigned int value; + unsigned int previous; + + previous = renderExtension -> data.picture_clip.src_x; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderXCache, 11); + + renderExtension -> data.picture_clip.src_x = value; + + previous = renderExtension -> data.picture_clip.src_y; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderYCache, 11); + + renderExtension -> data.picture_clip.src_y = value; + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.picture_clip.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderPictureClip.h b/nxcomp/RenderPictureClip.h new file mode 100644 index 000000000..35b320c31 --- /dev/null +++ b/nxcomp/RenderPictureClip.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderPictureClip_H +#define RenderPictureClip_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderPictureClip" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderPictureClipStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 12 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderPictureClip_H */ diff --git a/nxcomp/RenderPictureClipCompat.cpp b/nxcomp/RenderPictureClipCompat.cpp new file mode 100644 index 000000000..67d873008 --- /dev/null +++ b/nxcomp/RenderPictureClipCompat.cpp @@ -0,0 +1,237 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderPictureClipCompat.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 8, bigEndian), + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 10, bigEndian), + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeXidValue(value, + clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 4, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + PutUINT(clientCache -> renderLastX, buffer + 8, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + PutUINT(clientCache -> renderLastY, buffer + 10, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.picture_clip.type = *(buffer + 1); + + renderExtension -> data.picture_clip.src_id = GetULONG(buffer + 4, bigEndian); + + renderExtension -> data.picture_clip.src_x = GetUINT(buffer + 8, bigEndian); + renderExtension -> data.picture_clip.src_y = GetUINT(buffer + 10, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.picture_clip.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.picture_clip.type; + + PutULONG(renderExtension -> data.picture_clip.src_id, buffer + 4, bigEndian); + + PutUINT(renderExtension -> data.picture_clip.src_x, buffer + 8, bigEndian); + PutUINT(renderExtension -> data.picture_clip.src_y, buffer + 10, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.picture_clip.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); + md5_append(md5_state, buffer + 8, 4); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(renderExtension -> data.picture_clip.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.picture_clip.src_id = + renderExtension -> data.picture_clip.src_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.picture_clip.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.picture_clip.src_id, + clientCache -> renderSrcPictureCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.picture_clip.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderPictureClipCompat.h b/nxcomp/RenderPictureClipCompat.h new file mode 100644 index 000000000..05fc5cda8 --- /dev/null +++ b/nxcomp/RenderPictureClipCompat.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderPictureClipCompat_H +#define RenderPictureClipCompat_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderPictureClipCompat" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderPictureClipCompatStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 12 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderPictureClipCompat_H */ diff --git a/nxcomp/RenderPictureFilter.cpp b/nxcomp/RenderPictureFilter.cpp new file mode 100644 index 000000000..b48fdca15 --- /dev/null +++ b/nxcomp/RenderPictureFilter.cpp @@ -0,0 +1,266 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderPictureFilter.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding value " + << ((size - MESSAGE_OFFSET) >> 2) << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << size + << ".\n" << logofs_flush; + #endif + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeCachedValue(GetUINT(buffer + 8, bigEndian), 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeXidValue(value, + clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 4, bigEndian); + + decodeBuffer.decodeCachedValue(value, 16, + clientCache -> renderLengthCache, 5); + + PutUINT(value, buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.picture_filter.type = *(buffer + 1); + + renderExtension -> data.picture_filter.src_id = GetULONG(buffer + 4, bigEndian); + renderExtension -> data.picture_filter.num_elm = GetUINT(buffer + 8, bigEndian); + + // + // Clean the padding bytes. This + // should be the purpose of the + // filter. + // + + #ifdef TEST + *logofs << name() << ": Cleaning " + << RoundUp4(renderExtension -> data.picture_filter.num_elm) - + renderExtension -> data.picture_filter.num_elm << " bytes " + << "at offset " << MESSAGE_OFFSET + renderExtension -> + data.picture_filter.num_elm << " with " << renderExtension -> + data.picture_filter.num_elm << " elements and size " + << renderExtension -> size_ << ".\n" << logofs_flush; + #endif + + if (size >= MESSAGE_OFFSET + renderExtension -> + data.picture_filter.num_elm) + { + unsigned char *next = (unsigned char *) buffer + + MESSAGE_OFFSET + renderExtension -> + data.picture_filter.num_elm; + + while (next < buffer + size) + { + *next++ = '\0'; + } + } + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.picture_filter.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.picture_filter.type; + + PutULONG(renderExtension -> data.picture_filter.src_id, buffer + 4, bigEndian); + PutUINT(renderExtension -> data.picture_filter.num_elm, buffer + 8, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.picture_filter.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + // + // Include the length of the filter name + // in the checksum. + // + + md5_append(md5_state, buffer + 1, 3); + md5_append(md5_state, buffer + 8, 2); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(renderExtension -> data.picture_filter.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.picture_filter.src_id = + renderExtension -> data.picture_filter.src_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.picture_filter.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.picture_filter.src_id, + clientCache -> renderSrcPictureCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.picture_filter.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderPictureFilter.h b/nxcomp/RenderPictureFilter.h new file mode 100644 index 000000000..cf6ad5494 --- /dev/null +++ b/nxcomp/RenderPictureFilter.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderPictureFilter_H +#define RenderPictureFilter_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderPictureFilter" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderPictureFilterStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 12 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderPictureFilter_H */ diff --git a/nxcomp/RenderPictureTransform.cpp b/nxcomp/RenderPictureTransform.cpp new file mode 100644 index 000000000..048b73e6c --- /dev/null +++ b/nxcomp/RenderPictureTransform.cpp @@ -0,0 +1,202 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderPictureTransform.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + // + // Size is always 44. The identity size + // is set to 8, so we encode the 36 bytes + // of the transformation matrix as our + // data. + // +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + size = MESSAGE_OFFSET + 36; + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian), + clientCache -> renderSrcPictureCache); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeXidValue(value, + clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.picture_transform.type = *(buffer + 1); + + renderExtension -> data.picture_transform.src_id = GetULONG(buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.picture_transform.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.picture_transform.type; + + PutULONG(renderExtension -> data.picture_transform.src_id, buffer + 4, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.picture_transform.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + md5_append(md5_state, buffer + 1, 3); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(renderExtension -> data.picture_transform.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.picture_transform.src_id = + renderExtension -> data.picture_transform.src_id; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.picture_transform.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.picture_transform.src_id, + clientCache -> renderSrcPictureCache); + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.picture_transform.type + << " size is " << renderExtension -> size_ << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderPictureTransform.h b/nxcomp/RenderPictureTransform.h new file mode 100644 index 000000000..061b6a3d8 --- /dev/null +++ b/nxcomp/RenderPictureTransform.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderPictureTransform_H +#define RenderPictureTransform_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderPictureTransform" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderPictureTransformStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 8 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return MESSAGE_OFFSET; + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderPictureTransform_H */ diff --git a/nxcomp/RenderTrapezoids.cpp b/nxcomp/RenderTrapezoids.cpp new file mode 100644 index 000000000..32fcd01c0 --- /dev/null +++ b/nxcomp/RenderTrapezoids.cpp @@ -0,0 +1,360 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderTrapezoids.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + // + // The trapezoid data is made up of a structure + // containing a top and bottom coordinate in 4 + // bytes format, plus two lines, each represent- + // ed as four points in 4 bytes format. Thus + // each trapezoid is 4 * 2 + (4 * 4) * 2 = 40 + // bytes. Bytes are all padded to an long int, + // so we don't need to clean the message. + // + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding value " + << ((size - MESSAGE_OFFSET) >> 2) << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << size + << ".\n" << logofs_flush; + #endif + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), + clientCache -> renderDstPictureCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, + clientCache -> renderFormatCache); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian), + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian), + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + decodeBuffer.decodeXidValue(value, + clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeXidValue(value, + clientCache -> renderDstPictureCache); + + PutULONG(value, buffer + 12, bigEndian); + + decodeBuffer.decodeCachedValue(value, 32, + clientCache -> renderFormatCache); + + PutULONG(value, buffer + 16, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + if (size > MESSAGE_OFFSET) + { + encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + } + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of text data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + if (size > MESSAGE_OFFSET) + { + decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + } + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.trapezoids.type = *(buffer + 1); + renderExtension -> data.trapezoids.op = *(buffer + 4); + + renderExtension -> data.trapezoids.src_id = GetULONG(buffer + 8, bigEndian); + renderExtension -> data.trapezoids.dst_id = GetULONG(buffer + 12, bigEndian); + + renderExtension -> data.trapezoids.format = GetULONG(buffer + 16, bigEndian); + + renderExtension -> data.trapezoids.src_x = GetUINT(buffer + 20, bigEndian); + renderExtension -> data.trapezoids.src_y = GetUINT(buffer + 22, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.trapezoids.type + << " size is " << renderExtension -> size_ << " identity size " + << renderExtension -> i_size_ << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.trapezoids.type; + *(buffer + 4) = renderExtension -> data.trapezoids.op; + + PutULONG(renderExtension -> data.trapezoids.src_id, buffer + 8, bigEndian); + PutULONG(renderExtension -> data.trapezoids.dst_id, buffer + 12, bigEndian); + + PutULONG(renderExtension -> data.trapezoids.format, buffer + 16, bigEndian); + + PutUINT(renderExtension -> data.trapezoids.src_x, buffer + 20, bigEndian); + PutUINT(renderExtension -> data.trapezoids.src_y, buffer + 22, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.trapezoids.type + << " size is " << renderExtension -> size_ << " identity size " + << renderExtension -> i_size_ << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + // + // Include minor opcode, size and the + // operator in the identity. + // + + md5_append(md5_state, buffer + 1, 4); + + // + // Also include the format but not the + // x and y source. + // + + md5_append(md5_state, buffer + 16, 4); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(renderExtension -> data.trapezoids.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.trapezoids.src_id = + renderExtension -> data.trapezoids.src_id; + + encodeBuffer.encodeXidValue(renderExtension -> data.trapezoids.dst_id, + clientCache -> renderDstPictureCache); + + cachedRenderExtension -> data.trapezoids.dst_id = + renderExtension -> data.trapezoids.dst_id; + + // + // The source x and y coordinates are + // encoded as differerences in respect + // to the previous cached value. + // + + unsigned int value; + unsigned int previous; + + value = renderExtension -> data.trapezoids.src_x; + previous = cachedRenderExtension -> data.trapezoids.src_x; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderXCache, 11); + + cachedRenderExtension -> data.trapezoids.src_x = value; + + value = renderExtension -> data.trapezoids.src_y; + previous = cachedRenderExtension -> data.trapezoids.src_y; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderYCache, 11); + + cachedRenderExtension -> data.trapezoids.src_y = value; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.trapezoids.type + << " size is " << renderExtension -> size_ << " source x " + << renderExtension -> data.trapezoids.src_x << " y " + << renderExtension -> data.trapezoids.src_y << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.trapezoids.src_id, + clientCache -> renderSrcPictureCache); + + decodeBuffer.decodeXidValue(renderExtension -> data.trapezoids.dst_id, + clientCache -> renderDstPictureCache); + + unsigned int value; + unsigned int previous; + + previous = renderExtension -> data.trapezoids.src_x; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderXCache, 11); + + renderExtension -> data.trapezoids.src_x = value; + + previous = renderExtension -> data.trapezoids.src_y; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderYCache, 11); + + renderExtension -> data.trapezoids.src_y = value; + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.trapezoids.type + << " size is " << renderExtension -> size_ << " source x " + << renderExtension -> data.trapezoids.src_x << " y " + << renderExtension -> data.trapezoids.src_y << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderTrapezoids.h b/nxcomp/RenderTrapezoids.h new file mode 100644 index 000000000..3f3202016 --- /dev/null +++ b/nxcomp/RenderTrapezoids.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderTrapezoids_H +#define RenderTrapezoids_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderTrapezoids" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderTrapezoidsStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 24 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size); + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderTrapezoids_H */ diff --git a/nxcomp/RenderTriangles.cpp b/nxcomp/RenderTriangles.cpp new file mode 100644 index 000000000..e98bf3506 --- /dev/null +++ b/nxcomp/RenderTriangles.cpp @@ -0,0 +1,350 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +// +// Include the template for +// this message class. +// + +#include "RenderTriangles.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#include MESSAGE_TAGS + +// +// Message handling methods. +// + +MESSAGE_BEGIN_ENCODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding value " + << ((size - MESSAGE_OFFSET) >> 2) << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16, + clientCache -> renderLengthCache, 5); + + #ifdef TEST + *logofs << name() << ": Encoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_SIZE + +MESSAGE_BEGIN_DECODE_SIZE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(size, 16, + clientCache -> renderLengthCache, 5); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << size + << ".\n" << logofs_flush; + #endif + + size = MESSAGE_OFFSET + (size << 2); + + buffer = writeBuffer -> addMessage(size); + + #ifdef TEST + *logofs << name() << ": Decoded size with value " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_SIZE + +MESSAGE_BEGIN_ENCODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian), + clientCache -> renderSrcPictureCache); + + encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian), + clientCache -> renderDstPictureCache); + + encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32, + clientCache -> renderFormatCache); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian), + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian), + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + #ifdef TEST + *logofs << name() << ": Encoded message. Type is " + << (unsigned int) *(buffer + 1) << " size is " + << size << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_MESSAGE + +MESSAGE_BEGIN_DECODE_MESSAGE +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + *(buffer + 1) = type; + + decodeBuffer.decodeCachedValue(*(buffer + 4), 8, + clientCache -> renderOpCache); + + decodeBuffer.decodeXidValue(value, + clientCache -> renderSrcPictureCache); + + PutULONG(value, buffer + 8, bigEndian); + + decodeBuffer.decodeXidValue(value, + clientCache -> renderDstPictureCache); + + PutULONG(value, buffer + 12, bigEndian); + + decodeBuffer.decodeCachedValue(value, 32, + clientCache -> renderFormatCache); + + PutULONG(value, buffer + 16, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastX, 16, + clientCache -> renderXCache, 11); + + PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian); + + decodeBuffer.decodeDiffCachedValue(value, + clientCache -> renderLastY, 16, + clientCache -> renderYCache, 11); + + PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian); + + #ifdef TEST + *logofs << name() << ": Decoded message. Type is " + << (unsigned int) type << " size is " << size + << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_MESSAGE + +MESSAGE_BEGIN_ENCODE_DATA +{ + if (size > MESSAGE_OFFSET) + { + encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + } + + #ifdef TEST + *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET + << " bytes of text data.\n" << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_DATA + +MESSAGE_BEGIN_DECODE_DATA +{ + if (size > MESSAGE_OFFSET) + { + decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET, + size, bigEndian, channelCache); + } + + #ifdef TEST + *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET + << " bytes of data.\n" << logofs_flush; + #endif +} +MESSAGE_END_DECODE_DATA + +MESSAGE_BEGIN_PARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + renderExtension -> data.triangles.type = *(buffer + 1); + renderExtension -> data.triangles.op = *(buffer + 4); + + renderExtension -> data.triangles.src_id = GetULONG(buffer + 8, bigEndian); + renderExtension -> data.triangles.dst_id = GetULONG(buffer + 12, bigEndian); + + renderExtension -> data.triangles.format = GetULONG(buffer + 16, bigEndian); + + renderExtension -> data.triangles.src_x = GetUINT(buffer + 20, bigEndian); + renderExtension -> data.triangles.src_y = GetUINT(buffer + 22, bigEndian); + + #ifdef TEST + *logofs << name() << ": Parsed identity. Type is " + << (unsigned int) renderExtension -> data.triangles.type + << " size is " << renderExtension -> size_ << " identity size " + << renderExtension -> i_size_ << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_PARSE_IDENTITY + +MESSAGE_BEGIN_UNPARSE_IDENTITY +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + *(buffer + 1) = renderExtension -> data.triangles.type; + *(buffer + 4) = renderExtension -> data.triangles.op; + + PutULONG(renderExtension -> data.triangles.src_id, buffer + 8, bigEndian); + PutULONG(renderExtension -> data.triangles.dst_id, buffer + 12, bigEndian); + + PutULONG(renderExtension -> data.triangles.format, buffer + 16, bigEndian); + + PutUINT(renderExtension -> data.triangles.src_x, buffer + 20, bigEndian); + PutUINT(renderExtension -> data.triangles.src_y, buffer + 22, bigEndian); + + #ifdef TEST + *logofs << name() << ": Unparsed identity. Type is " + << (unsigned int) renderExtension -> data.triangles.type + << " size is " << renderExtension -> size_ << " identity size " + << renderExtension -> i_size_ << ".\n" << logofs_flush; + #endif +} +MESSAGE_END_UNPARSE_IDENTITY + +MESSAGE_BEGIN_IDENTITY_CHECKSUM +{ + // + // Include minor opcode, size and the + // operator in the identity. + // + + md5_append(md5_state, buffer + 1, 4); + + // + // Also include the format but not the + // x and y source. + // + + md5_append(md5_state, buffer + 16, 4); +} +MESSAGE_END_IDENTITY_CHECKSUM + +MESSAGE_BEGIN_ENCODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeXidValue(renderExtension -> data.triangles.src_id, + clientCache -> renderSrcPictureCache); + + cachedRenderExtension -> data.triangles.src_id = + renderExtension -> data.triangles.src_id; + + encodeBuffer.encodeXidValue(renderExtension -> data.triangles.dst_id, + clientCache -> renderDstPictureCache); + + cachedRenderExtension -> data.triangles.dst_id = + renderExtension -> data.triangles.dst_id; + + // + // The source x and y coordinates are + // encoded as differerences in respect + // to the previous cached value. + // + + unsigned int value; + unsigned int previous; + + value = renderExtension -> data.triangles.src_x; + previous = cachedRenderExtension -> data.triangles.src_x; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderXCache, 11); + + cachedRenderExtension -> data.triangles.src_x = value; + + value = renderExtension -> data.triangles.src_y; + previous = cachedRenderExtension -> data.triangles.src_y; + + encodeBuffer.encodeDiffCachedValue(value, previous, 16, + clientCache -> renderYCache, 11); + + cachedRenderExtension -> data.triangles.src_y = value; + + #ifdef TEST + *logofs << name() << ": Encoded update. Type is " + << (unsigned int) renderExtension -> data.triangles.type + << " size is " << renderExtension -> size_ << " source x " + << renderExtension -> data.triangles.src_x << " y " + << renderExtension -> data.triangles.src_y << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_ENCODE_UPDATE + +MESSAGE_BEGIN_DECODE_UPDATE +{ + RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeXidValue(renderExtension -> data.triangles.src_id, + clientCache -> renderSrcPictureCache); + + decodeBuffer.decodeXidValue(renderExtension -> data.triangles.dst_id, + clientCache -> renderDstPictureCache); + + unsigned int value; + unsigned int previous; + + previous = renderExtension -> data.triangles.src_x; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderXCache, 11); + + renderExtension -> data.triangles.src_x = value; + + previous = renderExtension -> data.triangles.src_y; + + decodeBuffer.decodeDiffCachedValue(value, previous, 16, + clientCache -> renderYCache, 11); + + renderExtension -> data.triangles.src_y = value; + + #ifdef TEST + *logofs << name() << ": Decoded update. Type is " + << (unsigned int) renderExtension -> data.triangles.type + << " size is " << renderExtension -> size_ << " source x " + << renderExtension -> data.triangles.src_x << " y " + << renderExtension -> data.triangles.src_y << ".\n" + << logofs_flush; + #endif +} +MESSAGE_END_DECODE_UPDATE diff --git a/nxcomp/RenderTriangles.h b/nxcomp/RenderTriangles.h new file mode 100644 index 000000000..6c4d105a1 --- /dev/null +++ b/nxcomp/RenderTriangles.h @@ -0,0 +1,80 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef RenderTriangles_H +#define RenderTriangles_H + +// +// Define the characteristics +// of this message class here. +// + +#undef MESSAGE_NAME +#define MESSAGE_NAME "RenderTriangles" + +#undef MESSAGE_STORE +#define MESSAGE_STORE RenderTrianglesStore + +#undef MESSAGE_CLASS +#define MESSAGE_CLASS RenderMinorExtensionStore + +#undef MESSAGE_METHODS +#define MESSAGE_METHODS "RenderMinorExtensionMethods.h" + +#undef MESSAGE_HEADERS +#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h" + +#undef MESSAGE_TAGS +#define MESSAGE_TAGS "RenderMinorExtensionTags.h" + +#undef MESSAGE_OFFSET +#define MESSAGE_OFFSET 24 + +#undef MESSAGE_HAS_SIZE +#define MESSAGE_HAS_SIZE 1 + +#undef MESSAGE_HAS_DATA +#define MESSAGE_HAS_DATA 1 + +#undef MESSAGE_HAS_FILTER +#define MESSAGE_HAS_FILTER 0 + +// +// Declare the message class. +// + +#include MESSAGE_HEADERS + +class MESSAGE_STORE : public MESSAGE_CLASS +{ + public: + + virtual const char *name() const + { + return MESSAGE_NAME; + } + + virtual int identitySize(const unsigned char *buffer, + unsigned int size) + { + return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size); + } + + #include MESSAGE_METHODS +}; + +#endif /* RenderTriangles_H */ diff --git a/nxcomp/Rgb.cpp b/nxcomp/Rgb.cpp new file mode 100644 index 000000000..ad2e2fd92 --- /dev/null +++ b/nxcomp/Rgb.cpp @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Misc.h" +#include "Rgb.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +int UnpackRgb(T_geometry *geometry, unsigned char method, unsigned char *src_data, + int src_size, int dst_bpp, int dst_width, int dst_height, + unsigned char *dst_data, int dst_size) +{ + if (*src_data == 0) + { + if (dst_size != src_size - 1) + { + #ifdef TEST + *logofs << "UnpackRgb: PANIC! Invalid destination size " + << dst_size << " with source " << src_size + << ".\n" << logofs_flush; + #endif + + return -1; + } + + #ifdef TEST + *logofs << "UnpackRgb: Expanding " << src_size - 1 + << " bytes of plain RGB data.\n" << logofs_flush; + #endif + + memcpy(dst_data, src_data + 1, src_size - 1); + + return 1; + } + + unsigned int check_size = dst_size; + + int result = ZDecompress(&unpackStream, dst_data, &check_size, + src_data + 1, src_size - 1); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "UnpackRgb: PANIC! Failure decompressing RGB data. " + << "Error is '" << zError(result) << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decompressing RGB data. " + << "Error is '" << zError(result) << "'.\n"; + + return -1; + } + else if (check_size != (unsigned int) dst_size) + { + #ifdef PANIC + *logofs << "UnpackRgb: PANIC! Size mismatch in RGB data. " + << "Resulting size is " << check_size << " with " + << "expected size " << dst_size << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Size mismatch in RGB data. " + << "Resulting size is " << check_size << " with " + << "expected size " << dst_size << ".\n"; + + return -1; + } + + #ifdef TEST + *logofs << "UnpackRgb: Decompressed " << src_size - 1 + << " bytes to " << dst_size << " bytes of RGB data.\n" + << logofs_flush; + #endif + + return 1; +} diff --git a/nxcomp/Rgb.h b/nxcomp/Rgb.h new file mode 100644 index 000000000..98ead38e3 --- /dev/null +++ b/nxcomp/Rgb.h @@ -0,0 +1,28 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Rgb_H +#define Rgb_H + +#include "Unpack.h" + +int UnpackRgb(T_geometry *geometry, unsigned char method, + unsigned char *src_data, int src_size, int dst_bpp, + int dst_width, int dst_height, unsigned char *dst_data, + int dst_size); + +#endif /* Rgb_H */ diff --git a/nxcomp/Rle.cpp b/nxcomp/Rle.cpp new file mode 100644 index 000000000..b7b1460bd --- /dev/null +++ b/nxcomp/Rle.cpp @@ -0,0 +1,94 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Misc.h" +#include "Rle.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +int UnpackRle(T_geometry *geometry, unsigned char method, unsigned char *src_data, + int src_size, int dst_bpp, int dst_width, int dst_height, + unsigned char *dst_data, int dst_size) +{ + if (*src_data == 0) + { + if (dst_size != src_size - 1) + { + #ifdef TEST + *logofs << "UnpackRle: PANIC! Invalid destination size " + << dst_size << " with source " << src_size + << ".\n" << logofs_flush; + #endif + + return -1; + } + + #ifdef TEST + *logofs << "UnpackRle: Expanding " << src_size - 1 + << " bytes of plain RLE data.\n" << logofs_flush; + #endif + + memcpy(dst_data, src_data + 1, src_size - 1); + + return 1; + } + + unsigned int check_size = dst_size; + + int result = ZDecompress(&unpackStream, dst_data, &check_size, + src_data + 1, src_size - 1); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "UnpackRle: PANIC! Failure decompressing RLE data. " + << "Error is '" << zError(result) << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decompressing RLE data. " + << "Error is '" << zError(result) << "'.\n"; + + return -1; + } + else if (check_size != (unsigned int) dst_size) + { + #ifdef PANIC + *logofs << "UnpackRle: PANIC! Size mismatch in RLE data. " + << "Resulting size is " << check_size << " with " + << "expected size " << dst_size << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Size mismatch in RLE data. " + << "Resulting size is " << check_size << " with " + << "expected size " << dst_size << ".\n"; + + return -1; + } + + #ifdef TEST + *logofs << "UnpackRle: Decompressed " << src_size - 1 + << " bytes to " << dst_size << " bytes of RLE data.\n" + << logofs_flush; + #endif + + return 1; +} diff --git a/nxcomp/Rle.h b/nxcomp/Rle.h new file mode 100644 index 000000000..0e39fae5a --- /dev/null +++ b/nxcomp/Rle.h @@ -0,0 +1,28 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Rle_H +#define Rle_H + +#include "Unpack.h" + +int UnpackRle(T_geometry *geometry, unsigned char method, + unsigned char *src_data, int src_size, int dst_bpp, + int dst_width, int dst_height, unsigned char *dst_data, + int dst_size); + +#endif /* Rle_H */ diff --git a/nxcomp/SendEvent.cpp b/nxcomp/SendEvent.cpp new file mode 100644 index 000000000..f65b1dbb2 --- /dev/null +++ b/nxcomp/SendEvent.cpp @@ -0,0 +1,292 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "SendEvent.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "IntCache.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int SendEventStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SendEventMessage *sendEvent = (SendEventMessage *) message; + + // + // Here is the fingerprint. + // + + sendEvent -> propagate = *(buffer + 1); + + sendEvent -> window = GetULONG(buffer + 4, bigEndian); + sendEvent -> mask = GetULONG(buffer + 8, bigEndian); + + sendEvent -> code = *(buffer + 12); + sendEvent -> byte_data = *(buffer + 13); + + sendEvent -> sequence = GetUINT(buffer + 14, bigEndian); + + sendEvent -> int_data = GetULONG(buffer + 16, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int SendEventStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SendEventMessage *sendEvent = (SendEventMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = sendEvent -> propagate; + + PutULONG(sendEvent -> window, buffer + 4, bigEndian); + PutULONG(sendEvent -> mask, buffer + 8, bigEndian); + + *(buffer + 12) = sendEvent -> code; + *(buffer + 13) = sendEvent -> byte_data; + + PutUINT(sendEvent -> sequence, buffer + 14, bigEndian); + + PutULONG(sendEvent -> int_data, buffer + 16, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void SendEventStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + SendEventMessage *sendEvent = (SendEventMessage *) message; + + *logofs << name() << ": Identity propagate " << (unsigned int) sendEvent -> propagate + << ", window " << sendEvent -> window << ", mask " << sendEvent -> mask + << ", code " << (unsigned int) sendEvent -> code << ", byte_data " + << (unsigned int) sendEvent -> byte_data << ", sequence " + << sendEvent -> sequence << ", int_data " << sendEvent -> int_data + << ", size " << sendEvent -> size_ << ".\n" << logofs_flush; + + #endif +} + +void SendEventStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ +} + +void SendEventStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + SendEventMessage *sendEvent = (SendEventMessage *) message; + SendEventMessage *cachedSendEvent = (SendEventMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << (unsigned int) sendEvent -> propagate + << " as propagate field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeBoolValue(sendEvent -> propagate); + + cachedSendEvent -> propagate = sendEvent -> propagate; + + #ifdef TEST + *logofs << name() << ": Encoding value " << sendEvent -> window + << " as window field.\n" << logofs_flush; + #endif + + if (sendEvent -> window == 0 || sendEvent -> window == 1) + { + encodeBuffer.encodeBoolValue(1); + + encodeBuffer.encodeBoolValue(sendEvent -> window); + } + else + { + encodeBuffer.encodeBoolValue(0); + + encodeBuffer.encodeXidValue(sendEvent -> window, clientCache -> windowCache); + } + + cachedSendEvent -> window = sendEvent -> window; + + #ifdef TEST + *logofs << name() << ": Encoding value " << sendEvent -> mask + << " as mask field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sendEvent -> mask, 32, + clientCache -> sendEventMaskCache); + + cachedSendEvent -> mask = sendEvent -> mask; + + #ifdef TEST + *logofs << name() << ": Encoding value " << sendEvent -> code + << " as code field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sendEvent -> code, 8, + clientCache -> sendEventCodeCache); + + cachedSendEvent -> code = sendEvent -> code; + + #ifdef TEST + *logofs << name() << ": Encoding value " << sendEvent -> byte_data + << " as byte_data field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sendEvent -> byte_data, 8, + clientCache -> sendEventByteDataCache); + + cachedSendEvent -> byte_data = sendEvent -> byte_data; + + #ifdef TEST + *logofs << name() << ": Encoding value " << sendEvent -> sequence + << " as sequence field.\n" << logofs_flush; + #endif + + unsigned int diffSeq = sendEvent -> sequence - + clientCache -> sendEventLastSequence; + + clientCache -> sendEventLastSequence = sendEvent -> sequence; + + encodeBuffer.encodeValue(diffSeq, 16, 4); + + cachedSendEvent -> sequence = sendEvent -> sequence; + + #ifdef TEST + *logofs << name() << ": Encoding value " << sendEvent -> int_data + << " as int_data field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sendEvent -> int_data, 32, + clientCache -> sendEventIntDataCache); + + cachedSendEvent -> int_data = sendEvent -> int_data; +} + +void SendEventStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + SendEventMessage *sendEvent = (SendEventMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeBoolValue(value); + + sendEvent -> propagate = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << (unsigned int) sendEvent -> propagate + << " as propagate field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeBoolValue(value); + + if (value) + { + decodeBuffer.decodeBoolValue(value); + } + else + { + decodeBuffer.decodeXidValue(value, clientCache -> windowCache); + } + + sendEvent -> window = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << sendEvent -> window + << " as window field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(sendEvent -> mask, 32, + clientCache -> sendEventMaskCache); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << sendEvent -> mask + << " as mask field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(sendEvent -> code, 8, + clientCache -> sendEventCodeCache); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << sendEvent -> code + << " as code field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(sendEvent -> byte_data, 8, + clientCache -> sendEventByteDataCache); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << sendEvent -> byte_data + << " as byte_data field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeValue(value, 16, 4); + + clientCache -> sendEventLastSequence += value; + clientCache -> sendEventLastSequence &= 0xffff; + + sendEvent -> sequence = clientCache -> sendEventLastSequence; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << sendEvent -> sequence + << " as sequence field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(sendEvent -> int_data, 32, + clientCache -> sendEventIntDataCache); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << sendEvent -> int_data + << " as int_data field.\n" << logofs_flush; + #endif +} diff --git a/nxcomp/SendEvent.h b/nxcomp/SendEvent.h new file mode 100644 index 000000000..9426180a4 --- /dev/null +++ b/nxcomp/SendEvent.h @@ -0,0 +1,187 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef SendEvent_H +#define SendEvent_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define SENDEVENT_ENABLE_CACHE 1 +#define SENDEVENT_ENABLE_DATA 0 +#define SENDEVENT_ENABLE_SPLIT 0 +#define SENDEVENT_ENABLE_COMPRESS 0 + +#define SENDEVENT_DATA_LIMIT 24 +#define SENDEVENT_DATA_OFFSET 20 + +#define SENDEVENT_CACHE_SLOTS 2000 +#define SENDEVENT_CACHE_THRESHOLD 2 +#define SENDEVENT_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class SendEventMessage : public Message +{ + friend class SendEventStore; + + public: + + SendEventMessage() + { + } + + ~SendEventMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char propagate; + unsigned int window; + unsigned int mask; + + // + // These are part of the event data. + // + + unsigned char code; + unsigned char byte_data; + unsigned short sequence; + unsigned int int_data; +}; + +class SendEventStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + SendEventStore() : MessageStore() + { + enableCache = SENDEVENT_ENABLE_CACHE; + enableData = SENDEVENT_ENABLE_DATA; + enableSplit = SENDEVENT_ENABLE_SPLIT; + enableCompress = SENDEVENT_ENABLE_COMPRESS; + + dataLimit = SENDEVENT_DATA_LIMIT; + dataOffset = SENDEVENT_DATA_OFFSET; + + cacheSlots = SENDEVENT_CACHE_SLOTS; + cacheThreshold = SENDEVENT_CACHE_THRESHOLD; + cacheLowerThreshold = SENDEVENT_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~SendEventStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "SendEvent"; + } + + virtual unsigned char opcode() const + { + return X_SendEvent; + } + + virtual unsigned int storage() const + { + return sizeof(SendEventMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new SendEventMessage(); + } + + virtual Message *create(const Message &message) const + { + return new SendEventMessage((const SendEventMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (SendEventMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* SendEvent_H */ diff --git a/nxcomp/SequenceQueue.cpp b/nxcomp/SequenceQueue.cpp new file mode 100644 index 000000000..ce044b1ff --- /dev/null +++ b/nxcomp/SequenceQueue.cpp @@ -0,0 +1,162 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "SequenceQueue.h" + +static const unsigned int INITIAL_SIZE_ = 16; +static const unsigned int GROWTH_INCREMENT = 16; + +SequenceQueue::SequenceQueue() + + : queue_(new RequestSequence[INITIAL_SIZE_]), size_(INITIAL_SIZE_), + length_(0), start_(0), end_(0) +{ +} + +SequenceQueue::~SequenceQueue() +{ + delete [] queue_; +} + +void SequenceQueue::push(unsigned short int sequence, unsigned char opcode, + unsigned int data1, unsigned int data2, + unsigned int data3) +{ + if (length_ == 0) + { + start_ = end_ = 0; + + queue_[0].opcode = opcode; + queue_[0].sequence = sequence; + + queue_[0].data1 = data1; + queue_[0].data2 = data2; + queue_[0].data3 = data3; + + length_ = 1; + + return; + } + + if (length_ == size_) + { + size_ += GROWTH_INCREMENT; + + RequestSequence *newQueue = new RequestSequence[size_]; + + for (int i = start_; (unsigned int) i < length_; i++) + { + newQueue[i - start_] = queue_[i]; + } + + for (int i1 = 0; (unsigned int) i1 < start_; i1++) + { + newQueue[i1 + length_ - start_] = queue_[i1]; + } + + delete [] queue_; + + queue_ = newQueue; + + start_ = 0; + + end_ = length_ - 1; + } + + end_++; + + if (end_ == size_) + { + end_ = 0; + } + + queue_[end_].opcode = opcode; + queue_[end_].sequence = sequence; + + queue_[end_].data1 = data1; + queue_[end_].data2 = data2; + queue_[end_].data3 = data3; + + length_++; +} + +int SequenceQueue::peek(unsigned short int &sequence, + unsigned char &opcode) +{ + if (length_ == 0) + { + return 0; + } + else + { + opcode = queue_[start_].opcode; + sequence = queue_[start_].sequence; + + return 1; + } +} + +int SequenceQueue::peek(unsigned short int &sequence, unsigned char &opcode, + unsigned int &data1, unsigned int &data2, + unsigned int &data3) +{ + if (length_ == 0) + { + return 0; + } + else + { + opcode = queue_[start_].opcode; + sequence = queue_[start_].sequence; + + data1 = queue_[start_].data1; + data2 = queue_[start_].data2; + data3 = queue_[start_].data3; + + return 1; + } +} + +int SequenceQueue::pop(unsigned short int &sequence, unsigned char &opcode, + unsigned int &data1, unsigned int &data2, + unsigned int &data3) +{ + if (length_ == 0) + { + return 0; + } + else + { + opcode = queue_[start_].opcode; + sequence = queue_[start_].sequence; + + data1 = queue_[start_].data1; + data2 = queue_[start_].data2; + data3 = queue_[start_].data3; + + start_++; + + if (start_ == size_) + { + start_ = 0; + } + + length_--; + + return 1; + } +} diff --git a/nxcomp/SequenceQueue.h b/nxcomp/SequenceQueue.h new file mode 100644 index 000000000..b243c4c9f --- /dev/null +++ b/nxcomp/SequenceQueue.h @@ -0,0 +1,83 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef SequenceQueue_H +#define SequenceQueue_H + +// +// List of outstanding request messages which +// are waiting for a reply. This class is used +// in X client and server channels to correlate +// the replies sequence numbers to the original +// request type. +// + +class SequenceQueue +{ + public: + + SequenceQueue(); + + virtual ~SequenceQueue(); + + void push(unsigned short int sequence, unsigned char opcode, + unsigned int data1 = 0, unsigned int data2 = 0, + unsigned int data3 = 0); + + int peek(unsigned short int &sequence, unsigned char &opcode); + + int peek(unsigned short int &sequence, unsigned char &opcode, + unsigned int &data1, unsigned int &data2, + unsigned int &data3); + + int pop(unsigned short int &sequence, unsigned char &opcode, + unsigned int &data1, unsigned int &data2, + unsigned int &data3); + + int pop(unsigned short int &sequence, unsigned char &opcode) + { + unsigned int data1, data2, data3; + + return pop(sequence, opcode, data1, data2, data3); + } + + int length() + { + return length_; + } + + private: + + struct RequestSequence + { + unsigned short int sequence; + unsigned char opcode; + unsigned int data1; + unsigned int data2; + unsigned int data3; + }; + + RequestSequence *queue_; + + unsigned int size_; + unsigned int length_; + + unsigned int start_; + unsigned int end_; +}; + +#endif /* SequenceQueue_H */ diff --git a/nxcomp/ServerCache.cpp b/nxcomp/ServerCache.cpp new file mode 100644 index 000000000..f0cc6f825 --- /dev/null +++ b/nxcomp/ServerCache.cpp @@ -0,0 +1,186 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ServerCache.h" + +// +// Some global caches used to store information +// common to all X connections. +// + +BlockCache ServerCache::lastInitReply; +BlockCache ServerCache::lastKeymap; +unsigned char ServerCache::getKeyboardMappingLastKeysymsPerKeycode = 0; +BlockCache ServerCache::getKeyboardMappingLastMap; +BlockCache ServerCache::getModifierMappingLastMap; +BlockCache ServerCache::xResources; +BlockCacheSet ServerCache::queryFontFontCache(16); + +ServerCache::ServerCache() : + + replySequenceCache(6), eventSequenceCache(6), + lastTimestamp(0), visualCache(8), colormapCache(8), + + errorMinorCache(8), + + colormapNotifyWindowCache(8), colormapNotifyColormapCache(8), + + createNotifyWindowCache(8), createNotifyLastWindow(0), + + exposeWindowCache(12), + + focusInWindowCache(8), + + keyPressLastKey(0), + + mapNotifyEventCache(8), mapNotifyWindowCache(8), + + motionNotifyTimestampCache(8), motionNotifyLastRootX(0), + motionNotifyLastRootY(0), motionNotifyRootXCache(8), + motionNotifyRootYCache(8), motionNotifyEventXCache(8), + motionNotifyEventYCache(8), motionNotifyStateCache(8), + + noExposeDrawableCache(8), noExposeMinorCache(8), + + propertyNotifyWindowCache(8), propertyNotifyAtomCache(8), + + reparentNotifyWindowCache(8), + + selectionClearWindowCache(8), selectionClearAtomCache(8), + + visibilityNotifyWindowCache(8), + + getGeometryRootCache(8), + + getInputFocusWindowCache(8), + + getKeyboardMappingKeysymCache(8), + + getPropertyTypeCache(8), + getPropertyTextCompressor(textCache, SERVER_TEXT_CACHE_SIZE), + + getSelectionOwnerCache(8), + + getWindowAttributesClassCache(8), getWindowAttributesPlanesCache(8), + getWindowAttributesPixelCache(8), getWindowAttributesAllEventsCache(8), + getWindowAttributesYourEventsCache(8), + getWindowAttributesDontPropagateCache(8), + + queryPointerRootCache(8), queryPointerChildCache(8), + + translateCoordsChildCache(8), translateCoordsXCache(8), + translateCoordsYCache(8), + + queryTreeWindowCache(8), + + getAtomNameTextCompressor(textCache, SERVER_TEXT_CACHE_SIZE) + +{ + unsigned int i; + + for (i = 0; i < 3; i++) + { + configureNotifyWindowCache[i] = new IntCache(8); + } + + for (i = 0; i < 5; i++) + { + configureNotifyGeomCache[i] = new IntCache(8); + } + + for (i = 0; i < 5; i++) + { + exposeGeomCache[i] = new IntCache(8); + } + + for (i = 0; i < 3; i++) + { + motionNotifyWindowCache[i] = new IntCache(8); + } + + for (i = 0; i < 5; i++) + { + getGeometryGeomCache[i] = new IntCache(8); + } + + for (i = 0; i < 23; i++) + { + keyPressCache[i] = 0; + } + + for (i = 0; i < 6; i++) + { + queryFontCharInfoCache[i] = new IntCache(8); + queryFontLastCharInfo[i] = 0; + } + + for (i = 0; i < 12; i++) + { + genericReplyIntCache[i] = new IntCache(8); + } + + for (i = 0; i < 14; i++) + { + genericEventIntCache[i] = new IntCache(8); + } +} + + +ServerCache::~ServerCache() +{ + unsigned int i; + + for (i = 0; i < 3; i++) + { + delete configureNotifyWindowCache[i]; + } + + for (i = 0; i < 5; i++) + { + delete configureNotifyGeomCache[i]; + } + + for (i = 0; i < 5; i++) + { + delete exposeGeomCache[i]; + } + + for (i = 0; i < 3; i++) + { + delete motionNotifyWindowCache[i]; + } + + for (i = 0; i < 5; i++) + { + delete getGeometryGeomCache[i]; + } + + for (i = 0; i < 6; i++) + { + delete queryFontCharInfoCache[i]; + } + + for (i = 0; i < 12; i++) + { + delete genericReplyIntCache[i]; + } + + for (i = 0; i < 14; i++) + { + delete genericEventIntCache[i]; + } +} diff --git a/nxcomp/ServerCache.h b/nxcomp/ServerCache.h new file mode 100644 index 000000000..ec213b89f --- /dev/null +++ b/nxcomp/ServerCache.h @@ -0,0 +1,305 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ServerCache_H +#define ServerCache_H + +#include "Misc.h" + +#include "IntCache.h" +#include "CharCache.h" +#include "OpcodeCache.h" +#include "TextCompressor.h" +#include "BlockCache.h" +#include "BlockCacheSet.h" + +#include "ChannelCache.h" + +class ServerCache : public ChannelCache +{ + public: + + ServerCache(); + + ~ServerCache(); + + // + // Opcode prediction caches. + // + + OpcodeCache opcodeCache; + + // + // General-purpose caches. + // + + CharCache textCache[SERVER_TEXT_CACHE_SIZE]; + IntCache replySequenceCache; + IntCache eventSequenceCache; + unsigned int lastTimestamp; + CharCache depthCache; + IntCache visualCache; + IntCache colormapCache; + CharCache resourceCache; + + // + // X connection startup. + // + + static BlockCache lastInitReply; + + // + // X errors. + // + + CharCache errorCodeCache; + IntCache errorMinorCache; + CharCache errorMajorCache; + + // + // ButtonPress and ButtonRelease events. + // + + CharCache buttonCache; + + // + // ColormapNotify event. + // + + IntCache colormapNotifyWindowCache; + IntCache colormapNotifyColormapCache; + + // + // ConfigureNotify event. + // + + IntCache *configureNotifyWindowCache[3]; + IntCache *configureNotifyGeomCache[5]; + + // + // CreateNotify event. + // + + IntCache createNotifyWindowCache; + unsigned int createNotifyLastWindow; + + // + // Expose event. + // + + IntCache exposeWindowCache; + IntCache *exposeGeomCache[5]; + + // + // FocusIn event (also used for FocusOut). + // + + IntCache focusInWindowCache; + + // + // KeymapNotify event. + // + + static BlockCache lastKeymap; + + // + // KeyPress event. + // + + unsigned char keyPressLastKey; + unsigned char keyPressCache[23]; + + // + // MapNotify event (also used for UnmapNotify). + // + + IntCache mapNotifyEventCache; + IntCache mapNotifyWindowCache; + + // + // MotionNotify event (also used for KeyPress, + // KeyRelease, ButtonPress, ButtonRelease, + // EnterNotify, and LeaveNotify events and + // QueryPointer reply). + // + + IntCache motionNotifyTimestampCache; + unsigned int motionNotifyLastRootX; + unsigned int motionNotifyLastRootY; + IntCache motionNotifyRootXCache; + IntCache motionNotifyRootYCache; + IntCache motionNotifyEventXCache; + IntCache motionNotifyEventYCache; + IntCache motionNotifyStateCache; + IntCache *motionNotifyWindowCache[3]; + + // + // NoExpose event. + // + + IntCache noExposeDrawableCache; + IntCache noExposeMinorCache; + CharCache noExposeMajorCache; + + // + // PropertyNotify event. + // + + IntCache propertyNotifyWindowCache; + IntCache propertyNotifyAtomCache; + + // + // ReparentNotify event. + // + + IntCache reparentNotifyWindowCache; + + // + // SelectionClear event. + // + + IntCache selectionClearWindowCache; + IntCache selectionClearAtomCache; + + // + // VisibilityNotify event. + // + + IntCache visibilityNotifyWindowCache; + + // + // GetGeometry reply. + // + + IntCache getGeometryRootCache; + IntCache *getGeometryGeomCache[5]; + + // + // GetInputFocus reply. + // + + IntCache getInputFocusWindowCache; + + // + // GetKeyboardMapping reply. + // + + static unsigned char getKeyboardMappingLastKeysymsPerKeycode; + static BlockCache getKeyboardMappingLastMap; + IntCache getKeyboardMappingKeysymCache; + CharCache getKeyboardMappingLastByteCache; + + // + // GetModifierMapping reply. + // + + static BlockCache getModifierMappingLastMap; + + // + // GetProperty reply. + // + + CharCache getPropertyFormatCache; + IntCache getPropertyTypeCache; + TextCompressor getPropertyTextCompressor; + static BlockCache xResources; + + // + // GetSelection reply. + // + + IntCache getSelectionOwnerCache; + + // + // GetWindowAttributes reply. + // + + IntCache getWindowAttributesClassCache; + CharCache getWindowAttributesBitGravityCache; + CharCache getWindowAttributesWinGravityCache; + IntCache getWindowAttributesPlanesCache; + IntCache getWindowAttributesPixelCache; + IntCache getWindowAttributesAllEventsCache; + IntCache getWindowAttributesYourEventsCache; + IntCache getWindowAttributesDontPropagateCache; + + // + // QueryColors reply. + // + + BlockCache queryColorsLastReply; + + // + // QueryFont reply. + // + + static BlockCacheSet queryFontFontCache; + IntCache *queryFontCharInfoCache[6]; + unsigned int queryFontLastCharInfo[6]; + + // + // QueryPointer reply. + // + + IntCache queryPointerRootCache; + IntCache queryPointerChildCache; + + // + // TranslateCoords reply. + // + + IntCache translateCoordsChildCache; + IntCache translateCoordsXCache; + IntCache translateCoordsYCache; + + // + // QueryTree reply. + // + + IntCache queryTreeWindowCache; + + // + // GetAtomName reply in protocol + // versions >= 3. + // + + TextCompressor getAtomNameTextCompressor; + + // + // Generic reply. Use short data + // in protocol versions >= 3. + // + + CharCache genericReplyCharCache; + IntCache *genericReplyIntCache[12]; + + // + // Generic event. Only in protocol + // versions >= 3. + // + + CharCache genericEventCharCache; + IntCache *genericEventIntCache[14]; + + // + // Used in the abort split events. + // + + OpcodeCache abortOpcodeCache; +}; + +#endif /* ServerCache_H */ diff --git a/nxcomp/ServerChannel.cpp b/nxcomp/ServerChannel.cpp new file mode 100644 index 000000000..4e6dea324 --- /dev/null +++ b/nxcomp/ServerChannel.cpp @@ -0,0 +1,8258 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <string.h> +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/shm.h> + +#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; + + handleSplitEnable(); + + // + // It will be eventually set by + // the server proxy. + // + + fontPort_ = -1; + + #ifdef REFERENCES + *logofs << "ServerChannel: Created new object at " + << this << " for FD#" << fd_ << " out of " + << ++references_ << " allocated channels.\n" + << logofs_flush; + #endif +} + +ServerChannel::~ServerChannel() +{ + #ifdef TEST + *logofs << "ServerChannel: Freeing image state information.\n" + << logofs_flush; + #endif + + handleImageStateRemove(); + + #ifdef TEST + *logofs << "ServerChannel: Freeing shared memory information.\n" + << logofs_flush; + #endif + + handleShmemStateRemove(); + + #ifdef TEST + *logofs << "ServerChannel: Freeing unpack state information.\n" + << logofs_flush; + #endif + + for (int i = 0; i < CONNECTIONS_LIMIT; i++) + { + handleUnpackStateRemove(i); + } + + #ifdef TEST + *logofs << "ServerChannel: Freeing channel caches.\n" + << logofs_flush; + #endif + + #ifdef REFERENCES + *logofs << "ServerChannel: Deleted object at " + << this << " for FD#" << fd_ << " out of " + << --references_ << " allocated channels.\n" + << logofs_flush; + #endif +} + +// +// Beginning of handleRead(). +// + +int ServerChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, + unsigned int length) +{ + #ifdef DEBUG + *logofs << "handleRead: Called for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Pointer to located message and + // its size in bytes. + // + + const unsigned char *inputMessage; + unsigned int inputLength; + + // + // Set when message is found in + // cache. + // + + int hit; + + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: Trying to read from FD#" + << fd_ << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + int result = readBuffer_.readMessage(); + + #if defined(DEBUG) || defined(INFO) + *logofs << "handleRead: Read result on FD#" << fd_ + << " is " << result << ".\n" + << logofs_flush; + #endif + + if (result < 0) + { + // + // Let the proxy close the channel. + // + + return -1; + } + else if (result == 0) + { + #if defined(TEST) || defined(INFO) + + // + // This can happen because we have the descriptor + // selected in the read set but we already read + // the data asynchronously, while decoding data + // read from the proxy. + // + + *logofs << "handleRead: WARNING! No data read from FD#" + << fd_ << " while encoding messages.\n" + << logofs_flush; + + #endif + + return 0; + } + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "handleRead: Encoding messages for FD#" << fd_ + << " with " << readBuffer_.getLength() << " bytes " + << "in the buffer.\n" << logofs_flush; + #endif + + // + // Extract any complete message which + // is available in the buffer. + // + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL) + { + hit = 0; + + if (firstReply_) + { + // + // Handle the X server's authorization reply. + // + + if (handleAuthorization(inputMessage, inputLength) < 0) + { + return -1; + } + + imageByteOrder_ = inputMessage[30]; + bitmapBitOrder_ = inputMessage[31]; + scanlineUnit_ = inputMessage[32]; + scanlinePad_ = inputMessage[33]; + + encodeBuffer.encodeValue((unsigned int) inputMessage[0], 8); + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 8); + encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_), 16); + encodeBuffer.encodeValue(GetUINT(inputMessage + 4, bigEndian_), 16); + encodeBuffer.encodeValue(GetUINT(inputMessage + 6, bigEndian_), 16); + + if (ServerCache::lastInitReply.compare(inputLength - 8, inputMessage + 8)) + { + encodeBuffer.encodeBoolValue(1); + } + else + { + encodeBuffer.encodeBoolValue(0); + + for (unsigned int i = 8; i < inputLength; i++) + { + encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8); + } + } + + firstReply_ = 0; + + #if defined(TEST) || defined(OPCODES) + + int bits = encodeBuffer.diffBits(); + + *logofs << "handleRead: Handled first reply. " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + + #endif + + priority_++; + + // + // Due to the way the loop was implemented + // we can't encode multiple messages if we + // are encoding the first request. + // + + if (control -> isProtoStep7() == 0) + { + if (proxy -> handleAsyncInit() < 0) + { + return -1; + } + } + } + else + { + // + // NX client needs this line to consider + // the initialization phase successfully + // completed. + // + + if (firstClient_ == -1) + { + cerr << "Info" << ": Established X server connection.\n" ; + + firstClient_ = fd_; + } + + // + // Check if this is a reply. + // + + if (*inputMessage == X_Reply) + { + int bits = 0; + + unsigned char inputOpcode = *inputMessage; + + unsigned short int requestSequenceNum; + unsigned char requestOpcode; + unsigned int requestData[3]; + + unsigned int sequenceNum = GetUINT(inputMessage + 2, bigEndian_); + + #ifdef SUSPEND + + if (sequenceNum >= 1000) + { + cerr << "Warning" << ": Exiting to test the resilience of the agent.\n"; + + sleep(2); + + HandleAbort(); + } + + #endif + + // + // We managed all the events and errors caused + // by the previous requests. We can now reset + // the queue of split commits. + // + + clearCommitQueue(); + + // + // Encode opcode and difference between + // current sequence and the last one. + // + + encodeBuffer.encodeOpcodeValue(inputOpcode, serverCache_ -> opcodeCache); + + unsigned int sequenceDiff = sequenceNum - serverSequence_; + + serverSequence_ = sequenceNum; + + #ifdef DEBUG + *logofs << "handleRead: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sequenceDiff, 16, + serverCache_ -> replySequenceCache, 7); + + // + // Now handle the data part. + // + + if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) && + requestSequenceNum == sequenceNum) + { + // + // We've found the request that generated this reply. + // It is possible to compress the reply based on the + // specific request type. + // + + sequenceQueue_.pop(requestSequenceNum, requestOpcode, + requestData[0], requestData[1], requestData[2]); + + // + // If differential compression is disabled + // then use the most simple encoding. + // + + if (control -> LocalDeltaCompression == 0) + { + int result = handleFastReadReply(encodeBuffer, requestOpcode, + inputMessage, inputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + switch (requestOpcode) + { + case X_AllocColor: + { + const unsigned char *nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + unsigned int colorValue = GetUINT(nextSrc, bigEndian_); + nextSrc += 2; + if (colorValue == requestData[i]) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(colorValue - colorValue, 16, 6); + } + } + unsigned int pixel = GetULONG(inputMessage + 16, bigEndian_); + encodeBuffer.encodeValue(pixel, 32, 9); + + priority_++; + } + break; + case X_GetAtomName: + { + unsigned int nameLength = GetUINT(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(nameLength, 16, 6); + const unsigned char *nextSrc = inputMessage + 32; + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, nameLength); + } + else + { + serverCache_ -> getAtomNameTextCompressor.reset(); + for (unsigned int i = 0; i < nameLength; i++) + { + serverCache_ -> getAtomNameTextCompressor. + encodeChar(*nextSrc++, encodeBuffer); + } + } + + priority_++; + } + break; + case X_GetGeometry: + { + // + // TODO: This obtains a satisfactory 10:1, but + // could be cached to leverage the big amount + // of such requests issued by QT clients. + // + + encodeBuffer.encodeCachedValue(inputMessage[1], 8, + serverCache_ -> depthCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> getGeometryRootCache, 9); + const unsigned char *nextSrc = inputMessage + 12; + for (unsigned int i = 0; i < 5; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *serverCache_ -> getGeometryGeomCache[i], 8); + nextSrc += 2; + } + + priority_++; + } + break; + case X_GetInputFocus: + { + // + // Is it a real X_GetInputFocus or a + // masqueraded reply? + // + + if (requestData[0] == X_GetInputFocus) + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> getInputFocusWindowCache); + + priority_++; + } + else + { + // + // TODO: We are not setting priority in case + // of replies other than real X_GetInputFocus + // or X_NXGetUnpackParameters. We should check + // once again that this is OK. + // + + #ifdef TEST + *logofs << "handleRead: Received tainted X_GetInputFocus reply " + << "for request OPCODE#" << requestData[0] << " with " + << "sequence " << sequenceNum << ".\n" + << logofs_flush; + #endif + + // + // Don't encode any data in case of sync + // messages or any other reply for which + // opcode is enough. + // + + if (requestData[0] == opcodeStore_ -> getUnpackParameters) + { + for (int i = 0; i < PACK_METHOD_LIMIT; i++) + { + encodeBuffer.encodeBoolValue(control -> LocalUnpackMethods[i]); + } + + priority_++; + } + else if (requestData[0] == opcodeStore_ -> getShmemParameters) + { + if (handleShmemReply(encodeBuffer, requestOpcode, requestData[1], + inputMessage, inputLength) < 0) + { + return -1; + } + + priority_++; + } + else if (requestData[0] == opcodeStore_ -> getFontParameters) + { + if (handleFontReply(encodeBuffer, requestOpcode, + inputMessage, inputLength) < 0) + { + return -1; + } + } + + // + // Account this data to the original opcode. + // + + requestOpcode = requestData[0]; + } + } + break; + case X_GetKeyboardMapping: + { + unsigned int keysymsPerKeycode = (unsigned int) inputMessage[1]; + if (ServerCache::getKeyboardMappingLastMap.compare(inputLength - 32, + inputMessage + 32) && (keysymsPerKeycode == + ServerCache::getKeyboardMappingLastKeysymsPerKeycode)) + { + encodeBuffer.encodeBoolValue(1); + + priority_++; + + break; + } + ServerCache::getKeyboardMappingLastKeysymsPerKeycode = keysymsPerKeycode; + encodeBuffer.encodeBoolValue(0); + unsigned int numKeycodes = + (((inputLength - 32) / keysymsPerKeycode) >> 2); + encodeBuffer.encodeValue(numKeycodes, 8); + encodeBuffer.encodeValue(keysymsPerKeycode, 8, 4); + const unsigned char *nextSrc = inputMessage + 32; + unsigned char previous = 0; + for (unsigned int count = numKeycodes * keysymsPerKeycode; + count; --count) + { + unsigned int keysym = GetULONG(nextSrc, bigEndian_); + nextSrc += 4; + if (keysym == NoSymbol) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + unsigned int first3Bytes = (keysym >> 8); + encodeBuffer.encodeCachedValue(first3Bytes, 24, + serverCache_ -> getKeyboardMappingKeysymCache, 9); + unsigned char lastByte = (unsigned char) (keysym & 0xff); + encodeBuffer.encodeCachedValue(lastByte - previous, 8, + serverCache_ -> getKeyboardMappingLastByteCache, 5); + previous = lastByte; + } + } + + priority_++; + } + break; + case X_GetModifierMapping: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 8); + const unsigned char *nextDest = inputMessage + 32; + if (ServerCache::getModifierMappingLastMap.compare(inputLength - 32, + nextDest)) + { + encodeBuffer.encodeBoolValue(1); + + priority_++; + + break; + } + encodeBuffer.encodeBoolValue(0); + for (unsigned int count = inputLength - 32; count; count--) + { + unsigned char next = *nextDest++; + if (next == 0) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(next, 8); + } + } + + priority_++; + } + break; + case X_GetProperty: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_GetProperty); + + hit = handleEncode(encodeBuffer, serverCache_, messageStore, + requestOpcode, inputMessage, inputLength); + + priority_++; + } + break; + case X_GetSelectionOwner: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> getSelectionOwnerCache, 9); + priority_++; + } + break; + case X_GetWindowAttributes: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> visualCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), + 16, serverCache_ -> getWindowAttributesClassCache, 3); + encodeBuffer.encodeCachedValue(inputMessage[14], 8, + serverCache_ -> getWindowAttributesBitGravityCache); + encodeBuffer.encodeCachedValue(inputMessage[15], 8, + serverCache_ -> getWindowAttributesWinGravityCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), + 32, serverCache_ -> getWindowAttributesPlanesCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 20, bigEndian_), + 32, serverCache_ -> getWindowAttributesPixelCache, 9); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[24]); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[25]); + encodeBuffer.encodeValue((unsigned int) inputMessage[26], 2); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[27]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 28, bigEndian_), + 29, serverCache_ -> colormapCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 32, bigEndian_), + 32, serverCache_ -> getWindowAttributesAllEventsCache); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 36, bigEndian_), + 32, serverCache_ -> getWindowAttributesYourEventsCache); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 40, bigEndian_), + 16, serverCache_ -> getWindowAttributesDontPropagateCache); + + priority_++; + } + break; + case X_GrabKeyboard: + case X_GrabPointer: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 3); + + priority_++; + } + break; + case X_InternAtom: + { + encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 29, 9); + + priority_++; + } + break; + case X_ListExtensions: + { + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8); + unsigned int numExtensions = (unsigned int) inputMessage[1]; + encodeBuffer.encodeValue(numExtensions, 8); + const unsigned char *nextSrc = inputMessage + 32; + + for (; numExtensions; numExtensions--) + { + unsigned int length = (unsigned int) (*nextSrc++); + + encodeBuffer.encodeValue(length, 8); + + #ifdef HIDE_MIT_SHM_EXTENSION + + if (!strncmp((char *) nextSrc, "MIT-SHM", 7)) + { + #ifdef TEST + *logofs << "handleRead: Hiding MIT-SHM extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-MIT-", 7); + } + + #endif + + #ifdef HIDE_BIG_REQUESTS_EXTENSION + + if (!strncmp((char *) nextSrc, "BIG-REQUESTS", 12)) + { + #ifdef TEST + *logofs << "handleRead: Hiding BIG-REQUESTS extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-BIG-REQUE", 12); + } + + #endif + + #ifdef HIDE_XKEYBOARD_EXTENSION + + if (!strncmp((char *) nextSrc, "XKEYBOARD", 9)) + { + #ifdef TEST + *logofs << "handleRead: Hiding XKEYBOARD extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-XKEYBO", 9); + } + + #endif + + #ifdef HIDE_XFree86_Bigfont_EXTENSION + + if (!strncmp((char *) nextSrc, "XFree86-Bigfont", 15)) + { + #ifdef TEST + *logofs << "handleRead: Hiding XFree86-Bigfont extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-XFree86-Bigf", 15); + } + + #endif + + #ifdef HIDE_SHAPE_EXTENSION + + if (!strncmp((char *) nextSrc, "SHAPE", 5)) + { + #ifdef TEST + *logofs << "handleRead: Hiding SHAPE extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-SH", 5); + } + + #endif + + // + // Check if user disabled RENDER extension. + // + + if (control -> HideRender == 1 && + !strncmp((char *) nextSrc, "RENDER", 6)) + { + #ifdef TEST + *logofs << "handleRead: Hiding RENDER extension in reply.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) nextSrc, "NO-REN", 6); + } + + for (; length; length--) + { + encodeBuffer.encodeValue((unsigned int) (*nextSrc++), 8); + } + } + + priority_++; + } + break; + case X_ListFonts: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_ListFonts); + + if (handleEncodeCached(encodeBuffer, serverCache_, messageStore, + inputMessage, inputLength)) + { + priority_++; + + hit = 1; + + break; + } + + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8); + unsigned int numFonts = GetUINT(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(numFonts, 16, 6); + + // Differential encoding. + encodeBuffer.encodeBoolValue(1); + + const unsigned char* nextSrc = inputMessage + 32; + for (; numFonts; numFonts--) + { + unsigned int length = (unsigned int) (*nextSrc++); + encodeBuffer.encodeValue(length, 8); + + if (control -> isProtoStep7() == 1) + { + encodeBuffer.encodeTextData(nextSrc, length); + + nextSrc += length; + } + else + { + serverCache_ -> getPropertyTextCompressor.reset(); + for (; length; length--) + { + serverCache_ -> getPropertyTextCompressor.encodeChar( + *nextSrc++, encodeBuffer); + } + } + } + + priority_++; + } + break; + case X_LookupColor: + case X_AllocNamedColor: + { + const unsigned char *nextSrc = inputMessage + 8; + if (requestOpcode == X_AllocNamedColor) + { + encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 9); + nextSrc += 4; + } + unsigned int count = 3; + do + { + unsigned int exactColor = GetUINT(nextSrc, bigEndian_); + encodeBuffer.encodeValue(exactColor, 16, 9); + unsigned int visualColor = GetUINT(nextSrc + 6, bigEndian_) - + exactColor; + encodeBuffer.encodeValue(visualColor, 16, 5); + nextSrc += 2; + } + while (--count); + + priority_++; + } + break; + case X_QueryBestSize: + { + encodeBuffer.encodeValue(GetUINT(inputMessage + 8, bigEndian_), 16, 8); + encodeBuffer.encodeValue(GetUINT(inputMessage + 10, bigEndian_), 16, 8); + + priority_++; + } + break; + case X_QueryColors: + { + // Differential encoding. + encodeBuffer.encodeBoolValue(1); + + unsigned int numColors = ((inputLength - 32) >> 3); + const unsigned char *nextSrc = inputMessage + 40; + unsigned char *nextDest = (unsigned char *) inputMessage + 38; + for (unsigned int c = 1; c < numColors; c++) + { + for (unsigned int i = 0; i < 6; i++) + *nextDest++ = *nextSrc++; + nextSrc += 2; + } + unsigned int colorsLength = numColors * 6; + if (serverCache_ -> queryColorsLastReply.compare(colorsLength, + inputMessage + 32)) + encodeBuffer.encodeBoolValue(1); + else + { + const unsigned char *nextSrc = inputMessage + 32; + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(numColors, 16, 5); + for (numColors *= 3; numColors; numColors--) + { + encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16); + nextSrc += 2; + } + } + + priority_++; + } + break; + case X_QueryExtension: + { + if (requestData[0] == X_QueryExtension) + { + // + // Value in requestData[0] will be nonzero + // if the request is for an extension that + // we should hide to the X client. + // + + if (requestData[1]) + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue(0, 8); + } + else + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[8]); + encodeBuffer.encodeValue((unsigned int) inputMessage[9], 8); + } + + encodeBuffer.encodeValue((unsigned int) inputMessage[10], 8); + encodeBuffer.encodeValue((unsigned int) inputMessage[11], 8); + + if (requestData[2] == X_NXInternalShapeExtension) + { + opcodeStore_ -> shapeExtension = inputMessage[9]; + + #ifdef TEST + *logofs << "handleRead: Shape extension opcode for FD#" << fd_ + << " is " << (unsigned int) opcodeStore_ -> shapeExtension + << ".\n" << logofs_flush; + #endif + } + else if (requestData[2] == X_NXInternalRenderExtension) + { + opcodeStore_ -> renderExtension = inputMessage[9]; + + #ifdef TEST + *logofs << "handleRead: Render extension opcode for FD#" << fd_ + << " is " << (unsigned int) opcodeStore_ -> renderExtension + << ".\n" << logofs_flush; + #endif + } + + priority_++; + } + else + { + #ifdef TEST + *logofs << "handleRead: Received tainted X_QueryExtension reply " + << "for request OPCODE#" << requestData[0] << " with " + << "sequence " << sequenceNum << ".\n" + << logofs_flush; + #endif + + if (requestData[0] == opcodeStore_ -> getShmemParameters) + { + if (handleShmemReply(encodeBuffer, requestOpcode, requestData[1], + inputMessage, inputLength) < 0) + { + return -1; + } + + priority_++; + } + + // + // Account this data to the original opcode. + // + + requestOpcode = requestData[0]; + } + } + break; + case X_QueryFont: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_QueryFont); + + if (handleEncodeCached(encodeBuffer, serverCache_, messageStore, + inputMessage, inputLength)) + { + priority_++; + + hit = 1; + + break; + } + + // Differential encoding. + encodeBuffer.encodeBoolValue(1); + + unsigned int numProperties = GetUINT(inputMessage + 46, bigEndian_); + unsigned int numCharInfos = GetULONG(inputMessage + 56, bigEndian_); + encodeBuffer.encodeValue(numProperties, 16, 8); + encodeBuffer.encodeValue(numCharInfos, 32, 10); + handleEncodeCharInfo(inputMessage + 8, encodeBuffer); + handleEncodeCharInfo(inputMessage + 24, encodeBuffer); + encodeBuffer.encodeValue(GetUINT(inputMessage + 40, bigEndian_), 16, 9); + encodeBuffer.encodeValue(GetUINT(inputMessage + 42, bigEndian_), 16, 9); + encodeBuffer.encodeValue(GetUINT(inputMessage + 44, bigEndian_), 16, 9); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[48]); + encodeBuffer.encodeValue((unsigned int) inputMessage[49], 8); + encodeBuffer.encodeValue((unsigned int) inputMessage[50], 8); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[51]); + encodeBuffer.encodeValue(GetUINT(inputMessage + 52, bigEndian_), 16, 9); + encodeBuffer.encodeValue(GetUINT(inputMessage + 54, bigEndian_), 16, 9); + const unsigned char *nextSrc = inputMessage + 60; + unsigned int index; + + int end = 0; + + if (ServerCache::queryFontFontCache.lookup(numProperties * 8 + + numCharInfos * 12, nextSrc, index)) + { + encodeBuffer.encodeBoolValue(1); + encodeBuffer.encodeValue(index, 4); + + end = 1; + } + + if (end == 0) + { + encodeBuffer.encodeBoolValue(0); + for (; numProperties; numProperties--) + { + encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 9); + encodeBuffer.encodeValue(GetULONG(nextSrc + 4, bigEndian_), 32, 9); + nextSrc += 8; + } + for (; numCharInfos; numCharInfos--) + { + handleEncodeCharInfo(nextSrc, encodeBuffer); + + nextSrc += 12; + } + } + + priority_++; + } + break; + case X_QueryPointer: + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> queryPointerRootCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), + 29, serverCache_ -> queryPointerChildCache, 9); + unsigned int rootX = GetUINT(inputMessage + 16, bigEndian_); + unsigned int rootY = GetUINT(inputMessage + 18, bigEndian_); + unsigned int eventX = GetUINT(inputMessage + 20, bigEndian_); + unsigned int eventY = GetUINT(inputMessage + 22, bigEndian_); + eventX -= rootX; + eventY -= rootY; + encodeBuffer.encodeCachedValue( + rootX - serverCache_ -> motionNotifyLastRootX, 16, + serverCache_ -> motionNotifyRootXCache, 8); + serverCache_ -> motionNotifyLastRootX = rootX; + encodeBuffer.encodeCachedValue( + rootY - serverCache_ -> motionNotifyLastRootY, 16, + serverCache_ -> motionNotifyRootYCache, 8); + serverCache_ -> motionNotifyLastRootY = rootY; + encodeBuffer.encodeCachedValue(eventX, 16, + serverCache_ -> motionNotifyEventXCache, 8); + encodeBuffer.encodeCachedValue(eventY, 16, + serverCache_ -> motionNotifyEventYCache, 8); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 24, bigEndian_), + 16, serverCache_ -> motionNotifyStateCache); + priority_++; + } + break; + case X_QueryTree: + { + // + // This was very inefficient. In practice + // it just copied data on the output. Now + // it obtains an average 7:1 compression + // and could optionally be cached. + // + + unsigned int children = GetUINT(inputMessage + 16, bigEndian_); + + encodeBuffer.encodeValue(children, 16, 8); + + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, + serverCache_ -> queryTreeWindowCache); + + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29, + serverCache_ -> queryTreeWindowCache); + + const unsigned char *next = inputMessage + 32; + + for (unsigned int i = 0; i < children; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(next + (i * 4), bigEndian_), 29, + serverCache_ -> queryTreeWindowCache); + } + + priority_++; + } + break; + case X_TranslateCoords: + { + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> translateCoordsChildCache, 9); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), + 16, serverCache_ -> translateCoordsXCache, 8); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 14, bigEndian_), + 16, serverCache_ -> translateCoordsYCache, 8); + priority_++; + } + break; + case X_GetImage: + { + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_GetImage); + + if (handleEncodeCached(encodeBuffer, serverCache_, messageStore, + inputMessage, inputLength)) + { + priority_++; + + hit = 1; + + break; + } + + // Depth. + encodeBuffer.encodeCachedValue(inputMessage[1], 8, + serverCache_ -> depthCache); + // Reply length. + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 9); + + // Visual. + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29, + serverCache_ -> visualCache); + + if (control -> isProtoStep8() == 0) + { + unsigned int compressedDataSize = 0; + unsigned char *compressedData = NULL; + + int compressed = handleCompress(encodeBuffer, requestOpcode, messageStore -> dataOffset, + inputMessage, inputLength, compressedData, + compressedDataSize); + if (compressed < 0) + { + return -1; + } + else if (compressed > 0) + { + // + // Update size according to result of image compression. + // + + handleUpdate(messageStore, inputLength - messageStore -> + dataOffset, compressedDataSize); + } + } + else + { + handleCopy(encodeBuffer, requestOpcode, messageStore -> + dataOffset, inputMessage, inputLength); + } + + priority_++; + } + break; + case X_GetPointerMapping: + { + encodeBuffer.encodeValue(inputMessage[1], 8, 4); + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 4); + for (unsigned int i = 32; i < inputLength; i++) + encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8, 4); + + priority_++; + } + break; + case X_GetKeyboardControl: + { + encodeBuffer.encodeValue(inputMessage[1], 8, 2); + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8); + for (unsigned int i = 8; i < inputLength; i++) + encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8, 4); + + priority_++; + } + break; + default: + { + #ifdef PANIC + *logofs << "ServerChannel: PANIC! No matching request with " + << "OPCODE#" << (unsigned int) requestOpcode + << " for reply with sequence number " + << requestSequenceNum << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": No matching request with OPCODE#" + << (unsigned int) requestOpcode << " for reply with " + << "sequence number " << requestSequenceNum << ".\n"; + + return -1; + } + } + + bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + const char *cacheString = (hit ? "cached " : ""); + + *logofs << "handleRead: Handled " << cacheString << "reply to OPCODE#" + << (unsigned int) requestOpcode << " (" << DumpOpcode(requestOpcode) + << ") for FD#" << fd_ << " sequence " << serverSequence_ + << ". " << inputLength << " bytes in, " << bits << " bits (" + << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; + + #endif + + } // End of if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) && ... + else + { + // + // We didn't push the request opcode. + // Check if fast encoding is required. + // + + requestOpcode = X_Reply; + + if (control -> LocalDeltaCompression == 0) + { + int result = handleFastReadReply(encodeBuffer, requestOpcode, + inputMessage, inputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + // + // Group all replies whose opcode was not + // pushed in sequence number queue under + // the category 'generic reply'. + // + + #ifdef DEBUG + *logofs << "handleRead: Identified generic reply.\n" + << logofs_flush; + #endif + + MessageStore *messageStore = serverStore_ -> + getReplyStore(X_NXInternalGenericReply); + + hit = handleEncode(encodeBuffer, serverCache_, messageStore, + requestOpcode, inputMessage, inputLength); + + priority_++; + + bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + const char *cacheString = (hit ? "cached " : ""); + + *logofs << "handleRead: Handled " << cacheString << "generic reply " + << "OPCODE#" << X_NXInternalGenericReply << " for FD#" << fd_ + << " sequence " << serverSequence_ << ". " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + + #endif + } + + if (hit) + { + statistics -> addCachedReply(requestOpcode); + } + + statistics -> addReplyBits(requestOpcode, inputLength << 3, bits); + + } // End of if (inputMessage[0] == 1) ... + else + { + // + // Event or error. + // + + unsigned char inputOpcode = *inputMessage; + + unsigned int inputSequence = GetUINT(inputMessage + 2, bigEndian_); + + // + // Check if this is an event which we can discard. + // + + if ((inputOpcode == Expose && enableExpose_ == 0) || + (inputOpcode == GraphicsExpose && enableGraphicsExpose_ == 0) || + (inputOpcode == NoExpose && enableNoExpose_ == 0)) + { + continue; + } + else if (shmemState_ != NULL && shmemState_ -> enabled == 1 && + inputOpcode == shmemState_ -> event && + checkShmemEvent(inputOpcode, inputSequence, + inputMessage) > 0) + { + continue; + } + else if (inputOpcode == MotionNotify) + { + // + // Save the motion event and send when another + // event or error is received or the motion ti- + // meout is elapsed. If a previous motion event + // was already saved, we replace it with the + // new one and don't reset the timeout, so we + // still have a motion event every given ms. + // + + memcpy(lastMotion_, inputMessage, 32); + + #ifdef TEST + *logofs << "handleRead: Saved suppressed motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + continue; + } + else if (inputOpcode == X_Error) + { + // + // Check if this is an error that matches a + // sequence number for which we are expecting + // a reply. + // + + unsigned short int errorSequenceNum; + unsigned char errorOpcode; + + if (sequenceQueue_.peek(errorSequenceNum, errorOpcode) && + ((unsigned int) errorSequenceNum == inputSequence)) + { + sequenceQueue_.pop(errorSequenceNum, errorOpcode); + } + + // + // Check if error is due to an image commit + // generated at the end of a split. + // + + if (checkCommitError(*(inputMessage + 1), inputSequence, inputMessage) > 0) + { + #ifdef TEST + *logofs << "handleRead: Skipping error on image commit for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + continue; + } + + // + // Check if it's an error generated by a request + // concerning shared memory support. + // + + else if (shmemState_ != NULL && (shmemState_ -> sequence == + inputSequence || (shmemState_ -> enabled == 1 && + (shmemState_ -> opcode == *(inputMessage + 10) || + shmemState_ -> error == *(inputMessage + 1)))) && + checkShmemError(*(inputMessage + 1), inputSequence, + inputMessage) > 0) + { + #ifdef TEST + *logofs << "handleRead: Skipping error on shmem operation for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + continue; + } + } + + // + // Check if user pressed the CTRL+ALT+SHIFT+ESC key + // sequence because was unable to kill the session + // through the normal procedure. + // + + if (inputOpcode == KeyPress) + { + if (checkKeyboardEvent(inputOpcode, inputSequence, inputMessage) == 1) + { + #ifdef TEST + *logofs << "handleRead: Removing the key sequence from the " + << "event stream for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + continue; + } + } + + // + // We are going to handle an event or error + // that's not a mouse motion. Prepend any + // saved motion to it. + // + + if (lastMotion_[0] != '\0') + { + if (handleMotion(encodeBuffer) < 0) + { + #ifdef PANIC + *logofs << "handleRead: PANIC! Can't encode motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't encode motion event for FD#" + << fd_ << ".\n"; + + return -1; + } + } + + // + // Encode opcode and difference between + // current sequence and the last one. + // + + encodeBuffer.encodeOpcodeValue(inputOpcode, serverCache_ -> opcodeCache); + + unsigned int sequenceDiff = inputSequence - serverSequence_; + + serverSequence_ = inputSequence; + + #ifdef DEBUG + *logofs << "handleRead: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sequenceDiff, 16, + serverCache_ -> eventSequenceCache, 7); + + // + // If differential compression is disabled + // then use the most simple encoding. + // + + if (control -> LocalDeltaCompression == 0) + { + int result = handleFastReadEvent(encodeBuffer, inputOpcode, + inputMessage, inputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + switch (inputOpcode) + { + case X_Error: + { + // + // Set the priority flag in the case of + // a X protocol error. This may restart + // the client if it was waiting for the + // reply. + // + + priority_++; + + unsigned char errorCode = *(inputMessage + 1); + + encodeBuffer.encodeCachedValue(errorCode, 8, + serverCache_ -> errorCodeCache); + + if (errorCode != 11 && errorCode != 8 && + errorCode != 15 && errorCode != 1) + { + encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 16); + } + + if (errorCode >= 18) + { + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, + serverCache_ -> errorMinorCache); + } + + encodeBuffer.encodeCachedValue(inputMessage[10], 8, + serverCache_ -> errorMajorCache); + + if (errorCode >= 18) + { + const unsigned char *nextSrc = inputMessage + 11; + for (unsigned int i = 11; i < 32; i++) + encodeBuffer.encodeValue(*nextSrc++, 8); + } + } + break; + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + case MotionNotify: + case EnterNotify: + case LeaveNotify: + { + // + // Set the priority in the case this is + // an event that the remote side may + // care to receive as soon as possible. + // + + switch (inputOpcode) + { + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + { + priority_++; + } + } + + unsigned char detail = inputMessage[1]; + if (*inputMessage == MotionNotify) + encodeBuffer.encodeBoolValue((unsigned int) detail); + else if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) detail, 3); + else if (*inputMessage == KeyRelease) + { + if (detail == serverCache_ -> keyPressLastKey) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue((unsigned int) detail, 8); + } + } + else if ((*inputMessage == ButtonPress) || (*inputMessage == ButtonRelease)) + encodeBuffer.encodeCachedValue(detail, 8, + serverCache_ -> buttonCache); + else + encodeBuffer.encodeValue((unsigned int) detail, 8); + unsigned int timestamp = GetULONG(inputMessage + 4, bigEndian_); + unsigned int timestampDiff = + timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = timestamp; + encodeBuffer.encodeCachedValue(timestampDiff, 32, + serverCache_ -> motionNotifyTimestampCache, 9); + int skipRest = 0; + if (*inputMessage == KeyRelease) + { + skipRest = 1; + for (unsigned int i = 8; i < 31; i++) + { + if (inputMessage[i] != serverCache_ -> keyPressCache[i - 8]) + { + skipRest = 0; + break; + } + } + encodeBuffer.encodeBoolValue(skipRest); + } + + if (!skipRest) + { + const unsigned char *nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, + *serverCache_ -> motionNotifyWindowCache[i], 6); + nextSrc += 4; + } + unsigned int rootX = GetUINT(inputMessage + 20, bigEndian_); + unsigned int rootY = GetUINT(inputMessage + 22, bigEndian_); + unsigned int eventX = GetUINT(inputMessage + 24, bigEndian_); + unsigned int eventY = GetUINT(inputMessage + 26, bigEndian_); + eventX -= rootX; + eventY -= rootY; + encodeBuffer.encodeCachedValue(rootX - + serverCache_ -> motionNotifyLastRootX, 16, + serverCache_ -> motionNotifyRootXCache, 6); + serverCache_ -> motionNotifyLastRootX = rootX; + encodeBuffer.encodeCachedValue(rootY - + serverCache_ -> motionNotifyLastRootY, 16, + serverCache_ -> motionNotifyRootYCache, 6); + serverCache_ -> motionNotifyLastRootY = rootY; + encodeBuffer.encodeCachedValue(eventX, 16, + serverCache_ -> motionNotifyEventXCache, 6); + encodeBuffer.encodeCachedValue(eventY, 16, + serverCache_ -> motionNotifyEventYCache, 6); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 28, bigEndian_), + 16, serverCache_ -> motionNotifyStateCache); + if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) inputMessage[30], 2); + else + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[30]); + if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) inputMessage[31], 2); + else if (*inputMessage == KeyPress) + { + serverCache_ -> keyPressLastKey = detail; + for (unsigned int i = 8; i < 31; i++) + { + serverCache_ -> keyPressCache[i - 8] = inputMessage[i]; + } + } + } + } + break; + case ColormapNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> colormapNotifyWindowCache, 8); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> colormapNotifyColormapCache, 8); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]); + } + break; + case ConfigureNotify: + { + const unsigned char *nextSrc = inputMessage + 4; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, + *serverCache_ -> configureNotifyWindowCache[i], 9); + nextSrc += 4; + } + for (unsigned int j = 0; j < 5; j++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *serverCache_ -> configureNotifyGeomCache[j], 8); + nextSrc += 2; + } + encodeBuffer.encodeBoolValue(*nextSrc); + } + break; + case CreateNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> createNotifyWindowCache, 9); + unsigned int window = GetULONG(inputMessage + 8, bigEndian_); + encodeBuffer.encodeValue(window - + serverCache_ -> createNotifyLastWindow, 29, 5); + serverCache_ -> createNotifyLastWindow = window; + const unsigned char* nextSrc = inputMessage + 12; + for (unsigned int i = 0; i < 5; i++) + { + encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16, 9); + nextSrc += 2; + } + encodeBuffer.encodeBoolValue(*nextSrc); + } + break; + case Expose: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29, + serverCache_ -> exposeWindowCache, 9); + const unsigned char *nextSrc = inputMessage + 8; + for (unsigned int i = 0; i < 5; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16, + *serverCache_ -> exposeGeomCache[i], 6); + nextSrc += 2; + } + } + break; + case FocusIn: + case FocusOut: + { + encodeBuffer.encodeValue((unsigned int) inputMessage[1], 3); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> focusInWindowCache, 9); + encodeBuffer.encodeValue((unsigned int) inputMessage[8], 2); + } + break; + case KeymapNotify: + { + if (ServerCache::lastKeymap.compare(31, inputMessage + 1)) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + const unsigned char *nextSrc = inputMessage + 1; + for (unsigned int i = 1; i < 32; i++) + encodeBuffer.encodeValue((unsigned int) *nextSrc++, 8); + } + } + break; + case MapNotify: + case UnmapNotify: + case DestroyNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> mapNotifyEventCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> mapNotifyWindowCache, 9); + if ((*inputMessage == MapNotify) || (*inputMessage == UnmapNotify)) + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]); + } + break; + case NoExpose: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> noExposeDrawableCache, 9); + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16, + serverCache_ -> noExposeMinorCache); + encodeBuffer.encodeCachedValue(inputMessage[10], 8, + serverCache_ -> noExposeMajorCache); + } + break; + case PropertyNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> propertyNotifyWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> propertyNotifyAtomCache, 9); + unsigned int timestamp = GetULONG(inputMessage + 12, bigEndian_); + unsigned int timestampDiff = + timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = timestamp; + encodeBuffer.encodeValue(timestampDiff, 32, 9); + encodeBuffer.encodeBoolValue((unsigned int) inputMessage[16]); + } + break; + case ReparentNotify: + { + const unsigned char* nextSrc = inputMessage + 4; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), + 29, serverCache_ -> reparentNotifyWindowCache, 9); + nextSrc += 4; + } + encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16, 6); + encodeBuffer.encodeValue(GetUINT(nextSrc + 2, bigEndian_), 16, 6); + encodeBuffer.encodeBoolValue((unsigned int)inputMessage[20]); + } + break; + case SelectionClear: + { + unsigned int timestamp = GetULONG(inputMessage + 4, bigEndian_); + unsigned int timestampDiff = timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = timestamp; + encodeBuffer.encodeValue(timestampDiff, 32, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> selectionClearWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + } + break; + case SelectionRequest: + { + unsigned int timestamp = GetULONG(inputMessage + 4, bigEndian_); + unsigned int timestampDiff = timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = timestamp; + encodeBuffer.encodeValue(timestampDiff, 32, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), + 29, serverCache_ -> selectionClearWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), + 29, serverCache_ -> selectionClearWindowCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 20, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 24, bigEndian_), + 29, serverCache_ -> selectionClearAtomCache, 9); + } + break; + case VisibilityNotify: + { + encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), + 29, serverCache_ -> visibilityNotifyWindowCache, 9); + encodeBuffer.encodeValue((unsigned int) inputMessage[8], 2); + } + break; + default: + { + #ifdef TEST + *logofs << "handleRead: Using generic event compression " + << "for OPCODE#" << (unsigned int) inputOpcode + << ".\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8, + serverCache_ -> genericEventCharCache); + + for (unsigned int i = 0; i < 14; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(inputMessage + i * 2 + 4, bigEndian_), + 16, *serverCache_ -> genericEventIntCache[i]); + } + } + + } // switch (inputOpcode)... + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + if (*inputMessage == X_Error) + { + unsigned char code = *(inputMessage + 1); + + *logofs << "handleRead: Handled error ERR_CODE#" + << (unsigned int) code << " for FD#" << fd_; + + *logofs << " RES_ID#" << GetULONG(inputMessage + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(inputMessage + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(inputMessage + 10); + + *logofs << " sequence " << inputSequence << ". " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + else + { + *logofs << "handleRead: Handled event OPCODE#" + << (unsigned int) *inputMessage << " for FD#" << fd_ + << " sequence " << inputSequence << ". " << inputLength + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + + #endif + + statistics -> addEventBits(*inputMessage, inputLength << 3, bits); + + } // End of if (inputMessage[0] == X_Reply) ... else ... + + } // End of if (firstReply_) ... else ... + + } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != 0) ... + + // + // Check if we need to flush because of + // prioritized data. + // + + if (priority_ > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: WARNING! Requesting flush " + << "because of " << priority_ << " prioritized " + << "messages for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncPriority() < 0) + { + return -1; + } + + // + // Reset the priority flag. + // + + priority_ = 0; + } + + // + // Flush if we produced enough data. + // + + if (proxy -> canAsyncFlush() == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleRead: WARNING! Requesting flush " + << "because of token length exceeded.\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + } + + #if defined(TEST) || defined(INFO) + + if (transport_ -> pending() != 0 || + readBuffer_.checkMessage() != 0) + { + *logofs << "handleRead: PANIC! Buffer for X descriptor FD#" + << fd_ << " has " << transport_ -> pending() + << " bytes to read.\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // Reset the read buffer. + // + + readBuffer_.fullReset(); + + return 1; +} + +// +// End of handleRead(). +// + +// +// Beginning of handleWrite(). +// + +int ServerChannel::handleWrite(const unsigned char *message, unsigned int length) +{ + #ifdef TEST + *logofs << "handleWrite: Called for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Create the buffer from which to + // decode messages. + // + + DecodeBuffer decodeBuffer(message, length); + + #if defined(TEST) || defined(INFO) || defined(FLUSH) + *logofs << "handleWrite: Decoding messages for FD#" << fd_ + << " with " << length << " bytes in the buffer.\n" + << logofs_flush; + #endif + + if (firstRequest_) + { + // + // Need to add the length of the first request + // because it was not present in the previous + // versions. Length of the first request was + // assumed to be the same as the encode buffer + // but this may be not the case if a different + // encoding is used. + // + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeValue(length, 8); + } + + unsigned int nextByte; + unsigned char *outputMessage = writeBuffer_.addMessage(length); + unsigned char *nextDest = outputMessage; + + for (unsigned int i = 0; i < length; i++) + { + decodeBuffer.decodeValue(nextByte, 8); + + *nextDest++ = (unsigned char) nextByte; + } + + if (*outputMessage == 0x42) + { + setBigEndian(1); + } + else + { + setBigEndian(0); + } + + #ifdef TEST + *logofs << "handleWrite: First request detected.\n" << logofs_flush; + #endif + + // + // Handle the fake authorization cookie. + // + + if (handleAuthorization(outputMessage) < 0) + { + return -1; + } + + firstRequest_ = 0; + + } // End of if (firstRequest_) + + // + // This was previously in a 'else' block. + // Due to the way the first request was + // handled, we could not decode multiple + // messages in the first frame. + // + + { // Start of the decoding block. + + unsigned char outputOpcode; + + unsigned char *outputMessage; + unsigned int outputLength; + + // + // Set when message is found in cache. + // + + int hit; + + while (decodeBuffer.decodeOpcodeValue(outputOpcode, clientCache_ -> opcodeCache, 1)) + { + hit = 0; + + // + // Splits are sent by client proxy outside the + // normal read loop. As we 'insert' splits in + // the real client-server X protocol, we must + // avoid to increment the sequence number or + // our clients would get confused. + // + + if (outputOpcode != opcodeStore_ -> splitData) + { + clientSequence_++; + clientSequence_ &= 0xffff; + + #ifdef DEBUG + *logofs << "handleWrite: Last client sequence number for FD#" + << fd_ << " is " << clientSequence_ << ".\n" + << logofs_flush; + #endif + } + else + { + // + // It's a split, not a normal + // burst of proxy data. + // + + handleSplit(decodeBuffer); + + continue; + } + + #ifdef SUSPEND + + if (clientSequence_ == 1000) + { + cerr << "Warning" << ": Exiting to test the resilience of the agent.\n"; + + sleep(2); + + HandleAbort(); + } + + #endif + + // + // Is differential encoding disabled? + // + + if (control -> RemoteDeltaCompression == 0) + { + int result = handleFastWriteRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + if (result < 0) + { + return -1; + } + else if (result > 0) + { + continue; + } + } + + // + // General-purpose temp variables for + // decoding ints and chars. + // + + unsigned int value; + unsigned char cValue; + + #ifdef DEBUG + *logofs << "handleWrite: Going to handle request OPCODE#" + << (unsigned int) outputOpcode << " (" << DumpOpcode(outputOpcode) + << ") for FD#" << fd_ << " sequence " << clientSequence_ + << ".\n" << logofs_flush; + #endif + + switch (outputOpcode) + { + case X_AllocColor: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + unsigned int colorData[3]; + + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *(clientCache_ -> allocColorRGBCache[i]), 4); + PutUINT(value, nextDest, bigEndian_); + colorData[i] = value; + nextDest += 2; + } + + sequenceQueue_.push(clientSequence_, outputOpcode, + colorData[0], colorData[1], colorData[2]); + } + break; + case X_ReparentWindow: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 16, 11); + PutUINT(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeValue(value, 16, 11); + PutUINT(value, outputMessage + 14, bigEndian_); + } + break; + case X_ChangeProperty: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ChangeProperty); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned char format; + decodeBuffer.decodeCachedValue(format, 8, + clientCache_ -> changePropertyFormatCache); + unsigned int dataLength; + decodeBuffer.decodeValue(dataLength, 32, 6); + outputLength = 24 + RoundUp4(dataLength * (format >> 3)); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> changePropertyPropertyCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> changePropertyTypeCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + outputMessage[16] = format; + PutULONG(dataLength, outputMessage + 20, bigEndian_); + unsigned char *nextDest = outputMessage + 24; + + if (format == 8) + { + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, dataLength); + } + else + { + clientCache_ -> changePropertyTextCompressor.reset(); + for (unsigned int i = 0; i < dataLength; i++) + { + *nextDest++ = clientCache_ -> changePropertyTextCompressor. + decodeChar(decodeBuffer); + } + } + } + else if (format == 32) + { + for (unsigned int i = 0; i < dataLength; i++) + { + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> changePropertyData32Cache); + + PutULONG(value, nextDest, bigEndian_); + + nextDest += 4; + } + } + else + { + for (unsigned int i = 0; i < dataLength; i++) + { + decodeBuffer.decodeValue(value, 16); + + PutUINT(value, nextDest, bigEndian_); + + nextDest += 2; + } + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_SendEvent: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_SendEvent); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 44; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + *(outputMessage + 1) = value; + decodeBuffer.decodeBoolValue(value); + if (value) + { + decodeBuffer.decodeBoolValue(value); + } + else + { + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + } + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> sendEventMaskCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(*(outputMessage + 12), 8, + clientCache_ -> sendEventCodeCache); + decodeBuffer.decodeCachedValue(*(outputMessage + 13), 8, + clientCache_ -> sendEventByteDataCache); + decodeBuffer.decodeValue(value, 16, 4); + clientCache_ -> sendEventLastSequence += value; + clientCache_ -> sendEventLastSequence &= 0xffff; + PutUINT(clientCache_ -> sendEventLastSequence, outputMessage + 14, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> sendEventIntDataCache); + PutULONG(value, outputMessage + 16, bigEndian_); + + for (unsigned int i = 20; i < 44; i++) + { + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> sendEventEventCache); + *(outputMessage + i) = cValue; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ChangeWindowAttributes: + { + unsigned int numAttrs; + decodeBuffer.decodeValue(numAttrs, 4); + outputLength = 12 + (numAttrs << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 15, + clientCache_ -> createWindowBitmaskCache); + PutULONG(bitmask, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 15; i++) + { + if (bitmask & mask) + { + decodeBuffer.decodeCachedValue(value, 32, + *clientCache_ -> createWindowAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + mask <<= 1; + } + } + break; + case X_ClearArea: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ClearArea); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + for (unsigned int i = 0; i < 4; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> clearAreaGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_CloseFont: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 29, 5); + clientCache_ -> lastFont += value; + clientCache_ -> lastFont &= 0x1fffffff; + PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_); + } + break; + case X_ConfigureWindow: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ConfigureWindow); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 7, + clientCache_ -> configureWindowBitmaskCache); + PutUINT(bitmask, outputMessage + 8, bigEndian_); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 7; i++) + { + if (bitmask & mask) + { + unsigned char* nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + decodeBuffer.decodeCachedValue(value, CONFIGUREWINDOW_FIELD_WIDTH[i], + *clientCache_ -> configureWindowAttrCache[i], 8); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ConvertSelection: + { + outputLength = 24; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> convertSelectionRequestorCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char* nextDest = outputMessage + 8; + for (unsigned int i = 0; i < 3; i++) + { + decodeBuffer.decodeCachedValue(value, 29, + *(clientCache_ -> convertSelectionAtomCache[i]), 9); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + } + decodeBuffer.decodeValue(value, 32, 4); + clientCache_ -> convertSelectionLastTimestamp += value; + PutULONG(clientCache_ -> convertSelectionLastTimestamp, + nextDest, bigEndian_); + } + break; + case X_CopyArea: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CopyArea); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 28; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 12, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + for (unsigned int i = 0; i < 6; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> copyAreaGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_CopyGC: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 23, + clientCache_ -> createGCBitmaskCache); + PutULONG(value, outputMessage + 12, bigEndian_); + } + break; + case X_CopyPlane: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 12, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + for (unsigned int i = 0; i < 6; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> copyPlaneGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> copyPlaneBitPlaneCache, 10); + PutULONG(value, outputMessage + 28, bigEndian_); + } + break; + case X_CreateGC: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CreateGC); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId, + clientCache_ -> lastIdCache, clientCache_ -> gcCache, + clientCache_ -> freeGCCache); + } + else + { + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + } + + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int offset = 8; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + offset, bigEndian_); + offset += 4; + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 23, + clientCache_ -> createGCBitmaskCache); + PutULONG(bitmask, outputMessage + offset, bigEndian_); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 23; i++) + { + if (bitmask & mask) + { + unsigned char* nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i]; + if (fieldWidth <= 4) + decodeBuffer.decodeValue(value, fieldWidth); + else + decodeBuffer.decodeCachedValue(value, fieldWidth, + *clientCache_ -> createGCAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ChangeGC: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ChangeGC); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int offset = 8; + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 23, + clientCache_ -> createGCBitmaskCache); + PutULONG(bitmask, outputMessage + offset, bigEndian_); + unsigned int mask = 0x1; + for (unsigned int i = 0; i < 23; i++) + { + if (bitmask & mask) + { + unsigned char* nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i]; + if (fieldWidth <= 4) + decodeBuffer.decodeValue(value, fieldWidth); + else + decodeBuffer.decodeCachedValue(value, fieldWidth, + *clientCache_ -> createGCAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_CreatePixmap: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_CreatePixmap); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + break; + case X_CreateWindow: + { + outputLength = 32; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeCachedValue(cValue, 8, clientCache_ -> depthCache); + outputMessage[1] = cValue; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId, + clientCache_ -> lastIdCache, clientCache_ -> windowCache, + clientCache_ -> freeWindowCache); + } + else + { + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + } + + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + unsigned int i; + for (i = 0; i < 6; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> createWindowGeomCache[i], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> visualCache); + PutULONG(value, outputMessage + 24, bigEndian_); + unsigned int bitmask; + decodeBuffer.decodeCachedValue(bitmask, 15, + clientCache_ -> createWindowBitmaskCache); + PutULONG(bitmask, outputMessage + 28, bigEndian_); + unsigned int mask = 0x1; + for (i = 0; i < 15; i++) + { + if (bitmask & mask) + { + nextDest = writeBuffer_.addMessage(4); + outputLength += 4; + decodeBuffer.decodeCachedValue(value, 32, + *clientCache_ -> createWindowAttrCache[i]); + PutULONG(value, nextDest, bigEndian_); + } + mask <<= 1; + } + writeBuffer_.unregisterPointer(); + } + break; + case X_DeleteProperty: + { + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 29, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + } + break; + case X_FillPoly: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_FillPoly); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numPoints; + + if (control -> isProtoStep10() == 1) + { + decodeBuffer.decodeCachedValue(numPoints, 16, + clientCache_ -> fillPolyNumPointsCache, 4); + } + else + { + decodeBuffer.decodeCachedValue(numPoints, 14, + clientCache_ -> fillPolyNumPointsCache, 4); + } + + outputLength = 16 + (numPoints << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 2); + outputMessage[12] = (unsigned char) value; + unsigned int relativeCoordMode; + decodeBuffer.decodeBoolValue(relativeCoordMode); + outputMessage[13] = (unsigned char) relativeCoordMode; + unsigned char *nextDest = outputMessage + 16; + unsigned int pointIndex = 0; + for (unsigned int i = 0; i < numPoints; i++) + { + if (relativeCoordMode) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> fillPolyXRelCache[pointIndex], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> fillPolyYRelCache[pointIndex], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + else + { + unsigned int x, y; + decodeBuffer.decodeBoolValue(value); + if (value) + { + decodeBuffer.decodeValue(value, 3); + x = clientCache_ -> fillPolyRecentX[value]; + y = clientCache_ -> fillPolyRecentY[value]; + } + else + { + decodeBuffer.decodeCachedValue(x, 16, + *clientCache_ -> fillPolyXAbsCache[pointIndex], 8); + decodeBuffer.decodeCachedValue(y, 16, + *clientCache_ -> fillPolyYAbsCache[pointIndex], 8); + clientCache_ -> fillPolyRecentX[clientCache_ -> fillPolyIndex] = x; + clientCache_ -> fillPolyRecentY[clientCache_ -> fillPolyIndex] = y; + clientCache_ -> fillPolyIndex++; + if (clientCache_ -> fillPolyIndex == 8) + clientCache_ -> fillPolyIndex = 0; + } + PutUINT(x, nextDest, bigEndian_); + nextDest += 2; + PutUINT(y, nextDest, bigEndian_); + nextDest += 2; + } + + if (++pointIndex == 10) pointIndex = 0; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_FreeColors: + { + unsigned int numPixels; + decodeBuffer.decodeValue(numPixels, 16, 4); + outputLength = 12 + (numPixels << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 32, 4); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char* nextDest = outputMessage + 12; + while (numPixels) + { + decodeBuffer.decodeValue(value, 32, 8); + PutULONG(value, nextDest, bigEndian_); + nextDest += 4; + numPixels--; + } + } + break; + case X_FreeCursor: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> cursorCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + } + break; + case X_FreeGC: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeGCCache); + } + else + { + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + } + + PutULONG(value, outputMessage + 4, bigEndian_); + } + break; + case X_FreePixmap: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeDrawableCache); + + PutULONG(value, outputMessage + 4, bigEndian_); + } + else + { + decodeBuffer.decodeBoolValue(value); + if (!value) + { + decodeBuffer.decodeValue(value, 29, 4); + clientCache_ -> createPixmapLastId += value; + clientCache_ -> createPixmapLastId &= 0x1fffffff; + } + PutULONG(clientCache_ -> createPixmapLastId, outputMessage + 4, bigEndian_); + } + } + break; + case X_GetAtomName: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 29, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetGeometry: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetInputFocus: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode, outputOpcode); + } + break; + case X_GetModifierMapping: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetKeyboardMapping: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 8); + outputMessage[4] = value; + decodeBuffer.decodeValue(value, 8); + outputMessage[5] = value; + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetProperty: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_GetProperty); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + // + // Save a reference to identify the reply. + // + + unsigned int property = GetULONG(outputMessage + 8, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode, property); + + break; + } + + outputLength = 24; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned int property; + decodeBuffer.decodeValue(property, 29, 9); + PutULONG(property, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 29, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeValue(value, 32, 2); + PutULONG(value, outputMessage + 16, bigEndian_); + decodeBuffer.decodeValue(value, 32, 8); + PutULONG(value, outputMessage + 20, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode, property); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_GetSelectionOwner: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> getSelectionOwnerSelectionCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GrabButton: + case X_GrabPointer: + { + outputLength = 24; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> grabButtonEventMaskCache); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[10] = (unsigned char) value; + decodeBuffer.decodeBoolValue(value); + outputMessage[11] = (unsigned char) value; + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> grabButtonConfineCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> cursorCache, 9); + PutULONG(value, outputMessage + 16, bigEndian_); + if (outputOpcode == X_GrabButton) + { + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> grabButtonButtonCache); + outputMessage[20] = cValue; + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> grabButtonModifierCache); + PutUINT(value, outputMessage + 22, bigEndian_); + } + else + { + decodeBuffer.decodeValue(value, 32, 4); + clientCache_ -> grabKeyboardLastTimestamp += value; + PutULONG(clientCache_ -> grabKeyboardLastTimestamp, + outputMessage + 20, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + } + break; + case X_GrabKeyboard: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 32, 4); + clientCache_ -> grabKeyboardLastTimestamp += value; + PutULONG(clientCache_ -> grabKeyboardLastTimestamp, outputMessage + 8, + bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[12] = (unsigned char) value; + decodeBuffer.decodeBoolValue(value); + outputMessage[13] = (unsigned char) value; + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GrabServer: + case X_UngrabServer: + case X_NoOperation: + { + #ifdef DEBUG + *logofs << "handleWrite: Managing (probably tainted) X_NoOperation request for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + } + break; + case X_PolyText8: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyText8); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheX); + clientCache_ -> polyTextLastX += value; + clientCache_ -> polyTextLastX &= 0xffff; + PutUINT(clientCache_ -> polyTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheY); + clientCache_ -> polyTextLastY += value; + clientCache_ -> polyTextLastY &= 0xffff; + PutUINT(clientCache_ -> polyTextLastY, outputMessage + 14, bigEndian_); + unsigned int addedLength = 0; + writeBuffer_.registerPointer(&outputMessage); + for (;;) + { + decodeBuffer.decodeBoolValue(value); + if (!value) + break; + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 8); + if (textLength == 255) + { + addedLength += 5; + unsigned char *nextSegment = writeBuffer_.addMessage(5); + *nextSegment = (unsigned char) textLength; + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> polyTextFontCache); + PutULONG(value, nextSegment + 1, 1); + } + else + { + addedLength += (textLength + 2); + unsigned char *nextSegment = + writeBuffer_.addMessage(textLength + 2); + *nextSegment = (unsigned char) textLength; + unsigned char *nextDest = nextSegment + 1; + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> polyTextDeltaCache); + *nextDest++ = cValue; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, textLength); + + nextDest += textLength; + } + else + { + clientCache_ -> polyTextTextCompressor.reset(); + while (textLength) + { + *nextDest++ = clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer); + textLength--; + } + } + } + } + outputLength += addedLength; + unsigned int mod4 = (addedLength & 0x3); + if (mod4) + { + unsigned int extra = 4 - mod4; + unsigned char *nextDest = writeBuffer_.addMessage(extra); + for (unsigned int i = 0; i < extra; i++) + *nextDest++ = 0; + outputLength += extra; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyText16: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyText16); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheX); + clientCache_ -> polyTextLastX += value; + clientCache_ -> polyTextLastX &= 0xffff; + PutUINT(clientCache_ -> polyTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> polyTextCacheY); + clientCache_ -> polyTextLastY += value; + clientCache_ -> polyTextLastY &= 0xffff; + PutUINT(clientCache_ -> polyTextLastY, outputMessage + 14, bigEndian_); + unsigned int addedLength = 0; + writeBuffer_.registerPointer(&outputMessage); + for (;;) + { + decodeBuffer.decodeBoolValue(value); + if (!value) + break; + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 8); + if (textLength == 255) + { + addedLength += 5; + unsigned char *nextSegment = writeBuffer_.addMessage(5); + *nextSegment = (unsigned char) textLength; + decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> polyTextFontCache); + PutULONG(value, nextSegment + 1, 1); + } + else + { + addedLength += (textLength * 2 + 2); + unsigned char *nextSegment = + writeBuffer_.addMessage(textLength * 2 + 2); + *nextSegment = (unsigned char) textLength; + unsigned char *nextDest = nextSegment + 1; + decodeBuffer.decodeCachedValue(cValue, 8, clientCache_ -> polyTextDeltaCache); + *nextDest++ = cValue; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, textLength * 2); + + nextDest += textLength * 2; + } + else + { + clientCache_ -> polyTextTextCompressor.reset(); + textLength <<= 1; + while (textLength) + { + *nextDest++ = + clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer); + textLength--; + } + } + } + } + outputLength += addedLength; + + unsigned int mod4 = (addedLength & 0x3); + if (mod4) + { + unsigned int extra = 4 - mod4; + unsigned char *nextDest = writeBuffer_.addMessage(extra); + for (unsigned int i = 0; i < extra; i++) + *nextDest++ = 0; + outputLength += extra; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ImageText8: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ImageText8); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int textLength; + decodeBuffer.decodeCachedValue(textLength, 8, + clientCache_ -> imageTextLengthCache, 4); + outputLength = 16 + RoundUp4(textLength); + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) textLength; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheX); + clientCache_ -> imageTextLastX += value; + clientCache_ -> imageTextLastX &= 0xffff; + PutUINT(clientCache_ -> imageTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheY); + clientCache_ -> imageTextLastY += value; + clientCache_ -> imageTextLastY &= 0xffff; + PutUINT(clientCache_ -> imageTextLastY, outputMessage + 14, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, textLength); + } + else + { + clientCache_ -> imageTextTextCompressor.reset(); + for (unsigned int j = 0; j < textLength; j++) + *nextDest++ = clientCache_ -> imageTextTextCompressor.decodeChar(decodeBuffer); + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ImageText16: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_ImageText16); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int textLength; + decodeBuffer.decodeCachedValue(textLength, 8, + clientCache_ -> imageTextLengthCache, 4); + outputLength = 16 + RoundUp4(textLength * 2); + outputMessage = writeBuffer_.addMessage(outputLength); + outputMessage[1] = (unsigned char) textLength; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheX); + clientCache_ -> imageTextLastX += value; + clientCache_ -> imageTextLastX &= 0xffff; + PutUINT(clientCache_ -> imageTextLastX, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> imageTextCacheY); + clientCache_ -> imageTextLastY += value; + clientCache_ -> imageTextLastY &= 0xffff; + PutUINT(clientCache_ -> imageTextLastY, outputMessage + 14, bigEndian_); + unsigned char *nextDest = outputMessage + 16; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, textLength * 2); + } + else + { + clientCache_ -> imageTextTextCompressor.reset(); + for (unsigned int j = 0; j < textLength * 2; j++) + *nextDest++ = clientCache_ -> imageTextTextCompressor.decodeChar(decodeBuffer); + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_InternAtom: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_InternAtom); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + + break; + } + + unsigned int nameLength; + decodeBuffer.decodeValue(nameLength, 16, 6); + outputLength = RoundUp4(nameLength) + 8; + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(nameLength, outputMessage + 4, bigEndian_); + decodeBuffer.decodeBoolValue(value); + outputMessage[1] = (unsigned char) value; + unsigned char *nextDest = outputMessage + 8; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, nameLength); + } + else + { + clientCache_ -> internAtomTextCompressor.reset(); + for (unsigned int i = 0; i < nameLength; i++) + { + *nextDest++ = clientCache_ -> internAtomTextCompressor.decodeChar(decodeBuffer); + } + } + + sequenceQueue_.push(clientSequence_, outputOpcode); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_ListExtensions: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_ListFonts: + { + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 16, 6); + outputLength = 8 + RoundUp4(textLength); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(textLength, outputMessage + 6, bigEndian_); + decodeBuffer.decodeValue(value, 16, 6); + PutUINT(value, outputMessage + 4, bigEndian_); + unsigned char* nextDest = outputMessage + 8; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, textLength); + } + else + { + clientCache_ -> polyTextTextCompressor.reset(); + for (unsigned int i = 0; i < textLength; i++) + { + *nextDest++ = clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer); + } + } + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_LookupColor: + case X_AllocNamedColor: + { + unsigned int textLength; + decodeBuffer.decodeValue(textLength, 16, 6); + outputLength = 12 + RoundUp4(textLength); + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + PutUINT(textLength, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, textLength); + } + else + { + clientCache_ -> polyTextTextCompressor.reset(); + for (unsigned int i = 0; i < textLength; i++) + { + *nextDest++ = clientCache_ -> polyTextTextCompressor.decodeChar(decodeBuffer); + } + } + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_MapWindow: + case X_UnmapWindow: + case X_MapSubwindows: + case X_GetWindowAttributes: + case X_DestroyWindow: + case X_DestroySubwindows: + case X_QueryPointer: + case X_QueryTree: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + + if (outputOpcode == X_DestroyWindow && control -> isProtoStep7() == 1) + { + decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeWindowCache); + } + else + { + decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache); + } + + PutULONG(value, outputMessage + 4, bigEndian_); + if (outputOpcode == X_QueryPointer || + outputOpcode == X_GetWindowAttributes || + outputOpcode == X_QueryTree) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + } + } + break; + case X_OpenFont: + { + unsigned int nameLength; + decodeBuffer.decodeValue(nameLength, 16, 7); + outputLength = RoundUp4(12 + nameLength); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(nameLength, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 29, 5); + clientCache_ -> lastFont += value; + clientCache_ -> lastFont &= 0x1fffffff; + PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeTextData(nextDest, nameLength); + } + else + { + clientCache_ -> openFontTextCompressor.reset(); + for (; nameLength; nameLength--) + { + *nextDest++ = clientCache_ -> openFontTextCompressor. + decodeChar(decodeBuffer); + } + } + } + break; + case X_PolyFillRectangle: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyFillRectangle); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0, lastWidth = 0, lastHeight = 0; + unsigned int numRectangles = 0; + + for (;;) + { + outputLength += 8; + writeBuffer_.addMessage(8); + unsigned char *nextDest = outputMessage + 12 + + (numRectangles << 3); + numRectangles++; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheX[index], 8); + value += lastX; + PutUINT(value, nextDest, bigEndian_); + lastX = value; + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheY[index], 8); + value += lastY; + PutUINT(value, nextDest, bigEndian_); + lastY = value; + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheWidth[index], 8); + value += lastWidth; + PutUINT(value, nextDest, bigEndian_); + lastWidth = value; + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillRectangleCacheHeight[index], 8); + value += lastHeight; + PutUINT(value, nextDest, bigEndian_); + lastHeight = value; + nextDest += 2; + + if (++index == 4) index = 0; + + decodeBuffer.decodeBoolValue(value); + + if (!value) break; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyFillArc: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyFillArc); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0, + lastWidth = 0, lastHeight = 0, + lastAngle1 = 0, lastAngle2 = 0; + + unsigned int numArcs = 0; + + for (;;) + { + outputLength += 12; + writeBuffer_.addMessage(12); + + unsigned char *nextDest = outputMessage + 12 + + (numArcs * 12); + numArcs++; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheX[index], 8); + value += lastX; + PutUINT(value, nextDest, bigEndian_); + lastX = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheY[index], 8); + value += lastY; + PutUINT(value, nextDest, bigEndian_); + lastY = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheWidth[index], 8); + value += lastWidth; + PutUINT(value, nextDest, bigEndian_); + lastWidth = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheHeight[index], 8); + value += lastHeight; + PutUINT(value, nextDest, bigEndian_); + lastHeight = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheAngle1[index], 8); + value += lastAngle1; + PutUINT(value, nextDest, bigEndian_); + lastAngle1 = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyFillArcCacheAngle2[index], 8); + value += lastAngle2; + PutUINT(value, nextDest, bigEndian_); + lastAngle2 = value; + nextDest += 2; + + if (++index == 2) index = 0; + + decodeBuffer.decodeBoolValue(value); + + if (!value) break; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyArc: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyArc); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + writeBuffer_.registerPointer(&outputMessage); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0, + lastWidth = 0, lastHeight = 0, + lastAngle1 = 0, lastAngle2 = 0; + + unsigned int numArcs = 0; + + for (;;) + { + outputLength += 12; + writeBuffer_.addMessage(12); + + unsigned char *nextDest = outputMessage + 12 + + (numArcs * 12); + numArcs++; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheX[index], 8); + value += lastX; + PutUINT(value, nextDest, bigEndian_); + lastX = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheY[index], 8); + value += lastY; + PutUINT(value, nextDest, bigEndian_); + lastY = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheWidth[index], 8); + value += lastWidth; + PutUINT(value, nextDest, bigEndian_); + lastWidth = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheHeight[index], 8); + value += lastHeight; + PutUINT(value, nextDest, bigEndian_); + lastHeight = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheAngle1[index], 8); + value += lastAngle1; + PutUINT(value, nextDest, bigEndian_); + lastAngle1 = value; + nextDest += 2; + + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyArcCacheAngle2[index], 8); + value += lastAngle2; + PutUINT(value, nextDest, bigEndian_); + lastAngle2 = value; + nextDest += 2; + + if (++index == 2) index = 0; + + decodeBuffer.decodeBoolValue(value); + + if (!value) break; + } + writeBuffer_.unregisterPointer(); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyPoint: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyPoint); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numPoints; + decodeBuffer.decodeValue(numPoints, 16, 4); + outputLength = (numPoints << 2) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + unsigned int relativeCoordMode; + decodeBuffer.decodeBoolValue(relativeCoordMode); + outputMessage[1] = (unsigned char) relativeCoordMode; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + + for (unsigned int i = 0; i < numPoints; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyPointCacheX[index], 8); + lastX += value; + PutUINT(lastX, nextDest, bigEndian_); + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyPointCacheY[index], 8); + lastY += value; + PutUINT(lastY, nextDest, bigEndian_); + nextDest += 2; + + if (++index == 2) index = 0; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyLine: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolyLine); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numPoints; + decodeBuffer.decodeValue(numPoints, 16, 4); + outputLength = (numPoints << 2) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + unsigned int relativeCoordMode; + decodeBuffer.decodeBoolValue(relativeCoordMode); + outputMessage[1] = (unsigned char) relativeCoordMode; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + unsigned int index = 0; + unsigned int lastX = 0, lastY = 0; + + for (unsigned int i = 0; i < numPoints; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyLineCacheX[index], 8); + lastX += value; + PutUINT(lastX, nextDest, bigEndian_); + nextDest += 2; + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyLineCacheY[index], 8); + lastY += value; + PutUINT(lastY, nextDest, bigEndian_); + nextDest += 2; + + if (++index == 2) index = 0; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PolyRectangle: + { + unsigned int numRectangles; + decodeBuffer.decodeValue(numRectangles, 16, 3); + outputLength = (numRectangles << 3) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + for (unsigned int i = 0; i < numRectangles; i++) + for (unsigned int k = 0; k < 4; k++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> polyRectangleGeomCache[k], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + } + break; + case X_PolySegment: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PolySegment); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numSegments; + decodeBuffer.decodeValue(numSegments, 16, 4); + outputLength = (numSegments << 3) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + + for (numSegments *= 2; numSegments; numSegments--) + { + unsigned int index; + decodeBuffer.decodeBoolValue(index); + unsigned int x; + decodeBuffer.decodeCachedValue(x, 16, + clientCache_ -> polySegmentCacheX, 6); + x += clientCache_ -> polySegmentLastX[index]; + PutUINT(x, nextDest, bigEndian_); + nextDest += 2; + + unsigned int y; + decodeBuffer.decodeCachedValue(y, 16, + clientCache_ -> polySegmentCacheY, 6); + y += clientCache_ -> polySegmentLastY[index]; + PutUINT(y, nextDest, bigEndian_); + nextDest += 2; + + clientCache_ -> polySegmentLastX[clientCache_ -> polySegmentCacheIndex] = x; + clientCache_ -> polySegmentLastY[clientCache_ -> polySegmentCacheIndex] = y; + + if (clientCache_ -> polySegmentCacheIndex == 1) + clientCache_ -> polySegmentCacheIndex = 0; + else + clientCache_ -> polySegmentCacheIndex = 1; + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_PutImage: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_PutImage); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + + if (outputOpcode == X_PutImage) + { + handleImage(outputOpcode, outputMessage, outputLength); + } + } + break; + case X_QueryBestSize: + { + outputLength = 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char)value; + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeValue(value, 16, 8); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeValue(value, 16, 8); + PutUINT(value, outputMessage + 10, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_QueryColors: + { + // Differential or plain data compression? + decodeBuffer.decodeBoolValue(value); + + if (value) + { + unsigned int numColors; + decodeBuffer.decodeValue(numColors, 16, 5); + outputLength = (numColors << 2) + 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> colormapCache); + PutULONG(value, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + unsigned int predictedPixel = clientCache_ -> queryColorsLastPixel; + for (unsigned int i = 0; i < numColors; i++) + { + unsigned int pixel; + decodeBuffer.decodeBoolValue(value); + if (value) + pixel = predictedPixel; + else + decodeBuffer.decodeValue(pixel, 32, 9); + PutULONG(pixel, nextDest, bigEndian_); + if (i == 0) + clientCache_ -> queryColorsLastPixel = pixel; + predictedPixel = pixel + 1; + nextDest += 4; + } + } + else + { + // Request length. + unsigned int requestLength; + decodeBuffer.decodeValue(requestLength, 16, 10); + outputLength = (requestLength << 2); + outputMessage = writeBuffer_.addMessage(outputLength); + + const unsigned char *compressedData = NULL; + unsigned int compressedDataSize = 0; + + int decompressed = handleDecompress(decodeBuffer, outputOpcode, 4, + outputMessage, outputLength, compressedData, + compressedDataSize); + if (decompressed < 0) + { + return -1; + } + } + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_QueryExtension: + { + unsigned int nameLength; + decodeBuffer.decodeValue(nameLength, 16, 6); + outputLength = 8 + RoundUp4(nameLength); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(nameLength, outputMessage + 4, bigEndian_); + unsigned char *nextDest = outputMessage + 8; + for (unsigned int i = 0; i < nameLength; i++) + { + decodeBuffer.decodeValue(value, 8); + *nextDest++ = (unsigned char) value; + } + + unsigned int hide = 0; + + #ifdef HIDE_MIT_SHM_EXTENSION + + if (!strncmp((char *) outputMessage + 8, "MIT-SHM", 7)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide MIT-SHM extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + #ifdef HIDE_BIG_REQUESTS_EXTENSION + + if (!strncmp((char *) outputMessage + 8, "BIG-REQUESTS", 12)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide BIG-REQUESTS extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + #ifdef HIDE_XKEYBOARD_EXTENSION + + else if (!strncmp((char *) outputMessage + 8, "XKEYBOARD", 9)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide XKEYBOARD extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + #ifdef HIDE_XFree86_Bigfont_EXTENSION + + else if (!strncmp((char *) outputMessage + 8, "XFree86-Bigfont", 15)) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide XFree86-Bigfont extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + // + // This is if you want to experiment disabling SHAPE extensions. + // + + #ifdef HIDE_SHAPE_EXTENSION + + if (!strncmp((char *) outputMessage + 8, "SHAPE", 5)) + { + #ifdef DEBUG + *logofs << "handleWrite: Going to hide SHAPE extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + #endif + + // + // Check if user disabled RENDER extension. + // + + if (control -> HideRender == 1 && + strncmp((char *) outputMessage + 8, "RENDER", 6) == 0) + { + #ifdef TEST + *logofs << "handleWrite: Going to hide RENDER extension in reply.\n" + << logofs_flush; + #endif + + hide = 1; + } + + unsigned int extension = 0; + + if (strncmp((char *) outputMessage + 8, "SHAPE", 5) == 0) + { + extension = X_NXInternalShapeExtension; + } + else if (strncmp((char *) outputMessage + 8, "RENDER", 6) == 0) + { + extension = X_NXInternalRenderExtension; + } + + sequenceQueue_.push(clientSequence_, outputOpcode, + outputOpcode, hide, extension); + } + break; + case X_QueryFont: + { + outputLength = 8; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 29, 5); + clientCache_ -> lastFont += value; + clientCache_ -> lastFont &= 0x1fffffff; + PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_SetClipRectangles: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_SetClipRectangles); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + break; + } + + unsigned int numRectangles; + + if (control -> isProtoStep9() == 1) + { + decodeBuffer.decodeValue(numRectangles, 15, 4); + } + else + { + decodeBuffer.decodeValue(numRectangles, 13, 4); + } + + outputLength = (numRectangles << 3) + 12; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeValue(value, 2); + outputMessage[1] = (unsigned char) value; + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> setClipRectanglesXCache, 8); + PutUINT(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> setClipRectanglesYCache, 8); + PutUINT(value, outputMessage + 10, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + for (unsigned int i = 0; i < numRectangles; i++) + { + for (unsigned int k = 0; k < 4; k++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache_ -> setClipRectanglesGeomCache[k], 8); + PutUINT(value, nextDest, bigEndian_); + nextDest += 2; + } + } + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_SetDashes: + { + unsigned int numDashes; + decodeBuffer.decodeCachedValue(numDashes, 16, + clientCache_ -> setDashesLengthCache, 5); + outputLength = 12 + RoundUp4(numDashes); + outputMessage = writeBuffer_.addMessage(outputLength); + PutUINT(numDashes, outputMessage + 10, bigEndian_); + decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> setDashesOffsetCache, 5); + PutUINT(value, outputMessage + 8, bigEndian_); + unsigned char *nextDest = outputMessage + 12; + for (unsigned int i = 0; i < numDashes; i++) + { + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> setDashesDashCache_[i & 1], 5); + *nextDest++ = cValue; + } + } + break; + case X_SetSelectionOwner: + { + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> setSelectionOwnerCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> getSelectionOwnerSelectionCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> setSelectionOwnerTimestampCache, 9); + PutULONG(value, outputMessage + 12, bigEndian_); + } + break; + case X_TranslateCoords: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_TranslateCoords); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + + break; + } + + outputLength = 16; + outputMessage = writeBuffer_.addMessage(outputLength); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> translateCoordsSrcCache, 9); + PutULONG(value, outputMessage + 4, bigEndian_); + decodeBuffer.decodeCachedValue(value, 29, + clientCache_ -> translateCoordsDstCache, 9); + PutULONG(value, outputMessage + 8, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> translateCoordsXCache, 8); + PutUINT(value, outputMessage + 12, bigEndian_); + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> translateCoordsYCache, 8); + PutUINT(value, outputMessage + 14, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_GetImage: + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_GetImage); + + if (handleDecodeCached(decodeBuffer, clientCache_, messageStore, + outputMessage, outputLength)) + { + sequenceQueue_.push(clientSequence_, outputOpcode); + + break; + } + + outputLength = 20; + outputMessage = writeBuffer_.addMessage(outputLength); + // Format. + unsigned int format; + decodeBuffer.decodeValue(format, 2); + outputMessage[1] = (unsigned char) format; + // Drawable. + decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache); + PutULONG(value, outputMessage + 4, bigEndian_); + // X. + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> putImageXCache, 8); + clientCache_ -> putImageLastX += value; + clientCache_ -> putImageLastX &= 0xffff; + PutUINT(clientCache_ -> putImageLastX, outputMessage + 8, bigEndian_); + // Y. + decodeBuffer.decodeCachedValue(value, 16, + clientCache_ -> putImageYCache, 8); + clientCache_ -> putImageLastY += value; + clientCache_ -> putImageLastY &= 0xffff; + PutUINT(clientCache_ -> putImageLastY, outputMessage + 10, bigEndian_); + // Width. + unsigned int width; + decodeBuffer.decodeCachedValue(width, 16, + clientCache_ -> putImageWidthCache, 8); + PutUINT(width, outputMessage + 12, bigEndian_); + // Height. + unsigned int height; + decodeBuffer.decodeCachedValue(height, 16, + clientCache_ -> putImageHeightCache, 8); + PutUINT(height, outputMessage + 14, bigEndian_); + // Plane mask. + decodeBuffer.decodeCachedValue(value, 32, + clientCache_ -> getImagePlaneMaskCache, 5); + PutULONG(value, outputMessage + 16, bigEndian_); + + sequenceQueue_.push(clientSequence_, outputOpcode); + + handleSave(messageStore, outputMessage, outputLength); + } + break; + case X_GetPointerMapping: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + case X_GetKeyboardControl: + { + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode); + } + break; + default: + { + if (outputOpcode == opcodeStore_ -> renderExtension) + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalRenderExtension); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> shapeExtension) + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalShapeExtension); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> putPackedImage) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding packed image request for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXPutPackedImage); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + + if (outputOpcode == opcodeStore_ -> putPackedImage) + { + handleImage(outputOpcode, outputMessage, outputLength); + } + } + else if (outputOpcode == opcodeStore_ -> setUnpackColormap) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding set unpack colormap request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackColormap); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + // + // Message could have been split. + // + + if (outputOpcode == opcodeStore_ -> setUnpackColormap) + { + handleColormap(outputOpcode, outputMessage, outputLength); + } + } + else if (outputOpcode == opcodeStore_ -> setUnpackAlpha) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding unpack alpha request for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackAlpha); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + // + // Message could have been split. + // + + if (outputOpcode == opcodeStore_ -> setUnpackAlpha) + { + handleAlpha(outputOpcode, outputMessage, outputLength); + } + } + else if (outputOpcode == opcodeStore_ -> setUnpackGeometry) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding set unpack geometry request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXSetUnpackGeometry); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + + handleGeometry(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> startSplit) + { + handleStartSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> endSplit) + { + handleEndSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> commitSplit) + { + int result = handleCommitSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + + // + // Check if message has been successfully + // extracted from the split store. In this + // case post-process it in the usual way. + // + + if (result > 0) + { + if (outputOpcode == opcodeStore_ -> putPackedImage || + outputOpcode == X_PutImage) + { + handleImage(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> setUnpackColormap) + { + handleColormap(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> setUnpackAlpha) + { + handleAlpha(outputOpcode, outputMessage, outputLength); + } + } + else if (result < 0) + { + return -1; + } + } + else if (outputOpcode == opcodeStore_ -> abortSplit) + { + handleAbortSplitRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> finishSplit) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding finish split request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> resourceCache); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> freeSplit) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding free split request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> resourceCache); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> freeUnpack) + { + #ifdef DEBUG + *logofs << "handleWrite: Decoding free unpack request " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache_ -> resourceCache); + + #ifdef DEBUG + *logofs << "handleWrite: Freeing unpack state for resource " + << (unsigned int) cValue << ".\n" << logofs_flush; + #endif + + handleUnpackStateRemove(cValue); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> setExposeParameters) + { + // + // Send expose events according to agent's wish. + // + + decodeBuffer.decodeBoolValue(enableExpose_); + decodeBuffer.decodeBoolValue(enableGraphicsExpose_); + decodeBuffer.decodeBoolValue(enableNoExpose_); + + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> getUnpackParameters) + { + // + // Client proxy needs the list of supported + // unpack methods. We would need an encode + // buffer, but this is in proxy, not here in + // channel. + // + + #ifdef TEST + *logofs << "handleWrite: Sending X_GetInputFocus request for FD#" + << fd_ << " due to OPCODE#" << (unsigned int) outputOpcode + << ".\n" << logofs_flush; + #endif + + outputOpcode = X_GetInputFocus; + + outputLength = 4; + outputMessage = writeBuffer_.addMessage(outputLength); + + sequenceQueue_.push(clientSequence_, outputOpcode, + opcodeStore_ -> getUnpackParameters); + } + else if (outputOpcode == opcodeStore_ -> getControlParameters || + outputOpcode == opcodeStore_ -> getCleanupParameters || + outputOpcode == opcodeStore_ -> getImageParameters) + { + handleNullRequest(outputOpcode, outputMessage, outputLength); + } + else if (outputOpcode == opcodeStore_ -> getShmemParameters) + { + if (handleShmemRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else if (outputOpcode == opcodeStore_ -> setCacheParameters) + { + if (handleCacheRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else if (outputOpcode == opcodeStore_ -> getFontParameters) + { + if (handleFontRequest(decodeBuffer, outputOpcode, + outputMessage, outputLength) < 0) + { + return -1; + } + } + else + { + MessageStore *messageStore = clientStore_ -> + getRequestStore(X_NXInternalGenericRequest); + + hit = handleDecode(decodeBuffer, clientCache_, messageStore, + outputOpcode, outputMessage, outputLength); + } + } + } // End of switch on opcode. + + // + // A packed image request can generate more than just + // a single X_PutImage. Write buffer is handled inside + // handleUnpack(). Cannot simply assume that the final + // opcode and size must be put at the buffer offset as + // as buffer could have been grown or could have been + // replaced by a scratch buffer. The same is true in + // the case of a shared memory image. + // + + if (outputOpcode != 0) + { + // + // Commit opcode and size to the buffer. + // + + *outputMessage = (unsigned char) outputOpcode; + + PutUINT(outputLength >> 2, outputMessage + 2, bigEndian_); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleWrite: Handled request OPCODE#" + << (unsigned int) outputOpcode << " (" + << DumpOpcode(outputOpcode) << ") for FD#" + << fd_ << " sequence " << clientSequence_ + << ". " << outputLength << " bytes out.\n" + << logofs_flush; + #endif + } + #if defined(TEST) || defined(OPCODES) + else + { + // + // In case of shared memory images the log doesn't + // reflect the actual opcode of the request that is + // going to be written. It would be possible to find + // the opcode of the original request received from + // the remote proxy in member imageState_ -> opcode, + // but we have probably already deleted the struct. + // + + *logofs << "handleWrite: Handled image request for FD#" + << fd_ << " new sequence " << clientSequence_ + << ". " << outputLength << " bytes out.\n" + << logofs_flush; + } + #endif + + // + // Check if we produced enough data. We need to + // decode all the proxy messages or the decode + // buffer will be left in an inconsistent state, + // so we just update the finish flag in case of + // failure. + // + + handleFlush(flush_if_needed); + + } // End of while (decodeBuffer.decodeOpcodeValue(outputOpcode, 8, ... + + } // End of the decoding block. + + // + // Write any remaining data to the X connection. + // + + if (handleFlush(flush_if_any) < 0) + { + return -1; + } + + // + // Reset offset at which we read the + // last event looking for the shared + // memory completion. + // + + if (shmemState_ != NULL) + { + shmemState_ -> checked = 0; + } + + return 1; +} + +// +// End of handleWrite(). +// + +// +// Other members. +// + +int ServerChannel::handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, + T_store_action action, int position, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + if (control -> isProtoStep7() == 1) + { + splitState_.current = splitState_.resource; + } + + handleSplitStoreAlloc(&splitResources_, splitState_.current); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Message OPCODE#" + << (unsigned int) store -> opcode() << " of size " << size + << " [split] with resource " << splitState_.current + << " position " << position << " and action [" + << DumpAction(action) << "] at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + // + // Get the MD5 of the message being + // split. + // + + T_checksum checksum = NULL; + + if (action != IS_HIT) + { + handleSplitChecksum(decodeBuffer, checksum); + } + + // + // The method must abort the connection + // if it can't allocate the split. + // + + Split *splitMessage = clientStore_ -> getSplitStore(splitState_.current) -> + add(store, splitState_.current, position, + action, checksum, buffer, size); + + // + // If we are connected to an old proxy + // version or the encoding side didn't + // provide a checksum, then don't send + // the split report. + // + + if (control -> isProtoStep7() == 0 || + checksum == NULL) + { + if (action == IS_HIT) + { + splitMessage -> setState(split_loaded); + } + else + { + splitMessage -> setState(split_missed); + } + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleSplit: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.current); + + #endif + + delete [] checksum; + + return 1; + } + + delete [] checksum; + + // + // Tell the split store if it must use + // the disk cache to retrieve and save + // the message. + // + + splitMessage -> setPolicy(splitState_.load, splitState_.save); + + // + // Try to locate the message on disk. + // + + if (clientStore_ -> getSplitStore(splitState_.current) -> + load(splitMessage) == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Loaded the message " + << "from the image cache.\n" << logofs_flush; + #endif + + splitMessage -> setState(split_loaded); + } + else + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: WARNING! SPLIT! Can't find the message " + << "in the image cache.\n" << logofs_flush; + #endif + + splitMessage -> setState(split_missed); + } + + #if defined(TEST) || defined(SPLIT) + + T_timestamp startTs = getTimestamp(); + + *logofs << "handleSplit: SPLIT! Encoding abort " + << "split events for FD#" << fd_ << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + + if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0) + { + return -1; + } + + // + // Send the encoded data immediately. We + // want the abort split message to reach + // the remote proxy as soon as possible. + // + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleSplit: SPLIT! Spent " + << diffTimestamp(startTs, getTimestamp()) << " Ms " + << "handling abort split events for FD#" << fd_ + << ".\n" << logofs_flush; + + *logofs << "handleSplit: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.current); + + #endif + + return 1; +} + +int ServerChannel::handleSplit(DecodeBuffer &decodeBuffer) +{ + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Going to handle splits " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + unsigned char resource; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + splitState_.current = resource; + } + + handleSplitStoreAlloc(&splitResources_, splitState_.current); + + SplitStore *splitStore = clientStore_ -> getSplitStore(splitState_.current); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Handling splits for " + << "resource [" << splitState_.current << "] with " + << splitStore -> getSize() << " elements " + << "in the split store.\n" << logofs_flush; + #endif + + int result = splitStore -> receive(decodeBuffer); + + if (result < 0) + { + #ifdef PANIC + *logofs << "handleSplit: PANIC! Receive of split for FD#" << fd_ + << " failed.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Receive of split for FD#" << fd_ + << " failed.\n"; + + return -1; + } + else if (result == 0) + { + // + // The split is still incomplete. It's time + // to check if we need to start the house- + // keeping process to take care of the image + // cache. + // + + if (proxy -> handleAsyncKeeperCallback() < 0) + { + return -1; + } + } + else + { + // + // Note that we don't need the resource id at the + // X server side and, thus, we don't provide it + // at the time we add split to the split store. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Remote agent should " + << "now commit a new split for resource [" + << splitState_.current << "].\n" + << logofs_flush; + + clientStore_ -> dumpCommitStore(); + + #endif + + if (splitStore -> getSize() == 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! Removing split store " + << "for resource [" << splitState_.current + << "] at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + handleSplitStoreRemove(&splitResources_, splitState_.current); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: SPLIT! There are [" << clientStore_ -> + getSplitTotalSize() << "] messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + #endif + } + else + { + // + // If the next split is discarded, it can be + // that, since the beginning of the split, we + // have saved the message on the disk, due to + // a more recent split operation. This is also + // the case when we had to discard the message + // because it was locked but, since then, we + // completed the transferral of the split. + // + + Split *splitMessage = splitStore -> getFirstSplit(); + + if (splitMessage -> getAction() == is_discarded && + splitMessage -> getState() == split_missed && + splitStore -> load(splitMessage) == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplit: WARNING! SPLIT! Asynchronously " + << "loaded the message from the image cache.\n" + << logofs_flush; + #endif + + splitMessage -> setState(split_loaded); + + #if defined(TEST) || defined(SPLIT) + + T_timestamp startTs = getTimestamp(); + + *logofs << "handleSplit: WARNING! SPLIT! Asynchronously " + << "encoding abort split events for FD#" << fd_ + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0) + { + return -1; + } + + // + // Send the encoded data immediately. We + // want the abort split message to reach + // the remote proxy as soon as possible. + // + + if (proxy -> handleAsyncFlush() < 0) + { + return -1; + } + + #if defined(TEST) || defined(SPLIT) + + *logofs << "handleSplit: WARNING! SPLIT! Spent " + << diffTimestamp(startTs, getTimestamp()) << " Ms " + << "handling asynchronous abort split events for " + << "FD#" << fd_ << ".\n" << logofs_flush; + + *logofs << "handleSplit: SPLIT! There are " << clientStore_ -> + getSplitTotalSize() << " messages and " << clientStore_ -> + getSplitTotalStorageSize() << " bytes to send in " + << "the split stores.\n" << logofs_flush; + + clientStore_ -> dumpSplitStore(splitState_.current); + + #endif + } + } + } + + return 1; +} + +int ServerChannel::handleSplitEvent(EncodeBuffer &encodeBuffer, Split *splitMessage) +{ + int resource = splitMessage -> getResource(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Going to send a " + << "split report for resource " << resource + << ".\n" << logofs_flush; + #endif + + // + // This function is called only after the message + // has been searched in the disk cache. We need to + // inform the other side if the data transfer can + // start or it must be aborted to let the local + // side use the copy that was found on the disk. + // + + #if defined(TEST) || defined(INFO) + + if (splitMessage -> getState() != split_loaded && + splitMessage -> getState() != split_missed) + { + *logofs << "handleSplitEvent: PANIC! Can't find the split to be aborted.\n" + << logofs_flush; + + HandleCleanup(); + } + + #endif + + // + // We need to send a boolean telling if the split + // was found or not, followed by the checksum of + // message we are referencing. + // + + T_checksum checksum = splitMessage -> getChecksum(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Sending split report for " + << "checksum [" << DumpChecksum(checksum) << "].\n" + << logofs_flush; + #endif + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + encodeBuffer.encodeOpcodeValue(opcodeStore_ -> splitEvent, + serverCache_ -> opcodeCache); + + // + // The encoding in older protocol versions + // is different but we will never try to + // send a split report if the remote does + // not support our version. + // + + encodeBuffer.encodeCachedValue(resource, 8, + serverCache_ -> resourceCache); + + if (splitMessage -> getState() == split_loaded) + { + encodeBuffer.encodeBoolValue(1); + + encodeBuffer.encodeOpcodeValue(splitMessage -> getStore() -> opcode(), + serverCache_ -> abortOpcodeCache); + + encodeBuffer.encodeValue(splitMessage -> compressedSize(), 32, 14); + } + else + { + encodeBuffer.encodeBoolValue(0); + } + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + encodeBuffer.encodeValue((unsigned int) checksum[i], 8); + } + + // + // Update statistics for this special opcode. + // + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) || defined(INFO) || defined(SPLIT) + *logofs << "handleSplitEvent: SPLIT! Handled event OPCODE#" + << (unsigned int) opcodeStore_ -> splitEvent << " (" + << DumpOpcode(opcodeStore_ -> splitEvent) << ")" << " for FD#" + << fd_ << " sequence none. 0 bytes in, " << bits << " bits (" + << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush; + #endif + + statistics -> addEventBits(opcodeStore_ -> splitEvent, 0, bits); + + return 1; +} + +int ServerChannel::handleAbortSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + unsigned char resource; + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Handling abort split " + << "request for FD#" << fd_ << " and resource " + << (unsigned) resource << ".\n" + << logofs_flush; + #endif + + int splits = 0; + + SplitStore *splitStore = clientStore_ -> getSplitStore(resource); + + if (splitStore != NULL) + { + // + // Discard from the memory cache the messages + // that are still incomplete and then get rid + // of the splits in the store. + // + + #if defined(TEST) || defined(SPLIT) + + clientStore_ -> dumpSplitStore(resource); + + #endif + + Split *splitMessage; + + for (;;) + { + splitMessage = splitStore -> getFirstSplit(); + + if (splitMessage == NULL) + { + // + // Check if we had created the store + // but no message was added yet. + // + + #ifdef WARNING + + if (splits == 0) + { + *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The " + << "split store for resource [" << (unsigned int) + resource << "] is unexpectedly empty.\n" + << logofs_flush; + } + + #endif + + break; + } + + // + // Splits already aborted can't be in the + // split store. + // + + #if defined(TEST) || defined(SPLIT) + + if (splitMessage -> getState() == split_aborted) + { + *logofs << "handleAbortSplitRequest: PANIC! SPLIT! Found an " + << "aborted split in store [" << (unsigned int) resource + << "].\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + if (splitMessage -> getAction() == IS_HIT) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Removing the " + << "split from the memory cache.\n" + << logofs_flush; + #endif + + splitMessage -> getStore() -> remove(splitMessage -> getPosition(), + discard_checksum, use_data); + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Removing the " + << "split from the split store.\n" + << logofs_flush; + #endif + + splitMessage = splitStore -> pop(); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleAbortSplitRequest: SPLIT! Freeing up the " + << "aborted split.\n" << logofs_flush; + #endif + + delete splitMessage; + + splits++; + } + } + #ifdef WARNING + else + { + *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The " + << "split store for resource [" << (unsigned int) + resource << "] is already empty.\n" + << logofs_flush; + } + #endif + + handleNullRequest(opcode, buffer, size); + + return (splits > 0); +} + +int ServerChannel::handleCommitSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Get request type and position of the image + // to commit. + // + + unsigned char request; + + decodeBuffer.decodeOpcodeValue(request, clientCache_ -> opcodeCache); + + unsigned int diffCommit; + + decodeBuffer.decodeValue(diffCommit, 32, 5); + + splitState_.commit += diffCommit; + + unsigned char resource = 0; + unsigned int commit = 1; + + // + // Send the resource id and the commit flag. + // The resource id is ignored at the moment. + // The message will be handled based on the + // resource id that was sent together with + // the original message. + // + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + decodeBuffer.decodeBoolValue(commit); + + Split *split = handleSplitCommitRemove(request, resource, splitState_.commit); + + if (split == NULL) + { + return -1; + } + + clientStore_ -> getCommitStore() -> update(split); + + if (commit == 1) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleCommitSplitRequest: SPLIT! Handling split commit " + << "for FD#" << fd_ << " with commit " << commit + << " request " << (unsigned) request << " resource " + << (unsigned) resource << " and position " + << splitState_.commit << ".\n" + << logofs_flush; + #endif + + // + // Allocate as many bytes in the write + // buffer as the final length of the + // message in uncompressed form. + // + + size = split -> plainSize(); + + buffer = writeBuffer_.addMessage(size); + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleCommitSplitRequest: SPLIT! Prepared an " + << "outgoing buffer of " << size << " bytes.\n" + << logofs_flush; + #endif + + if (clientStore_ -> getCommitStore() -> expand(split, buffer, size) < 0) + { + writeBuffer_.removeMessage(size); + + commit = 0; + } + } + + // + // Free the split. + // + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleCommitSplitRequest: SPLIT! Freeing up the " + << "committed split.\n" << logofs_flush; + #endif + + delete split; + + // + // Discard the operation and send a null + // message. + // + + if (commit == 0) + { + handleNullRequest(opcode, buffer, size); + } + else + { + // + // Save the sequence number to be able + // to mask any error generated by the + // request. + // + + updateCommitQueue(clientSequence_); + + // + // Now in the write buffer there is + // a copy of this request. + // + + opcode = request; + } + + return commit; +} + +int ServerChannel::handleGeometry(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + // + // Replace the old geometry and taint + // the message into a X_NoOperation. + // + + int resource = *(buffer + 1); + + #ifdef TEST + *logofs << "handleGeometry: Setting new unpack geometry " + << "for resource " << resource << ".\n" + << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + handleUnpackAllocGeometry(resource); + + unpackState_[resource] -> geometry -> depth1_bpp = *(buffer + 4); + unpackState_[resource] -> geometry -> depth4_bpp = *(buffer + 5); + unpackState_[resource] -> geometry -> depth8_bpp = *(buffer + 6); + unpackState_[resource] -> geometry -> depth16_bpp = *(buffer + 7); + unpackState_[resource] -> geometry -> depth24_bpp = *(buffer + 8); + unpackState_[resource] -> geometry -> depth32_bpp = *(buffer + 9); + + unpackState_[resource] -> geometry -> red_mask = GetULONG(buffer + 12, bigEndian_); + unpackState_[resource] -> geometry -> green_mask = GetULONG(buffer + 16, bigEndian_); + unpackState_[resource] -> geometry -> blue_mask = GetULONG(buffer + 20, bigEndian_); + + handleCleanAndNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleColormap(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + // + // Replace the old colormap and taint + // the message into a X_NoOperation. + // + + int resource = *(buffer + 1); + + #ifdef TEST + *logofs << "handleColormap: Setting new unpack colormap " + << "for resource " << resource << ".\n" + << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + handleUnpackAllocColormap(resource); + + // + // New protocol versions send the alpha + // data in compressed form. + // + + if (control -> isProtoStep7() == 1) + { + unsigned int packed = GetULONG(buffer + 8, bigEndian_); + unsigned int unpacked = GetULONG(buffer + 12, bigEndian_); + + validateSize("colormap", packed, unpacked, 16, size); + + if (unpackState_[resource] -> colormap -> entries != unpacked >> 2 && + unpackState_[resource] -> colormap -> data != NULL) + { + #ifdef TEST + *logofs << "handleColormap: Freeing previously allocated " + << "unpack colormap data.\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> colormap -> data; + + unpackState_[resource] -> colormap -> data = NULL; + unpackState_[resource] -> colormap -> entries = 0; + } + + #ifdef TEST + *logofs << "handleColormap: Setting " << unpacked + << " bytes of unpack colormap data for resource " + << resource << ".\n" << logofs_flush; + #endif + + if (unpackState_[resource] -> colormap -> data == NULL) + { + unpackState_[resource] -> colormap -> data = + (unsigned int *) new unsigned char[unpacked]; + + if (unpackState_[resource] -> colormap -> data == NULL) + { + #ifdef PANIC + *logofs << "handleColormap: PANIC! Can't allocate " + << unpacked << " entries for unpack colormap data " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + goto handleColormapEnd; + } + + #ifdef DEBUG + *logofs << "handleColormap: Size of new colormap data is " + << unpacked << ".\n" << logofs_flush; + #endif + } + + unsigned int method = *(buffer + 4); + + if (method == PACK_COLORMAP) + { + if (UnpackColormap(method, buffer + 16, packed, + (unsigned char *) unpackState_[resource] -> + colormap -> data, unpacked) < 0) + { + #ifdef PANIC + *logofs << "handleColormap: PANIC! Can't unpack " << packed + << " bytes to " << unpacked << " entries for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> colormap -> data; + + unpackState_[resource] -> colormap -> data = NULL; + unpackState_[resource] -> colormap -> entries = 0; + + goto handleColormapEnd; + } + } + else + { + memcpy((unsigned char *) unpackState_[resource] -> + colormap -> data, buffer + 16, unpacked); + } + + unpackState_[resource] -> colormap -> entries = unpacked >> 2; + + #if defined(DEBUG) && defined(DUMP) + + *logofs << "handleColormap: Dumping colormap entries:\n" + << logofs_flush; + + const unsigned char *p = unpackState_[resource] -> colormap -> data; + + for (unsigned int i = 0; i < unpackState_[resource] -> + colormap -> entries; i++) + { + *logofs << "handleColormap: [" << i << "] [" + << (void *) ((int) p[i]) << "].\n" + << logofs_flush; + } + + #endif + } + else + { + unsigned int entries = GetULONG(buffer + 4, bigEndian_); + + if (size == entries * 4 + 8) + { + if (unpackState_[resource] -> colormap -> entries != entries && + unpackState_[resource] -> colormap -> data != NULL) + { + #ifdef TEST + *logofs << "handleColormap: Freeing previously " + << "allocated unpack colormap.\n" + << logofs_flush; + #endif + + delete [] unpackState_[resource] -> colormap -> data; + + unpackState_[resource] -> colormap -> data = NULL; + unpackState_[resource] -> colormap -> entries = 0; + } + + if (entries > 0) + { + if (unpackState_[resource] -> colormap -> data == NULL) + { + unpackState_[resource] -> + colormap -> data = new unsigned int[entries]; + } + + if (unpackState_[resource] -> colormap -> data != NULL) + { + unpackState_[resource] -> colormap -> entries = entries; + + #ifdef DEBUG + *logofs << "handleColormap: Size of new colormap " + << "data is " << (entries << 2) << ".\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) unpackState_[resource] -> + colormap -> data, buffer + 8, entries << 2); + + #if defined(DEBUG) && defined(DUMP) + + *logofs << "handleColormap: Dumping colormap entries:\n" + << logofs_flush; + + const unsigned int *p = (unsigned int *) buffer + 8; + + for (unsigned int i = 0; i < entries; i++) + { + *logofs << "handleColormap: [" << i << "] [" + << (void *) p[i] << "].\n" + << logofs_flush; + } + + #endif + } + else + { + #ifdef PANIC + *logofs << "handleColormap: PANIC! Can't allocate " + << entries << " entries for unpack colormap " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + } + } + } + else + { + #ifdef PANIC + *logofs << "handleColormap: PANIC! Bad size " << size + << " for set unpack colormap message for FD#" + << fd_ << " with " << entries << " entries.\n" + << logofs_flush; + #endif + } + } + +handleColormapEnd: + + handleCleanAndNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleAlpha(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + int resource = *(buffer + 1); + + #ifdef TEST + *logofs << "handleAlpha: Setting new unpack alpha " + << "for resource " << resource << ".\n" + << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + handleUnpackAllocAlpha(resource); + + // + // New protocol versions send the alpha + // data in compressed form. + // + + if (control -> isProtoStep7() == 1) + { + unsigned int packed = GetULONG(buffer + 8, bigEndian_); + unsigned int unpacked = GetULONG(buffer + 12, bigEndian_); + + validateSize("alpha", packed, unpacked, 16, size); + + if (unpackState_[resource] -> alpha -> entries != unpacked && + unpackState_[resource] -> alpha -> data != NULL) + { + #ifdef TEST + *logofs << "handleAlpha: Freeing previously allocated " + << "unpack alpha data.\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> alpha -> data; + + unpackState_[resource] -> alpha -> data = NULL; + unpackState_[resource] -> alpha -> entries = 0; + } + + #ifdef TEST + *logofs << "handleAlpha: Setting " << unpacked + << " bytes of unpack alpha data for resource " + << resource << ".\n" << logofs_flush; + #endif + + if (unpackState_[resource] -> alpha -> data == NULL) + { + unpackState_[resource] -> alpha -> data = new unsigned char[unpacked]; + + if (unpackState_[resource] -> alpha -> data == NULL) + { + #ifdef PANIC + *logofs << "handleAlpha: PANIC! Can't allocate " + << unpacked << " entries for unpack alpha data " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + goto handleAlphaEnd; + } + + #ifdef DEBUG + *logofs << "handleAlpha: Size of new alpha data is " + << unpacked << ".\n" << logofs_flush; + #endif + } + + unsigned int method = *(buffer + 4); + + if (method == PACK_ALPHA) + { + if (UnpackAlpha(method, buffer + 16, packed, + unpackState_[resource] -> alpha -> + data, unpacked) < 0) + { + #ifdef PANIC + *logofs << "handleAlpha: PANIC! Can't unpack " << packed + << " bytes to " << unpacked << " entries for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> alpha -> data; + + unpackState_[resource] -> alpha -> data = NULL; + unpackState_[resource] -> alpha -> entries = 0; + + goto handleAlphaEnd; + } + } + else + { + memcpy((unsigned char *) unpackState_[resource] -> + alpha -> data, buffer + 16, unpacked); + } + + unpackState_[resource] -> alpha -> entries = unpacked; + + #if defined(DEBUG) && defined(DUMP) + + *logofs << "handleAlpha: Dumping alpha entries:\n" + << logofs_flush; + + const unsigned char *p = unpackState_[resource] -> alpha -> data; + + for (unsigned int i = 0; i < unpackState_[resource] -> + alpha -> entries; i++) + { + *logofs << "handleAlpha: [" << i << "] [" + << (void *) ((int) p[i]) << "].\n" + << logofs_flush; + } + + #endif + } + else + { + unsigned int entries = GetULONG(buffer + 4, bigEndian_); + + if (size == RoundUp4(entries) + 8) + { + if (unpackState_[resource] -> alpha -> entries != entries && + unpackState_[resource] -> alpha -> data != NULL) + { + #ifdef TEST + *logofs << "handleAlpha: Freeing previously allocated " + << "unpack alpha data.\n" << logofs_flush; + #endif + + delete [] unpackState_[resource] -> alpha -> data; + + unpackState_[resource] -> alpha -> data = NULL; + unpackState_[resource] -> alpha -> entries = 0; + } + + if (entries > 0) + { + if (unpackState_[resource] -> alpha -> data == NULL) + { + unpackState_[resource] -> alpha -> data = new unsigned char[entries]; + } + + if (unpackState_[resource] -> alpha -> data != NULL) + { + unpackState_[resource] -> alpha -> entries = entries; + + #ifdef DEBUG + *logofs << "handleAlpha: Size of new alpha data is " + << entries << ".\n" << logofs_flush; + #endif + + memcpy((unsigned char *) unpackState_[resource] -> + alpha -> data, buffer + 8, entries); + + #if defined(DEBUG) && defined(DUMP) + + *logofs << "handleAlpha: Dumping alpha entries:\n" + << logofs_flush; + + const unsigned char *p = buffer + 8; + + for (unsigned int i = 0; i < entries; i++) + { + *logofs << "handleAlpha: [" << i << "] [" + << (void *) ((int) p[i]) << "].\n" + << logofs_flush; + } + + #endif + } + else + { + #ifdef PANIC + *logofs << "handleAlpha: PANIC! Can't allocate " + << entries << " entries for unpack alpha data " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + } + } + } + #ifdef PANIC + else + { + *logofs << "handleAlpha: PANIC! Bad size " << size + << " for set unpack alpha message for FD#" + << fd_ << " with " << entries << " entries.\n" + << logofs_flush; + } + #endif + } + +handleAlphaEnd: + + handleCleanAndNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleImage(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + int result = 1; + + // + // Save the original opcode together with + // the image state so we can later find if + // this is a plain or a packed image when + // moving data to the shared memory area. + // + + handleImageStateAlloc(opcode); + + if (opcode == opcodeStore_ -> putPackedImage) + { + // + // Unpack the image and put a X_PutImage in a + // new buffer. Save the expected output size, + // so, in the case of a decoding error we can + // still update the statistics. + // + + int length = GetULONG(buffer + 20, bigEndian_); + + #ifdef TEST + *logofs << "handleImage: Sending image for FD#" << fd_ + << " due to OPCODE#" << (unsigned int) opcode << " with " + << GetULONG(buffer + 16, bigEndian_) << " bytes packed " + << "and " << GetULONG(buffer + 20, bigEndian_) + << " bytes unpacked.\n" << logofs_flush; + #endif + + statistics -> addPackedBytesIn(size); + + result = handleUnpack(opcode, buffer, size); + + if (result < 0) + { + // + // Recover from the error. Send a X_NoOperation + // to keep the sequence counter in sync with + // the remote peer. + // + + size = 4; + buffer = writeBuffer_.addMessage(size); + + *buffer = X_NoOperation; + + PutUINT(size >> 2, buffer + 2, bigEndian_); + + #ifdef PANIC + *logofs << "handleImage: PANIC! Sending X_NoOperation for FD#" + << fd_ << " to recover from failed unpack.\n" + << logofs_flush; + #endif + + // + // Set the output length to reflect the amount of + // data that would have been produced by unpacking + // the image. This is advisable to keep the count- + // ers in sync with those at remote proxy. Setting + // the size here doesn't have any effect on the + // size of data sent to the X server as the actual + // size will be taken from the content of the write + // buffer. + // + + size = length; + } + + statistics -> addPackedBytesOut(size); + + // + // Refrain the write loop from putting + // opcode and size in the output buffer. + // + + opcode = 0; + } + + // + // Now image is unpacked as a X_PutImage + // in write buffer. Check if we can send + // the image using the MIT-SHM extension. + // + + if (result > 0) + { + result = handleShmem(opcode, buffer, size); + + // + // We already put opcode and size in + // the resulting buffer. + // + + if (result > 0) + { + opcode = 0; + } + } + + return 1; +} + +int ServerChannel::handleMotion(EncodeBuffer &encodeBuffer) +{ + #if defined(TEST) || defined(INFO) + + if (lastMotion_[0] == '\0') + { + *logofs << "handleMotion: PANIC! No motion events to send " + << "for FD#" << fd_ << ".\n" << logofs_flush; + + HandleCleanup(); + } + + #endif + + #if defined(TEST) || defined(INFO) + *logofs << "handleMotion: Sending motion events for FD#" + << fd_ << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + // + // Replicate code from read loop. When have + // time and wish, try to split everything + // in functions. + // + + if (proxy -> handleAsyncSwitch(fd_) < 0) + { + return -1; + } + + const unsigned char *buffer = lastMotion_; + unsigned char opcode = *lastMotion_; + unsigned int size = 32; + + if (GetUINT(buffer + 2, bigEndian_) < serverSequence_) + { + PutUINT(serverSequence_, (unsigned char *) buffer + 2, bigEndian_); + } + + encodeBuffer.encodeOpcodeValue(opcode, serverCache_ -> opcodeCache); + + unsigned int sequenceNum = GetUINT(buffer + 2, bigEndian_); + + unsigned int sequenceDiff = sequenceNum - serverSequence_; + + serverSequence_ = sequenceNum; + + #ifdef DEBUG + *logofs << "handleMotion: Last server sequence number for FD#" + << fd_ << " is " << serverSequence_ << " with " + << "difference " << sequenceDiff << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(sequenceDiff, 16, + serverCache_ -> eventSequenceCache, 7); + + // + // If we fast encoded the message + // then skip the rest. + // + + if (control -> LocalDeltaCompression == 0) + { + int result = handleFastReadEvent(encodeBuffer, opcode, + buffer, size); + + #ifdef DEBUG + *logofs << "handleMotion: Sent saved motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + lastMotion_[0] = '\0'; + + #ifdef DEBUG + *logofs << "handleMotion: Reset last motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + if (result < 0) + { + return -1; + } + else if (result > 0) + { + return 1; + } + } + + // + // This should be just the part specific + // for motion events but is currently a + // copy-paste of code from the read loop. + // + + unsigned char detail = buffer[1]; + if (*buffer == MotionNotify) + encodeBuffer.encodeBoolValue((unsigned int) detail); + else if ((*buffer == EnterNotify) || (*buffer == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) detail, 3); + else if (*buffer == KeyRelease) + { + if (detail == serverCache_ -> keyPressLastKey) + encodeBuffer.encodeBoolValue(1); + else + { + encodeBuffer.encodeBoolValue(0); + encodeBuffer.encodeValue((unsigned int) detail, 8); + } + } + else if ((*buffer == ButtonPress) || (*buffer == ButtonRelease)) + encodeBuffer.encodeCachedValue(detail, 8, + serverCache_ -> buttonCache); + else + encodeBuffer.encodeValue((unsigned int) detail, 8); + unsigned int timestamp = GetULONG(buffer + 4, bigEndian_); + unsigned int timestampDiff = timestamp - serverCache_ -> lastTimestamp; + serverCache_ -> lastTimestamp = timestamp; + encodeBuffer.encodeCachedValue(timestampDiff, 32, + serverCache_ -> motionNotifyTimestampCache, 9); + int skipRest = 0; + if (*buffer == KeyRelease) + { + skipRest = 1; + for (unsigned int i = 8; i < 31; i++) + { + if (buffer[i] != serverCache_ -> keyPressCache[i - 8]) + { + skipRest = 0; + break; + } + } + encodeBuffer.encodeBoolValue(skipRest); + } + if (!skipRest) + { + const unsigned char *nextSrc = buffer + 8; + for (unsigned int i = 0; i < 3; i++) + { + encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29, + *serverCache_ -> motionNotifyWindowCache[i], 6); + nextSrc += 4; + } + unsigned int rootX = GetUINT(buffer + 20, bigEndian_); + unsigned int rootY = GetUINT(buffer + 22, bigEndian_); + unsigned int eventX = GetUINT(buffer + 24, bigEndian_); + unsigned int eventY = GetUINT(buffer + 26, bigEndian_); + eventX -= rootX; + eventY -= rootY; + encodeBuffer.encodeCachedValue(rootX - + serverCache_ -> motionNotifyLastRootX, 16, + serverCache_ -> motionNotifyRootXCache, 6); + serverCache_ -> motionNotifyLastRootX = rootX; + encodeBuffer.encodeCachedValue(rootY - + serverCache_ -> motionNotifyLastRootY, 16, + serverCache_ -> motionNotifyRootYCache, 6); + serverCache_ -> motionNotifyLastRootY = rootY; + encodeBuffer.encodeCachedValue(eventX, 16, + serverCache_ -> motionNotifyEventXCache, 6); + encodeBuffer.encodeCachedValue(eventY, 16, + serverCache_ -> motionNotifyEventYCache, 6); + encodeBuffer.encodeCachedValue(GetUINT(buffer + 28, bigEndian_), + 16, serverCache_ -> motionNotifyStateCache); + if ((*buffer == EnterNotify) || (*buffer == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) buffer[30], 2); + else + encodeBuffer.encodeBoolValue((unsigned int) buffer[30]); + if ((*buffer == EnterNotify) || (*buffer == LeaveNotify)) + encodeBuffer.encodeValue((unsigned int) buffer[31], 2); + else if (*buffer == KeyPress) + { + serverCache_ -> keyPressLastKey = detail; + for (unsigned int i = 8; i < 31; i++) + { + serverCache_ -> keyPressCache[i - 8] = buffer[i]; + } + } + } + + // + // Print info about achieved compression + // and update the statistics. + // + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleMotion: Handled event OPCODE#" << (unsigned int) buffer[0] + << " for FD#" << fd_ << " sequence " << sequenceNum << ". " + << size << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + #endif + + statistics -> addEventBits(*buffer, size << 3, bits); + + #ifdef DEBUG + *logofs << "handleMotion: Sent saved motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + lastMotion_[0] = '\0'; + + #ifdef DEBUG + *logofs << "handleMotion: Reset last motion event for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ServerChannel::handleConfiguration() +{ + #ifdef TEST + *logofs << "ServerChannel: Setting new buffer parameters " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + readBuffer_.setSize(control -> ServerInitialReadSize, + control -> ServerMaximumBufferSize); + + writeBuffer_.setSize(control -> TransportXBufferSize, + control -> TransportXBufferThreshold, + control -> TransportMaximumBufferSize); + + transport_ -> setSize(control -> TransportXBufferSize, + control -> TransportXBufferThreshold, + control -> TransportMaximumBufferSize); + return 1; +} + +int ServerChannel::handleFinish() +{ + #ifdef TEST + *logofs << "ServerChannel: Finishing connection for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + congestion_ = 0; + priority_ = 0; + + finish_ = 1; + + // + // Reset the motion event. + // + + lastMotion_[0] = '\0'; + + transport_ -> fullReset(); + + return 1; +} + +int ServerChannel::handleAsyncEvents() +{ + // + // Encode more events while decoding the + // proxy messages. + // + + if (transport_ -> readable() > 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAsyncEvents: WARNING! Encoding events " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + #endif + + #if defined(TEST) || defined(INFO) + + T_timestamp startTs = getTimestamp(); + + #endif + + if (proxy -> handleAsyncRead(fd_) < 0) + { + return -1; + } + + #if defined(TEST) || defined(INFO) + *logofs << "handleAsyncEvents: Spent " << diffTimestamp(startTs, + getTimestamp()) << " Ms handling events for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 1; + } + + return 0; +} + +int ServerChannel::handleUnpack(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + int resource = *(buffer + 1); + + #ifdef DEBUG + *logofs << "handleUnpack: Unpacking image for resource " << resource + << " with method " << (unsigned int) *(buffer + 12) + << ".\n" << logofs_flush; + #endif + + handleUnpackStateInit(resource); + + T_geometry *geometryState = unpackState_[resource] -> geometry; + T_colormap *colormapState = unpackState_[resource] -> colormap; + T_alpha *alphaState = unpackState_[resource] -> alpha; + + if (geometryState == NULL) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Missing geometry unpacking " + << "image for resource " << resource << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Missing geometry unpacking " + << "image for resource " << resource << ".\n"; + + return -1; + } + + // + // Get the image data from the buffer. + // + + imageState_ -> drawable = GetULONG(buffer + 4, bigEndian_); + imageState_ -> gcontext = GetULONG(buffer + 8, bigEndian_); + + imageState_ -> method = *(buffer + 12); + + imageState_ -> format = *(buffer + 13); + imageState_ -> srcDepth = *(buffer + 14); + imageState_ -> dstDepth = *(buffer + 15); + + imageState_ -> srcLength = GetULONG(buffer + 16, bigEndian_); + imageState_ -> dstLength = GetULONG(buffer + 20, bigEndian_); + + imageState_ -> srcX = GetUINT(buffer + 24, bigEndian_); + imageState_ -> srcY = GetUINT(buffer + 26, bigEndian_); + imageState_ -> srcWidth = GetUINT(buffer + 28, bigEndian_); + imageState_ -> srcHeight = GetUINT(buffer + 30, bigEndian_); + + imageState_ -> dstX = GetUINT(buffer + 32, bigEndian_); + imageState_ -> dstY = GetUINT(buffer + 34, bigEndian_); + imageState_ -> dstWidth = GetUINT(buffer + 36, bigEndian_); + imageState_ -> dstHeight = GetUINT(buffer + 38, bigEndian_); + + #ifdef TEST + *logofs << "handleUnpack: Source X is " << imageState_ -> srcX + << " Y is " << imageState_ -> srcY << " width is " + << imageState_ -> srcWidth << " height is " + << imageState_ -> srcHeight << ".\n" + << logofs_flush; + #endif + + #ifdef TEST + *logofs << "handleUnpack: Destination X is " << imageState_ -> dstX + << " Y is " << imageState_ -> dstY << " width is " + << imageState_ -> dstWidth << " height is " + << imageState_ -> dstHeight << ".\n" + << logofs_flush; + #endif + + if (imageState_ -> srcX != 0 || imageState_ -> srcY != 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Unsupported source coordinates " + << "in unpack request.\n" << logofs_flush; + #endif + + return -1; + } + else if (imageState_ -> method == PACK_COLORMAP_256_COLORS && + (colormapState == NULL || colormapState -> data == NULL)) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Cannot find any unpack colormap.\n" + << logofs_flush; + #endif + + return -1; + } + + // + // Field srcLength carries size of image data in + // packed format. Field dstLength is size of the + // image in the original X bitmap format. + // + + unsigned int srcDataOffset = 40; + + unsigned int srcSize = imageState_ -> srcLength; + + unsigned int removeSize = size; + + unsigned char *srcData = buffer + srcDataOffset; + + // + // Get source and destination bits per pixel. + // + + int srcBitsPerPixel = MethodBitsPerPixel(imageState_ -> method); + + if (srcBitsPerPixel <= 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Can't identify source " + << "bits per pixel for method " << (unsigned int) + imageState_ -> method << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify source bits " + << "per pixel for method " << (unsigned int) + imageState_ -> method << ".\n"; + + writeBuffer_.removeMessage(removeSize); + + return -1; + } + + #ifdef TEST + *logofs << "handleUnpack: Source bits per pixel are " + << srcBitsPerPixel << " source data size is " + << srcSize << ".\n" << logofs_flush; + #endif + + int dstBitsPerPixel = UnpackBitsPerPixel(geometryState, imageState_ -> dstDepth); + + if (dstBitsPerPixel <= 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Can't identify " + << "destination bits per pixel for depth " + << (unsigned int) imageState_ -> dstDepth + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Can't identify " + << "destination bits per pixel for depth " + << (unsigned int) imageState_ -> dstDepth + << ".\n"; + + writeBuffer_.removeMessage(removeSize); + + return -1; + } + + // + // Destination is a PutImage request. + // + + unsigned int dstDataOffset = 24; + + // + // Output buffer size must match the number of input + // pixels multiplied by the number of bytes per pixel + // of current geometry. + // + + size = (RoundUp4(imageState_ -> dstWidth * dstBitsPerPixel / 8) * + imageState_ -> dstHeight) + dstDataOffset; + + #ifdef TEST + *logofs << "handleUnpack: Destination bits per pixel are " + << dstBitsPerPixel << " destination data size is " + << size - dstDataOffset << ".\n" << logofs_flush; + #endif + + unsigned int dstSize = size - dstDataOffset; + + imageState_ -> dstLines = imageState_ -> dstHeight; + + unsigned char *dstData; + + // + // Size of the final output buffer had to be stored + // in the offset field of XImage/NXPackedImage. + // + + #ifdef WARNING + + if (dstSize != imageState_ -> dstLength) + { + *logofs << "handleUnpack: WARNING! Destination size mismatch " + << "with reported " << imageState_ -> dstLength + << " and actual " << dstSize << ".\n" + << logofs_flush; + } + + #endif + + // + // The decoding algorithm has put the packed image + // in the plain write buffer. Let's use the scratch + // buffer to uncompress the image. + // + + buffer = writeBuffer_.addScratchMessage(size); + + dstData = buffer + dstDataOffset; + + // + // Unpack image into the buffer. + // + + *buffer = (unsigned char) X_PutImage; + + *(buffer + 1) = imageState_ -> format; + + PutUINT(size >> 2, buffer + 2, bigEndian_); + + PutULONG(imageState_ -> drawable, buffer + 4, bigEndian_); + PutULONG(imageState_ -> gcontext, buffer + 8, bigEndian_); + + PutUINT(imageState_ -> dstWidth, buffer + 12, bigEndian_); + PutUINT(imageState_ -> dstLines, buffer + 14, bigEndian_); + + PutUINT(imageState_ -> dstX, buffer + 16, bigEndian_); + PutUINT(imageState_ -> dstY, buffer + 18, bigEndian_); + + *(buffer + 20) = 0; + *(buffer + 21) = imageState_ -> dstDepth; + + #ifdef TEST + *logofs << "handleUnpack: Write buffer size is " + << writeBuffer_.getLength() << " scratch size is " + << writeBuffer_.getScratchLength() << ".\n" + << logofs_flush; + #endif + + int result = 0; + + switch (imageState_ -> method) + { + case PACK_JPEG_8_COLORS: + case PACK_JPEG_64_COLORS: + case PACK_JPEG_256_COLORS: + case PACK_JPEG_512_COLORS: + case PACK_JPEG_4K_COLORS: + case PACK_JPEG_32K_COLORS: + case PACK_JPEG_64K_COLORS: + case PACK_JPEG_256K_COLORS: + case PACK_JPEG_2M_COLORS: + case PACK_JPEG_16M_COLORS: + { + result = UnpackJpeg(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_PNG_8_COLORS: + case PACK_PNG_64_COLORS: + case PACK_PNG_256_COLORS: + case PACK_PNG_512_COLORS: + case PACK_PNG_4K_COLORS: + case PACK_PNG_32K_COLORS: + case PACK_PNG_64K_COLORS: + case PACK_PNG_256K_COLORS: + case PACK_PNG_2M_COLORS: + case PACK_PNG_16M_COLORS: + { + result = UnpackPng(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_RGB_16M_COLORS: + { + result = UnpackRgb(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_RLE_16M_COLORS: + { + result = UnpackRle(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_BITMAP_16M_COLORS: + { + result = UnpackBitmap(geometryState, imageState_ -> method, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + break; + } + case PACK_COLORMAP_256_COLORS: + { + result = Unpack8(geometryState, colormapState, srcBitsPerPixel, + imageState_ -> srcWidth, imageState_ -> srcHeight, srcData, + srcSize, dstBitsPerPixel, imageState_ -> dstWidth, + imageState_ -> dstHeight, dstData, dstSize); + + break; + } + default: + { + const T_colormask *colorMask = MethodColorMask(imageState_ -> method); + + switch (imageState_ -> method) + { + case PACK_MASKED_8_COLORS: + case PACK_MASKED_64_COLORS: + case PACK_MASKED_256_COLORS: + { + result = Unpack8(geometryState, colorMask, imageState_ -> srcDepth, + imageState_ -> srcWidth, imageState_ -> srcHeight, + srcData, srcSize, imageState_ -> dstDepth, + imageState_ -> dstWidth, imageState_ -> dstHeight, + dstData, dstSize); + break; + } + case PACK_MASKED_512_COLORS: + case PACK_MASKED_4K_COLORS: + case PACK_MASKED_32K_COLORS: + case PACK_MASKED_64K_COLORS: + { + result = Unpack16(geometryState, colorMask, imageState_ -> srcDepth, + imageState_ -> srcWidth, imageState_ -> srcHeight, + srcData, srcSize, imageState_ -> dstDepth, + imageState_ -> dstWidth, imageState_ -> dstHeight, + dstData, dstSize); + break; + } + case PACK_MASKED_256K_COLORS: + case PACK_MASKED_2M_COLORS: + case PACK_MASKED_16M_COLORS: + { + result = Unpack24(geometryState, colorMask, imageState_ -> srcDepth, + imageState_ -> srcWidth, imageState_ -> srcHeight, + srcData, srcSize, imageState_ -> dstDepth, + imageState_ -> dstWidth, imageState_ -> dstHeight, + dstData, dstSize); + break; + } + default: + { + break; + } + } + } + } + + writeBuffer_.removeMessage(removeSize); + + if (result <= 0) + { + #ifdef PANIC + *logofs << "handleUnpack: PANIC! Failed to unpack image " + << "with method '" << (unsigned int) imageState_ -> method + << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Failed to unpack image " + << "with method '" << (unsigned int) imageState_ -> method + << "'.\n"; + + // + // TODO: We should mark the image somehow, + // and force the remote to remove it from + // the cache. + // + + writeBuffer_.removeScratchMessage(); + + return -1; + } + + // + // Alpha channel is used only on some 32 bits pixmaps + // and only if render extension is in use. Presently + // we don't have an efficient way to know in advance + // if mask must be applied or not to the image. If an + // alpha channel is set, the function will check if + // the size of the alpha data matches the size of the + // image. In the worst case we'll create an useless + // alpha plane for a pixmap that doesn't need it. + // + + if (alphaState != NULL && alphaState -> data != NULL && + imageState_ -> dstDepth == 32) + { + UnpackAlpha(alphaState, dstData, dstSize, imageByteOrder_); + } + + return 1; +} + +int ServerChannel::handleAuthorization(unsigned char *buffer) +{ + // + // At the present moment we don't support more than + // a single display for each proxy, so authorization + // data is shared among all the channels. + // + // Use the following code to simulate authentication + // failures on a LSB machine: + // + // memcpy(buffer + 12 + (((buffer[6] + 256 * + // buffer[7]) + 3) & ~3), "1234567890123456", 16); + // + + if (auth == NULL) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAuthorization: Forwarding the real cookie " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (auth -> checkCookie(buffer) == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAuthorization: Matched the fake cookie " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 1; + } + else + { + #if defined(TEST) || defined(INFO) + *logofs << "handleAuthorization: WARNING! Failed to match " + << "the fake cookie for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + return -1; + } +} + +int ServerChannel::handleAuthorization(const unsigned char *buffer, int size) +{ + // + // Check the X server's response and, in the case of + // an error, print the textual information reported + // by the server. + // + + if (*buffer != 1) + { + const char *reason = NULL; + + // + // At the moment we don't take into account the end- + // ianess of the reply. This should work in any case + // because we simply try to match a few well-known + // error strings. + // + + if (size >= INVALID_COOKIE_SIZE + 8 && + memcmp(buffer + 8, INVALID_COOKIE_DATA, + INVALID_COOKIE_SIZE) == 0) + { + reason = INVALID_COOKIE_DATA; + } + else if (size >= NO_AUTH_PROTO_SIZE + 8 && + memcmp(buffer + 8, NO_AUTH_PROTO_DATA, + NO_AUTH_PROTO_SIZE) == 0) + { + reason = NO_AUTH_PROTO_DATA; + } + else + { + reason = "Unknown"; + } + + #ifdef WARNING + *logofs << "handleAuthorization: WARNING! X connection failed " + << "with error '" << reason << "' on FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + cerr << "Warning" << ": X connection failed " + << "with error '" << reason << "'.\n"; + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "handleAuthorization: X connection successful " + << "on FD#" << fd_ << ".\n" << logofs_flush; + } + #endif + + return 1; +} + +// +// Use a simple encoding. Need to handle the image +// requests in the usual way and the X_ListExtensions +// and X_QueryExtension to hide MIT-SHM and RENDER +// in the reply. +// + +int ServerChannel::handleFastWriteRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // All the NX requests are handled in the + // main message loop. The X_PutImage can + // be handled here only if a split was + // not requested. + // + + if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) || + (control -> isProtoStep7() == 1 && opcode == X_PutImage && + splitState_.resource != nothing) || opcode == X_ListExtensions || + opcode == X_QueryExtension) + { + return 0; + } + + #ifdef DEBUG + *logofs << "handleFastWriteRequest: Decoding raw request OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + buffer = writeBuffer_.addMessage(4); + + #ifndef __sun + + unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(4); + + *((unsigned int *) buffer) = *next; + + #else /* #ifndef __sun */ + + memcpy(buffer, decodeBuffer.decodeMemory(4), 4); + + #endif /* #ifndef __sun */ + + size = GetUINT(buffer + 2, bigEndian_) << 2; + + if (size < 4) + { + #ifdef WARNING + *logofs << "handleFastWriteRequest: WARNING! Assuming size 4 " + << "for suspicious message of size " << size + << ".\n" << logofs_flush; + #endif + + size = 4; + } + + writeBuffer_.registerPointer(&buffer); + + if (writeBuffer_.getAvailable() < size - 4 || + (int) size >= control -> TransportFlushBufferSize) + { + #ifdef DEBUG + *logofs << "handleFastWriteRequest: Using scratch buffer for OPCODE#" + << (unsigned int) opcode << " with size " << size << " and " + << writeBuffer_.getLength() << " bytes in buffer.\n" + << logofs_flush; + #endif + + // + // The procedure moving data to shared memory + // assumes that the full message is stored in + // the scratch buffer. We can safely let the + // scratch buffer inherit the decode buffer + // at the next offset. + // + + writeBuffer_.removeMessage(4); + + buffer = writeBuffer_.addScratchMessage(((unsigned char *) + decodeBuffer.decodeMemory(size - 4)) - 4, size); + } + else + { + writeBuffer_.addMessage(size - 4); + + #ifndef __sun + + if (size <= 32) + { + next = (unsigned int *) decodeBuffer.decodeMemory(size - 4); + + for (unsigned int i = 4; i < size; i += sizeof(unsigned int)) + { + *((unsigned int *) (buffer + i)) = *next++; + } + } + else + { + memcpy(buffer + 4, decodeBuffer.decodeMemory(size - 4), size - 4); + } + + #else /* #ifndef __sun */ + + memcpy(buffer + 4, decodeBuffer.decodeMemory(size - 4), size - 4); + + #endif /* #ifndef __sun */ + } + + // + // Opcode could have been tainted by the client + // proxy. Replace the original opcode with the + // one sent in the decode buffer. + // + + *buffer = opcode; + + writeBuffer_.unregisterPointer(); + + if (opcode == X_PutImage) + { + handleImage(opcode, buffer, size); + } + + #if defined(TEST) || defined(OPCODES) + + if (opcode != 0) + { + *logofs << "handleFastWriteRequest: Handled request " + << "OPCODE#" << (unsigned int) opcode << " (" + << DumpOpcode(opcode) << ") for FD#" << fd_ + << " sequence " << clientSequence_ << ". " + << size << " bytes out.\n" << logofs_flush; + } + else + { + *logofs << "handleFastWriteRequest: Handled image or " + << "other request for FD#" << fd_ + << " sequence " << clientSequence_ << ". " + << size << " bytes out.\n" << logofs_flush; + } + + #endif + + handleFlush(flush_if_needed); + + return 1; +} + +// +// Use the simplest encoding except for replies that +// need to be managed some way. +// + +int ServerChannel::handleFastReadReply(EncodeBuffer &encodeBuffer, const unsigned char &opcode, + const unsigned char *&buffer, const unsigned int &size) +{ + // + // If we pushed a X_GetInputFocus in the sequence + // queue this means that the original message was + // a NX request for which we have to provide a NX + // reply. + // + + if ((opcode >= X_NXFirstOpcode && + opcode <= X_NXLastOpcode) || + opcode == X_QueryExtension || + opcode == X_ListExtensions || + opcode == X_GetInputFocus) + { + return 0; + } + + #ifdef DEBUG + *logofs << "handleFastReadReply: Encoding raw reply OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << " with size " << size << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeMemory(buffer, size); + + // + // Send back the reply as soon + // as possible. + // + + priority_++; + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + *logofs << "handleFastReadReply: Handled raw reply OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ << " sequence " + << serverSequence_ << ". " << size << " bytes in, " + << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + #endif + + statistics -> addReplyBits(opcode, size << 3, bits); + + return 1; +} + +int ServerChannel::handleFastReadEvent(EncodeBuffer &encodeBuffer, const unsigned char &opcode, + const unsigned char *&buffer, const unsigned int &size) +{ + #ifdef DEBUG + *logofs << "handleFastReadEvent: Encoding raw " + << (opcode == X_Error ? "error" : "event") << " OPCODE#" + << (unsigned int) opcode << " for FD#" << fd_ + << " with size " << size << ".\n" + << logofs_flush; + #endif + + encodeBuffer.encodeMemory(buffer, size); + + switch (opcode) + { + case X_Error: + case ButtonPress: + case ButtonRelease: + case KeyPress: + case KeyRelease: + { + priority_++; + } + } + + int bits = encodeBuffer.diffBits(); + + #if defined(TEST) || defined(OPCODES) + + if (opcode == X_Error) + { + unsigned char code = *(buffer + 1); + + *logofs << "handleFastReadEvent: Handled error ERR_CODE#" + << (unsigned int) code << " for FD#" << fd_; + + *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + *logofs << " sequence " << serverSequence_ << ". " << size + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + else + { + *logofs << "handleFastReadEvent: Handled event OPCODE#" + << (unsigned int) *buffer << " for FD#" << fd_ + << " sequence " << serverSequence_ << ". " << size + << " bytes in, " << bits << " bits (" << ((float) bits) / 8 + << " bytes) out.\n" << logofs_flush; + } + + #endif + + statistics -> addEventBits(opcode, size << 3, bits); + + return 1; +} + +void ServerChannel::initCommitQueue() +{ + #ifdef TEST + *logofs << "initCommitQueue: Resetting the queue of split commits " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE; i++) + { + commitSequenceQueue_[i] = 0; + } +} + +void ServerChannel::updateCommitQueue(unsigned short sequence) +{ + for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE - 1; i++) + { + commitSequenceQueue_[i + 1] = commitSequenceQueue_[i]; + } + + #ifdef TEST + *logofs << "updateCommitQueue: Saved " << sequence + << " as last sequence number of image to commit.\n" + << logofs_flush; + #endif + + commitSequenceQueue_[0] = sequence; +} + +int ServerChannel::checkCommitError(unsigned char error, unsigned short sequence, + const unsigned char *buffer) +{ + // + // Check if error is due to an image commit + // generated at the end of a split. + // + // TODO: It should zero the head of the list + // when an event comes with a sequence number + // greater than the value of the last element + // added. + // + + for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE && + commitSequenceQueue_[i] != 0; i++) + { + #ifdef TEST + *logofs << "checkCommitError: Checking committed image's " + << "sequence number " << commitSequenceQueue_[i] + << " with input sequence " << sequence << ".\n" + << logofs_flush; + #endif + + if (commitSequenceQueue_[i] == sequence) + { + #ifdef WARNING + + *logofs << "checkCommitError: WARNING! Failed operation for " + << "FD#" << fd_ << " with ERR_CODE#" + << (unsigned int) *(buffer + 1); + + *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + *logofs << " sequence " << sequence << ".\n"; + + *logofs << logofs_flush; + + #endif + + cerr << "Warning" << ": Failed commit operation " + << "with ERR_CODE#" << (unsigned int) error; + + cerr << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + cerr << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + cerr << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + cerr << ".\n"; + + #ifdef WARNING + *logofs << "checkCommitError: WARNING! Suppressing error on " + << "OPCODE#" << (unsigned int) opcodeStore_ -> commitSplit + << " for FD#" << fd_ << " with sequence " << sequence + << " at position " << i << ".\n" << logofs_flush; + #endif + + return 0; + } + } + + return 0; +} + +// +// Check if the user pressed the CTRL+ALT+SHIFT+ESC +// keystroke. At the present moment it uses different +// keycodes based on the client OS. This should be +// implemented in a way that is platform independent +// (that's not an easy task, considered that we don't +// have access to the higher level X libraries). +// + +int ServerChannel::checkKeyboardEvent(unsigned char event, unsigned short sequence, + const unsigned char *buffer) +{ + #ifdef TEST + *logofs << "checkKeyboardEvent: Checking escape sequence with byte [1] " + << (void *) ((unsigned) *(buffer + 1)) << " and bytes [28-29] " + << (void *) ((unsigned) GetUINT(buffer + 28, bigEndian_)) + << " for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + #ifdef __APPLE__ + + int alert = (*(buffer + 1) == 0x3d && + GetUINT(buffer + 28, bigEndian_) == 0x2005); + + #else + + int alert = (*(buffer + 1) == 0x09 && + ((GetUINT(buffer + 28, bigEndian_) & + 0x0d) == 0x0d)); + + #endif + + if (alert == 1) + { + #ifdef PANIC + *logofs << "checkKeyboardEvent: PANIC! Received sequence " + << "CTRL+ALT+SHIFT+ESC " << "for FD#"<< fd_ + << ". Showing the abort dialog.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Received sequence CTRL+ALT+SHIFT+ESC. " + << "Showing the abort dialog.\n"; + + HandleAlert(CLOSE_UNRESPONSIVE_X_SERVER_ALERT, 1); + } + + return alert; +} + +// +// Handle the MIT-SHM initialization +// messages exchanged with the remote +// proxy. +// + +int ServerChannel::handleShmemReply(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned int stage, const unsigned char *buffer, + const unsigned int size) +{ + #ifdef TEST + *logofs << "handleShmemReply: Returning shmem reply for " + << "stage " << stage << ".\n" << logofs_flush; + #endif + + if (opcode == X_QueryExtension) + { + encodeBuffer.encodeValue(stage, 2); + + shmemState_ -> present = *(buffer + 8); + shmemState_ -> opcode = *(buffer + 9); + shmemState_ -> event = *(buffer + 10); + shmemState_ -> error = *(buffer + 11); + + #ifdef TEST + *logofs << "handleShmemReply: Extension present is " + << shmemState_ -> present << " with base OPCODE#" + << (unsigned int) shmemState_ -> opcode << " base event " + << (unsigned int) shmemState_ -> event << " base error " + << (unsigned int) shmemState_ -> error << ".\n" + << logofs_flush; + #endif + } + else if (opcode == X_GetInputFocus) + { + encodeBuffer.encodeValue(stage, 2); + + encodeBuffer.encodeBoolValue(0); + + if (shmemState_ -> present == 1 && + shmemState_ -> address != NULL && + shmemState_ -> segment > 0 && + shmemState_ -> id > 0) + { + cerr << "Info" << ": Using shared memory parameters 1/" + << (shmemState_ -> size / 1024) << "K.\n"; + + shmemState_ -> enabled = 1; + + encodeBuffer.encodeBoolValue(1); + } + else + { + #ifdef TEST + *logofs << "handleShmemReply: WARNING! Not using shared memory " + << "support in X server for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Info" << ": Using shared memory parameters 0/0K.\n"; + + handleShmemStateRemove(); + + encodeBuffer.encodeBoolValue(0); + } + } + else + { + #ifdef PANIC + *logofs << "handleShmemReply: PANIC! Conversation error " + << "handling shared memory support for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Conversation error handling " + << "shared memory support.\n"; + + return -1; + } + + return 1; +} + +int ServerChannel::handleShmemRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // We need to query and initialize MIT-SHM on + // the real X server. To do this we'll need 3 + // requests. At the end we'll have to encode + // the final reply for the X client side. + // + + handleShmemStateAlloc(); + + unsigned int stage; + + decodeBuffer.decodeValue(stage, 2); + + unsigned int expected = shmemState_ -> stage + 1; + + if (stage != expected || stage > 2) + { + #ifdef PANIC + *logofs << "handleShmemRequest: PANIC! Unexpected stage " + << stage << " in handling shared memory " + << "support for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Unexpected stage " + << stage << " in handling shared memory " + << "support for FD#" << fd_ << ".\n"; + + return -1; + } + + switch (stage) + { + case 0: + { + unsigned int enableClient; + unsigned int enableServer; + + decodeBuffer.decodeBoolValue(enableClient); + decodeBuffer.decodeBoolValue(enableServer); + + unsigned int clientSegment; + unsigned int serverSegment; + + decodeBuffer.decodeValue(clientSegment, 29, 9); + decodeBuffer.decodeValue(serverSegment, 29, 9); + + shmemState_ -> segment = serverSegment; + + #ifdef TEST + *logofs << "handleShmemRequest: Size of the shared memory " + << "segment will be " << control -> ShmemServerSize + << ".\n" << logofs_flush; + #endif + + #ifdef TEST + *logofs << "handleShmemRequest: Sending X_QueryExtension request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getShmemParameters << " in stage " + << stage << ".\n" << logofs_flush; + #endif + + opcode = X_QueryExtension; + + size = 16; + buffer = writeBuffer_.addMessage(size); + + PutUINT(7, buffer + 4, bigEndian_); + + // + // Simply make the query fail if shared + // memory support is disabled by the + // user. + // + + if (control -> ShmemServer == 1 && + control -> ShmemServerSize > 0 && + enableServer == 1) + { + memcpy(buffer + 8, "MIT-SHM", 7); + } + else + { + memcpy(buffer + 8, "NO-MIT-", 7); + } + + sequenceQueue_.push(clientSequence_, opcode, + opcodeStore_ -> getShmemParameters, stage); + + // + // Save the sequence number so we can + // later identify any matching X error + // received from server. + // + + shmemState_ -> sequence = clientSequence_; + + break; + } + case 1: + { + if (shmemState_ -> present == 1) + { + // + // Make the segment read-write for everybody on + // Cygwin (to avoid any lack of support or any + // performance issue) and on MacOS/X (where the + // 0600 mask doesn't seem to work). + // + + #if defined(__CYGWIN32__) || defined(__APPLE__) + + int permissions = 0777; + + #else + + int permissions = 0600; + + #endif + + shmemState_ -> size = control -> ShmemServerSize; + + shmemState_ -> id = shmget(IPC_PRIVATE, shmemState_ -> size, + IPC_CREAT | permissions); + + if (shmemState_ -> id >= 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemRequest: Allocated shared memory " + << "segment of " << shmemState_ -> size + << " bytes with id " << shmemState_ -> id + << ".\n" << logofs_flush; + #endif + + + shmemState_ -> address = shmat(shmemState_ -> id, 0, 0); + + if (shmemState_ -> address != NULL) + { + #ifdef TEST + *logofs << "handleShmemRequest: Sending X_ShmAttach request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getShmemParameters << " in stage " + << stage << ".\n" << logofs_flush; + #endif + + opcode = shmemState_ -> opcode; + + size = 16; + buffer = writeBuffer_.addMessage(size); + + *(buffer + 1) = X_ShmAttach; + + PutULONG(shmemState_ -> segment, buffer + 4, bigEndian_); + PutULONG(shmemState_ -> id, buffer + 8, bigEndian_); + + *(buffer + 12) = 1; + + shmemState_ -> sequence = clientSequence_; + + break; + } + else + { + #ifdef WARNING + *logofs << "handleShmemRequest: WARNING! Can't attach the shared " + << "memory segment. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Can't attach the shared memory " + << "segment. Error is " << EGET() << " '" + << ESTR() << "'.\n"; + } + } + else + { + #ifndef __CYGWIN32__ + + #ifdef WARNING + *logofs << "handleShmemRequest: WARNING! Can't create the shared " + << "memory segment. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Can't create the shared memory " + << "segment. Error is " << EGET() << " '" + << ESTR() << "'.\n"; + + #else + + #ifdef TEST + *logofs << "handleShmemRequest: WARNING! Can't create the shared " + << "memory segment. Error is " << EGET() << " '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + #endif + } + } + + if (shmemState_ -> present != 0) + { + #ifdef TEST + *logofs << "handleShmemRequest: Resetting shared memory " + << "presence flag for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + shmemState_ -> present = 0; + } + + handleNullRequest(opcode, buffer, size); + + break; + } + default: + { + #ifdef TEST + *logofs << "handleShmemRequest: Sending X_GetInputFocus request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getShmemParameters << " in stage " + << stage << ".\n" << logofs_flush; + #endif + + opcode = X_GetInputFocus; + + size = 4; + buffer = writeBuffer_.addMessage(size); + + sequenceQueue_.push(clientSequence_, opcode, + opcodeStore_ -> getShmemParameters, stage); + break; + } + } + + shmemState_ -> stage += 1; + + return 1; +} + +// +// Handling of MIT-SHM extension has been plugged late in +// the design, so we have to make some assumptions. Image +// is a X_PutImage request contained either in the scratch +// buffer or in the normal write buffer. We need to move +// the image data to the shared memory segment and replace +// the X_PutImage request with a X_ShmPutImage. +// + +int ServerChannel::handleShmem(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size) +{ + if (shmemState_ == NULL || shmemState_ -> enabled != 1) + { + #ifdef TEST + + if (shmemState_ != NULL) + { + *logofs << "handleShmem: PANIC! Shared memory " + << "state found but support is not enabled " + << "for FD#" << fd_ << " in stage " + << shmemState_ -> stage << ".\n" + << logofs_flush; + } + + #endif + + return 0; + } + + // + // Ignore null requests and requests that will not result + // in a single X_PutImage. To conform with the other func- + // tions, we get the opcode passed as a parameter. It can + // be zero if we don't want the write loop to put opcode + // and length in the resulting buffer. Anyway we are only + // interested in the original opcode of the request, that + // is stored in the image state. + // + + unsigned char *dstData = buffer + 24; + unsigned int dstDataSize = size - 24; + + if (dstDataSize == 0 || dstDataSize > + (unsigned int) control -> MaximumRequestSize) + { + #ifdef TEST + *logofs << "handleShmem: Ignoring image with opcode " + << (unsigned int) imageState_ -> opcode + << " and size " << size << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + return 0; + } + + #ifdef TEST + *logofs << "handleShmem: Handling image with opcode " + << (unsigned int) imageState_ -> opcode + << " and size " << size << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Get image data from buffer. + // + + if (imageState_ -> opcode == X_PutImage) + { + // + // We still need to get the image's data. + // + + imageState_ -> format = *(buffer + 1); + + imageState_ -> drawable = GetULONG(buffer + 4, bigEndian_); + imageState_ -> gcontext = GetULONG(buffer + 8, bigEndian_); + + imageState_ -> dstWidth = GetUINT(buffer + 12, bigEndian_); + imageState_ -> dstHeight = GetUINT(buffer + 14, bigEndian_); + + imageState_ -> srcX = 0; + imageState_ -> srcY = 0; + + imageState_ -> srcWidth = imageState_ -> dstWidth; + imageState_ -> srcHeight = imageState_ -> dstHeight; + + imageState_ -> dstX = GetUINT(buffer + 16, bigEndian_); + imageState_ -> dstY = GetUINT(buffer + 18, bigEndian_); + + imageState_ -> leftPad = *(buffer + 20); + imageState_ -> dstDepth = *(buffer + 21); + + imageState_ -> dstLines = imageState_ -> dstHeight; + + imageState_ -> dstLength = size - 24; + } + + // + // Skip the MIT-SHM operation if the image + // is 1 bits-per-plane. + // + + if (imageState_ -> dstDepth == 1) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmem: Ignoring image with opcode " + << (unsigned int) imageState_ -> opcode << " depth " + << (unsigned int) imageState_ -> dstDepth << " and " + << "size " << size << " for FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + return 0; + } + + // + // If the image can't fit in the available + // space, check if the completion event is + // arrived. + // + + #if defined(TEST) || defined(INFO) + + if (isTimestamp(shmemState_ -> last) == 0 && + shmemState_ -> offset != 0) + { + *logofs << "handleShmem: PANIC! No timestamp for sequence " + << shmemState_ -> sequence << " with offset " + << shmemState_ -> offset << ".\n" + << logofs_flush; + } + + #endif + + if (shmemState_ -> offset + imageState_ -> dstLength > + shmemState_ -> size) + { + if (imageState_ -> dstLength > shmemState_ -> size) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmem: WARNING! Can't fit the image " + << "in the available memory segment for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (handleShmemEvent() <= 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmem: WARNING! Missing completion " + << "after " << diffTimestamp(shmemState_ -> last, + getTimestamp()) << " Ms for shared memory " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + return 0; + } + } + + // + // Let image start at current offset + // in the shared segment. + // + + #ifdef TEST + *logofs << "handleShmem: Copying " << dstDataSize + << " bytes to shared memory at offset " + << shmemState_ -> offset << " for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + memcpy((unsigned char *) shmemState_ -> address + + shmemState_ -> offset, dstData, dstDataSize); + + // + // Get rid of the original X_PutImage + // request. + // + + if (writeBuffer_.getScratchData() != NULL) + { + writeBuffer_.removeScratchMessage(); + } + else + { + writeBuffer_.removeMessage(size); + } + + // + // Add a X_ShmPutImage request to the + // write buffer. + // + + buffer = writeBuffer_.addMessage(40); + + *buffer = shmemState_ -> opcode; + + *(buffer + 1) = X_ShmPutImage; + + PutUINT(40 >> 2, buffer + 2, bigEndian_); + + PutULONG(imageState_ -> drawable, buffer + 4, bigEndian_); + PutULONG(imageState_ -> gcontext, buffer + 8, bigEndian_); + + PutUINT(imageState_ -> dstWidth, buffer + 12, bigEndian_); + PutUINT(imageState_ -> dstLines, buffer + 14, bigEndian_); + + PutUINT(imageState_ -> srcX, buffer + 16, bigEndian_); + PutUINT(imageState_ -> srcY, buffer + 18, bigEndian_); + + PutUINT(imageState_ -> dstWidth, buffer + 20, bigEndian_); + PutUINT(imageState_ -> dstLines, buffer + 22, bigEndian_); + + PutUINT(imageState_ -> dstX, buffer + 24, bigEndian_); + PutUINT(imageState_ -> dstY, buffer + 26, bigEndian_); + + *(buffer + 28) = imageState_ -> dstDepth; + *(buffer + 29) = imageState_ -> format; + *(buffer + 30) = 1; + + PutULONG(shmemState_ -> segment, buffer + 32, bigEndian_); + PutULONG(shmemState_ -> offset, buffer + 36, bigEndian_); + + shmemState_ -> offset += dstDataSize; + + shmemState_ -> sequence = clientSequence_; + shmemState_ -> last = getTimestamp(); + + #ifdef TEST + *logofs << "handleShmem: Saved shared memory sequence " + << shmemState_ -> sequence << " for FD#" << fd_ + << " with offset " << shmemState_ -> offset + << " at " << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + + // + // Make the X server read immediately + // from the shared memory buffer and + // produce the completion event. + // + + handleFlush(flush_if_any); + + return 1; +} + +// +// Try to read more events from the socket in the +// attempt to get the completion event required +// to reset the MIT-SHM segment. +// + +int ServerChannel::handleShmemEvent() +{ + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemEvent: Waiting for shared memory " + << "sequence " << shmemState_ -> sequence + << " for X server FD#" << fd_ << ".\n" + << logofs_flush; + + T_timestamp startTs = getTimestamp(); + + #endif + + while (isTimestamp(shmemState_ -> last) != 0) + { + if (handleWait(control -> ShmemTimeout) <= 0) + { + break; + } + #if defined(TEST) || defined(INFO) + else + { + *logofs << "handleShmemEvent: WARNING! Encoded events " + << "for FD#" << fd_ << " at " << strMsTimestamp() + << ".\n" << logofs_flush; + } + #endif + } + + if (isTimestamp(shmemState_ -> last) == 0) + { + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemEvent: Spent " + << diffTimestamp(startTs, getTimestamp()) << " Ms " + << "waiting for shared memory sequence for FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + return 1; + } + + #if defined(TEST) || defined(INFO) + *logofs << "handleShmemEvent: WARNING! Can't reset shared " + << "memory sequence for FD#" << fd_ << " after " + << diffTimestamp(shmemState_ -> last, getTimestamp()) + << " Ms.\n" << logofs_flush; + #endif + + return 0; +} + +int ServerChannel::checkShmemEvent(unsigned char event, unsigned short sequence, + const unsigned char *buffer) +{ + if (isTimestamp(shmemState_ -> last) == 1 && + sequence == shmemState_ -> sequence) + { + #ifdef TEST + *logofs << "checkShmemEvent: Reset shared memory sequence " + << shmemState_ -> sequence << " for FD#" << fd_ + << " after " << diffTimestamp(shmemState_ -> last, + getTimestamp()) << " Ms.\n" << logofs_flush; + #endif + + shmemState_ -> sequence = 0; + shmemState_ -> offset = 0; + shmemState_ -> last = nullTimestamp(); + } + #ifdef TEST + else + { + *logofs << "checkShmemEvent: Skipping past shared memory " + << "image sequence " << sequence << " for FD#" + << fd_ << ".\n" << logofs_flush; + } + #endif + + return 1; +} + +int ServerChannel::checkShmemError(unsigned char error, unsigned short sequence, + const unsigned char *buffer) +{ + #ifdef TEST + + *logofs << "checkShmemError: WARNING! Failed operation for " + << "FD#" << fd_ << " in stage " << shmemState_ -> stage + << " with ERR_CODE#" << (unsigned int) *(buffer + 1); + + *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_); + + *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_); + + *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10); + + *logofs << " sequence " << sequence << ".\n"; + + *logofs << logofs_flush; + + #endif + + // + // If enabled flag is <= 0 we are still + // in the inizialization phase. In this + // case force presence to false. + // + + if (shmemState_ -> enabled != 1) + { + if (shmemState_ -> present != 0) + { + #ifdef TEST + *logofs << "checkShmemError: Resetting shared memory " + << "presence flag for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + shmemState_ -> present = 0; + } + + return 0; + } + + if (shmemState_ -> sequence == sequence) + { + // + // Reset the sequence and timestamp. + // + + shmemState_ -> sequence = 0; + shmemState_ -> offset = 0; + shmemState_ -> last = nullTimestamp(); + } + + return 1; +} + +int ServerChannel::handleFontRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Send a synchronization request and use + // the reply to return the requested font + // path. + // + + #ifdef TEST + *logofs << "handleFontRequest: Sending X_GetInputFocus request " + << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int) + opcodeStore_ -> getFontParameters << ".\n" + << logofs_flush; + #endif + + opcode = X_GetInputFocus; + + size = 4; + buffer = writeBuffer_.addMessage(size); + + sequenceQueue_.push(clientSequence_, X_GetInputFocus, + opcodeStore_ -> getFontParameters); + + return 1; +} + +int ServerChannel::handleFontReply(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) +{ + #ifdef TEST + *logofs << "handleFontReply: Encoding font operation " + << "reply with size " << size << ".\n" + << logofs_flush; + #endif + + char data[256]; + + if (fontPort_ != -1) + { + sprintf(data + 1, "tcp/localhost:%d", fontPort_); + } + else + { + *(data + 1) = '\0'; + } + + *data = strlen(data + 1); + + unsigned char *next = (unsigned char *) data; + + unsigned int length = (unsigned int) (*next++); + + encodeBuffer.encodeValue(length, 8); + + encodeBuffer.encodeTextData(next, length); + + return 1; +} + +int ServerChannel::handleCacheRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + unsigned int mask; + + decodeBuffer.decodeCachedValue(mask, 32, clientCache_ -> + setCacheParametersCache); + + splitState_.save = (mask >> 8) & 0xff; + splitState_.load = mask & 0xff; + + // + // Just to be sure. We should never + // receive this request if connected + // to an old proxy version. + // + + handleSplitEnable(); + + #ifdef TEST + *logofs << "handleCacheRequest: Set cache parameters to " + << "save " << splitState_.save << " load " + << splitState_.load << ".\n" << logofs_flush; + #endif + + handleNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleStartSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Prepare for the split for the selected + // resource. Old proxy versions only use + // the split store at position 0. + // + + if (control -> isProtoStep7() == 1) + { + unsigned char resource; + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + splitState_.resource = resource; + + splitState_.current = splitState_.resource; + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleStartSplitRequest: SPLIT! Registered id " + << splitState_.resource << " as resource " + << "waiting for a split.\n" << logofs_flush; + #endif + } + #if defined(TEST) || defined(SPLIT) + else + { + *logofs << "handleStartSplitRequest: SPLIT! Assuming fake id " + << splitState_.current << " as resource " + << "waiting for a split.\n" << logofs_flush; + } + #endif + + handleNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleEndSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size) +{ + // + // Verify that the agent resource matches. + // + + if (control -> isProtoStep7() == 1) + { + unsigned char resource; + + decodeBuffer.decodeCachedValue(resource, 8, + clientCache_ -> resourceCache); + + #ifdef TEST + + if (splitState_.resource == nothing) + { + #ifdef PANIC + *logofs << "handleEndSplitRequest: PANIC! SPLIT! Received an end of " + << "split for resource id " << (unsigned int) *(buffer + 1) + << " without a previous start.\n" + << logofs_flush; + #endif + + HandleCleanup(); + } + else if (resource != splitState_.resource) + { + #ifdef PANIC + *logofs << "handleEndSplitRequest: PANIC! SPLIT! Invalid resource id " + << resource << " received while waiting for resource id " + << splitState_.resource << ".\n" << logofs_flush; + #endif + + HandleCleanup(); + } + + #endif + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleEndSplitRequest: SPLIT! Reset id " + << splitState_.resource << " as resource " + << "selected for splits.\n" << logofs_flush; + #endif + + splitState_.resource = nothing; + + handleNullRequest(opcode, buffer, size); + + return 1; +} + +int ServerChannel::handleSplitChecksum(DecodeBuffer &decodeBuffer, T_checksum &checksum) +{ + unsigned int receive; + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeBoolValue(receive); + } + else + { + receive = (control -> ImageCacheEnableLoad == 1 || + control -> ImageCacheEnableSave == 1); + } + + if (receive == 1) + { + checksum = new md5_byte_t[MD5_LENGTH]; + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + decodeBuffer.decodeValue(receive, 8); + + if (checksum != NULL) + { + checksum[i] = (unsigned char) receive; + } + } + + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitChecksum: SPLIT! Received checksum " + << "[" << DumpChecksum(checksum) << "].\n" + << logofs_flush; + #endif + + return 1; + } + + return 0; +} + +void ServerChannel::handleShmemStateAlloc() +{ + if (shmemState_ == NULL) + { + shmemState_ = new T_shmem_state(); + + shmemState_ -> stage = -1; + shmemState_ -> present = -1; + shmemState_ -> enabled = -1; + + shmemState_ -> segment = -1; + shmemState_ -> id = -1; + shmemState_ -> address = NULL; + shmemState_ -> size = 0; + + shmemState_ -> opcode = 0xff; + shmemState_ -> event = 0xff; + shmemState_ -> error = 0xff; + + shmemState_ -> sequence = 0; + shmemState_ -> offset = 0; + shmemState_ -> last = nullTimestamp(); + + shmemState_ -> checked = 0; + } +} + +void ServerChannel::handleShmemStateRemove() +{ + if (shmemState_ != NULL) + { + if (shmemState_ -> address != NULL) + { + shmdt((char *) shmemState_ -> address); + } + + if (shmemState_ -> id > 0) + { + shmctl(shmemState_ -> id, IPC_RMID, 0); + } + + delete shmemState_; + + shmemState_ = NULL; + } +} + +void ServerChannel::handleUnpackStateInit(int resource) +{ + if (unpackState_[resource] == NULL) + { + unpackState_[resource] = new T_unpack_state(); + + if (unpackState_[resource] == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackStateInit: PANIC! Can't allocate " + << "memory for unpack state in context [A].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [A].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> geometry = NULL; + unpackState_[resource] -> colormap = NULL; + unpackState_[resource] -> alpha = NULL; + } +} + +void ServerChannel::handleUnpackAllocGeometry(int resource) +{ + if (unpackState_[resource] -> geometry == NULL) + { + unpackState_[resource] -> geometry = new T_geometry(); + + if (unpackState_[resource] -> geometry == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackAllocGeometry: PANIC! Can't allocate " + << "memory for unpack state in context [B].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [B].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> geometry -> depth1_bpp = 4; + unpackState_[resource] -> geometry -> depth4_bpp = 4; + unpackState_[resource] -> geometry -> depth8_bpp = 8; + unpackState_[resource] -> geometry -> depth16_bpp = 16; + unpackState_[resource] -> geometry -> depth24_bpp = 32; + unpackState_[resource] -> geometry -> depth32_bpp = 32; + + unpackState_[resource] -> geometry -> red_mask = 0xff0000; + unpackState_[resource] -> geometry -> green_mask = 0x00ff00; + unpackState_[resource] -> geometry -> blue_mask = 0x0000ff; + + unpackState_[resource] -> geometry -> image_byte_order = imageByteOrder_; + unpackState_[resource] -> geometry -> bitmap_bit_order = bitmapBitOrder_; + unpackState_[resource] -> geometry -> scanline_unit = scanlineUnit_; + unpackState_[resource] -> geometry -> scanline_pad = scanlinePad_; + } +} + +void ServerChannel::handleUnpackAllocColormap(int resource) +{ + if (unpackState_[resource] -> colormap == NULL) + { + unpackState_[resource] -> colormap = new T_colormap(); + + if (unpackState_[resource] -> colormap == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackAllocColormap: PANIC! Can't allocate " + << "memory for unpack state in context [C].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [C].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> colormap -> entries = 0; + unpackState_[resource] -> colormap -> data = NULL; + } +} + +void ServerChannel::handleUnpackAllocAlpha(int resource) +{ + if (unpackState_[resource] -> alpha == NULL) + { + unpackState_[resource] -> alpha = new T_alpha(); + + if (unpackState_[resource] -> alpha == NULL) + { + #ifdef PANIC + *logofs << "handleUnpackAllocAlpha: PANIC! Can't allocate " + << "memory for unpack state in context [D].\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't allocate memory for " + << "unpack state in context [D].\n"; + + HandleAbort(); + } + + unpackState_[resource] -> alpha -> entries = 0; + unpackState_[resource] -> alpha -> data = NULL; + } +} + +void ServerChannel::handleUnpackStateRemove(int resource) +{ + if (unpackState_[resource] != NULL) + { + delete unpackState_[resource] -> geometry; + + if (unpackState_[resource] -> colormap != NULL) + { + delete [] unpackState_[resource] -> colormap -> data; + } + + delete unpackState_[resource] -> colormap; + + if (unpackState_[resource] -> alpha != NULL) + { + delete [] unpackState_[resource] -> alpha -> data; + } + + delete unpackState_[resource] -> alpha; + + delete unpackState_[resource]; + + unpackState_[resource] = NULL; + } +} + +void ServerChannel::handleEncodeCharInfo(const unsigned char *nextSrc, EncodeBuffer &encodeBuffer) +{ + unsigned int value = GetUINT(nextSrc, bigEndian_) | + (GetUINT(nextSrc + 10, bigEndian_) << 16); + + encodeBuffer.encodeCachedValue(value, 32, + *serverCache_ -> queryFontCharInfoCache[0], 6); + + nextSrc += 2; + + for (unsigned int i = 1; i < 5; i++) + { + unsigned int value = GetUINT(nextSrc, bigEndian_); + + nextSrc += 2; + + encodeBuffer.encodeCachedValue(value, 16, + *serverCache_ -> queryFontCharInfoCache[i], 6); + } +} + +int ServerChannel::setBigEndian(int flag) +{ + bigEndian_ = flag; + + readBuffer_.setBigEndian(flag); + + return 1; +} + +int ServerChannel::setReferences() +{ + #ifdef TEST + *logofs << "ServerChannel: Initializing the static " + << "members for the server channels.\n" + << logofs_flush; + #endif + + #ifdef REFERENCES + + references_ = 0; + + #endif + + return 1; +} diff --git a/nxcomp/ServerChannel.h b/nxcomp/ServerChannel.h new file mode 100644 index 000000000..6536db829 --- /dev/null +++ b/nxcomp/ServerChannel.h @@ -0,0 +1,536 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ServerChannel_H +#define ServerChannel_H + +#include "List.h" +#include "Channel.h" + +#include "SequenceQueue.h" + +#include "ServerReadBuffer.h" + +#include "Unpack.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// How many sequence numbers of split commit +// requests we are going to save in order to +// mask errors. +// + +#define MAX_COMMIT_SEQUENCE_QUEUE 16 + +// +// Define this to know when a channel +// is created or destroyed. +// + +#undef REFERENCES + +// +// This class implements the X server +// side compression of X protocol. +// + +class ServerChannel : public Channel +{ + public: + + ServerChannel(Transport *transport, StaticCompressor *compressor); + + virtual ~ServerChannel(); + + virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message, + unsigned int length); + + virtual int handleWrite(const unsigned char *message, unsigned int length); + + virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store, + T_store_action action, int position, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size) + { + return 0; + } + + virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store, + T_store_action action, int position, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + virtual int handleSplit(EncodeBuffer &encodeBuffer) + { + return 0; + } + + virtual int handleSplit(DecodeBuffer &decodeBuffer); + + virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split); + + virtual int handleSplitEvent(DecodeBuffer &decodeBuffer) + { + return 0; + } + + // + // Send the last motion notify event + // received from the X server to the + // remote proxy. + // + + virtual int handleMotion(EncodeBuffer &encodeBuffer); + + virtual int handleCompletion(EncodeBuffer &encodeBuffer) + { + return 0; + } + + virtual int handleConfiguration(); + + virtual int handleFinish(); + + virtual int handleAsyncEvents(); + + virtual int needSplit() const + { + return 0; + } + + virtual int needMotion() const + { + return (lastMotion_[0] != '\0'); + } + + virtual T_channel_type getType() const + { + return channel_x11; + } + + int setBigEndian(int flag); + + // + // Initialize the static members. + // + + static int setReferences(); + + private: + + int handleFastReadReply(EncodeBuffer &encodeBuffer, const unsigned char &opcode, + const unsigned char *&buffer, const unsigned int &size); + + int handleFastReadEvent(EncodeBuffer &encodeBuffer, const unsigned char &opcode, + const unsigned char *&buffer, const unsigned int &size); + + int handleFastWriteRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + // + // Handle the fake authorization cookie + // and the X server's reply. + // + + int handleAuthorization(unsigned char *buffer); + int handleAuthorization(const unsigned char *buffer, int size); + + // + // Set the unpack colormap and the alpha + // blending data to be used to unpack + // images. + // + + int handleGeometry(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size); + + int handleColormap(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size); + + int handleAlpha(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size); + + // + // Manage the decoded buffer to unpack + // the image and move the data to the + // shared memory segment. + // + + int handleImage(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size); + + // + // Uncompress a packed image in one + // or more graphic X requests. + // + + int handleUnpack(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size); + + // + // Move the image to the shared + // memory buffer. + // + + int handleShmem(unsigned char &opcode, unsigned char *&buffer, + unsigned int &size); + + // + // Handle suppression of error on + // commit of image splits. + // + + void initCommitQueue(); + + void updateCommitQueue(unsigned short sequence); + + int checkCommitError(unsigned char error, unsigned short sequence, + const unsigned char *buffer); + + void clearCommitQueue() + { + if (commitSequenceQueue_[0] != 0) + { + initCommitQueue(); + } + } + + // + // Check if the user pressed the + // CTRL+ALT+SHIFT+ESC keystroke. + // + + int checkKeyboardEvent(unsigned char event, unsigned short sequence, + const unsigned char *buffer); + + // + // Other utilities. + // + + void handleEncodeCharInfo(const unsigned char *nextSrc, EncodeBuffer &encodeBuffer); + + // + // Handle the MIT-SHM initialization + // messages exchanged with the remote + // proxy. + // + + int handleShmemRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + + int handleShmemReply(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned int stage, const unsigned char *buffer, + const unsigned int size); + + // + // Try to read more events in the attempt to + // get the MIT-SHM image completion event + // from the X server. + // + + int handleShmemEvent(); + + // + // Handle the MIT-SHM events as they are read + // from the socket. + // + + int checkShmemEvent(unsigned char event, unsigned short sequence, + const unsigned char *buffer); + + int checkShmemError(unsigned char error, unsigned short sequence, + const unsigned char *buffer); + + // + // Query the port used to tunnel + // the font server connections. + // + + int handleFontRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + int handleFontReply(EncodeBuffer &encodeBuffer, const unsigned char opcode, + const unsigned char *buffer, const unsigned int size); + + // + // Set the cache policy for image + // requests. + // + + int handleCacheRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + // + // Decode the start and end split + // requests. + // + + int handleStartSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + int handleEndSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + // + // Remove the split store and the + // incomplete messages from the + // memory cache. + // + + int handleAbortSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + // + // Send the split requests to the + // X server once they have been + // recomposed. + // + + int handleCommitSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode, + unsigned char *&buffer, unsigned int &size); + + int handleSplitChecksum(DecodeBuffer &decodeBuffer, T_checksum &checksum); + + void handleSplitEnable() + { + if (control -> isProtoStep7() == 0) + { + #if defined(TEST) || defined(SPLIT) + *logofs << "handleSplitEnable: WARNING! Disabling load " + << "and save with an old proxy version.\n" + << logofs_flush; + #endif + + splitState_.save = 0; + splitState_.load = 0; + } + } + + // + // Allocate and free the shared memory + // support resources. + // + + void handleShmemStateAlloc(); + void handleShmemStateRemove(); + + // + // Temporary storage for the image info. + // + + void handleImageStateAlloc(unsigned char opcode) + { + if (imageState_ == NULL) + { + imageState_ = new T_image_state(); + } + + imageState_ -> opcode = opcode; + } + + void handleImageStateRemove() + { + if (imageState_ != NULL) + { + delete imageState_; + + imageState_ = NULL; + } + } + + // + // Store the information needed to unpack + // images per each known agent's client. + // + + void handleUnpackStateInit(int resource); + + void handleUnpackAllocGeometry(int resource); + void handleUnpackAllocColormap(int resource); + void handleUnpackAllocAlpha(int resource); + + void handleUnpackStateRemove(int resource); + + typedef struct + { + T_geometry *geometry; + T_colormap *colormap; + T_alpha *alpha; + + } T_unpack_state; + + T_unpack_state *unpackState_[256]; + + // + // Own read buffer. It is able to identify + // full messages read from X descriptor. + // + + ServerReadBuffer readBuffer_; + + // + // Sequence number of last request coming + // from X client or X server. + // + + unsigned int clientSequence_; + unsigned int serverSequence_; + + // + // Used to identify replies based on sequence + // number of original request. + // + + SequenceQueue sequenceQueue_; + + // + // Last motion notify read from the X server. + // + + unsigned char lastMotion_[32]; + + // + // Sequence numbers of last auto-generated + // put image requests. Needed to intercept + // and suppress errors generated by such + // requests. + // + + unsigned int commitSequenceQueue_[MAX_COMMIT_SEQUENCE_QUEUE]; + + // + // Let agent select which expose + // events is going to receive. + // + + unsigned int enableExpose_; + unsigned int enableGraphicsExpose_; + unsigned int enableNoExpose_; + + // + // Used in initialization and handling + // of MIT-SHM shared memory put images. + // + + typedef struct + { + int stage; + int present; + int enabled; + int segment; + int id; + void *address; + unsigned int size; + + unsigned char opcode; + unsigned char event; + unsigned char error; + + unsigned int sequence; + unsigned int offset; + T_timestamp last; + + unsigned int checked; + + } T_shmem_state; + + T_shmem_state *shmemState_; + + // + // Used to pass current image data between + // the different decompression stages. + // + + typedef struct + { + unsigned char opcode; + + unsigned int drawable; + unsigned int gcontext; + + unsigned char method; + + unsigned char format; + unsigned char srcDepth; + unsigned char dstDepth; + + unsigned int srcLength; + unsigned int dstLength; + unsigned int dstLines; + + short int srcX; + short int srcY; + unsigned short srcWidth; + unsigned short srcHeight; + + short int dstX; + short int dstY; + unsigned short dstWidth; + unsigned short dstHeight; + + unsigned char leftPad; + + } T_image_state; + + T_image_state *imageState_; + + // + // The flags is set according to the + // split load and save policy set by + // the encoding side. + // + + typedef struct + { + int resource; + int current; + int load; + int save; + int commit; + + } T_split_state; + + T_split_state splitState_; + + // + // List of agent resources. + // + + List splitResources_; + + // + // Keep track of object creation and + // deletion. + // + + private: + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +#endif /* ServerChannel_H */ diff --git a/nxcomp/ServerProxy.cpp b/nxcomp/ServerProxy.cpp new file mode 100644 index 000000000..0a72fc301 --- /dev/null +++ b/nxcomp/ServerProxy.cpp @@ -0,0 +1,605 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2011 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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_ = -1; + smbServerPort_ = -1; + mediaServerPort_ = -1; + httpServerPort_ = -1; + + fontServerPort_ = NULL; + + #ifdef DEBUG + *logofs << "ServerProxy: Created new object at " << this + << ".\n" << logofs_flush; + #endif +} + +ServerProxy::~ServerProxy() +{ + delete xServerAddr_; + + delete [] xServerDisplay_; + + delete [] fontServerPort_; + + #ifdef DEBUG + *logofs << "ServerProxy: Deleted object at " << this + << ".\n" << logofs_flush; + #endif +} + +void ServerProxy::handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily, + sockaddr *xServerAddr, unsigned int xServerAddrLength) +{ + delete xServerAddr_; + + xServerAddr_ = xServerAddr; + + xServerAddrFamily_ = xServerAddrFamily; + xServerAddrLength_ = xServerAddrLength; + + delete [] xServerDisplay_; + + xServerDisplay_ = new char[strlen(xServerDisplay) + 1]; + + strcpy(xServerDisplay_, xServerDisplay); + + #ifdef DEBUG + *logofs << "ServerProxy: Set display configuration to display '" + << xServerDisplay_ << "'.\n" + << logofs_flush; + #endif +} + +void ServerProxy::handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort, + int httpServerPort, const char *fontServerPort) +{ + cupsServerPort_ = cupsServerPort; + smbServerPort_ = smbServerPort; + mediaServerPort_ = mediaServerPort; + httpServerPort_ = httpServerPort; + + delete [] fontServerPort_; + + fontServerPort_ = new char[strlen(fontServerPort) + 1]; + + strcpy(fontServerPort_, fontServerPort); + + #ifdef DEBUG + *logofs << "ServerProxy: Set port configuration to CUPS " + << cupsServerPort_ << ", SMB " << smbServerPort_ + << ", media " << mediaServerPort_ << ", HTTP " + << httpServerPort_ << ".\n" + << logofs_flush; + #endif +} + +int ServerProxy::handleNewConnection(T_channel_type type, int clientFd) +{ + switch (type) + { + case channel_font: + { + return handleNewGenericConnection(clientFd, channel_font, "font"); + } + case channel_slave: + { + return handleNewSlaveConnection(clientFd); + } + default: + { + #ifdef PANIC + *logofs << "ServerProxy: PANIC! Unsupported channel with type '" + << getTypeName(type) << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unsupported channel with type '" + << getTypeName(type) << "'.\n"; + + return -1; + } + } +} + +int ServerProxy::handleNewConnectionFromProxy(T_channel_type type, int channelId) +{ + switch (type) + { + case channel_x11: + { + return handleNewXConnectionFromProxy(channelId); + } + case channel_cups: + { + return handleNewGenericConnectionFromProxy(channelId, channel_cups, "localhost", + cupsServerPort_, "CUPS"); + } + case channel_smb: + { + return handleNewGenericConnectionFromProxy(channelId, channel_smb, getComputerName(), + smbServerPort_, "SMB"); + } + case channel_media: + { + return handleNewGenericConnectionFromProxy(channelId, channel_media, "localhost", + mediaServerPort_, "media"); + } + case channel_http: + { + return handleNewGenericConnectionFromProxy(channelId, channel_http, getComputerName(), + httpServerPort_, "HTTP"); + } + case channel_slave: + { + return handleNewSlaveConnectionFromProxy(channelId); + } + default: + { + #ifdef PANIC + *logofs << "ServerProxy: PANIC! Unsupported channel with type '" + << getTypeName(type) << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Unsupported channel with type '" + << getTypeName(type) << "'.\n"; + + return -1; + } + } +} + +int ServerProxy::handleNewAgentConnection(Agent *agent) +{ + #ifdef PANIC + *logofs << "ServerProxy: PANIC! Can't create an agent " + << "connection at this side.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't create an agent " + << "connection at this side.\n"; + + return -1; +} + +int ServerProxy::handleNewXConnection(int clientFd) +{ + #ifdef PANIC + *logofs << "ServerProxy: PANIC! Can't create a new X channel " + << "with FD#" << clientFd << " at this side.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Can't create a new X channel " + << "with FD#" << clientFd << " at this side.\n"; + + return -1; +} + +int ServerProxy::handleNewXConnectionFromProxy(int channelId) +{ + // + // Connect to the real X server. + // + + int retryConnect = control -> OptionServerRetryConnect; + + int xServerFd; + + for (;;) + { + xServerFd = socket(xServerAddrFamily_, SOCK_STREAM, PF_UNSPEC); + + if (xServerFd < 0) + { + #ifdef PANIC + *logofs << "ServerProxy: PANIC! Call to socket failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Call to socket failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n"; + + return -1; + } + + #ifdef TEST + *logofs << "ServerProxy: Trying to connect to X server '" + << xServerDisplay_ << "'.\n" << logofs_flush; + #endif + + int result = connect(xServerFd, xServerAddr_, xServerAddrLength_); + + getNewTimestamp(); + + if (result < 0) + { + #ifdef WARNING + *logofs << "ServerProxy: WARNING! Connection to '" + << xServerDisplay_ << "' failed with error '" + << ESTR() << "'. Retrying.\n" << logofs_flush; + #endif + + close(xServerFd); + + if (--retryConnect == 0) + { + #ifdef PANIC + *logofs << "ServerProxy: PANIC! Connection to '" + << xServerDisplay_ << "' for channel ID#" + << channelId << " failed. Error is " + << EGET() << " '" << ESTR() << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Connection to '" + << xServerDisplay_ << "' failed. Error is " + << EGET() << " '" << ESTR() << "'.\n"; + + close(xServerFd); + + return -1; + } + + if (activeChannels_.getSize() == 0) + { + sleep(2); + } + else + { + sleep(1); + } + } + else + { + break; + } + } + + assignChannelMap(channelId, xServerFd); + + #ifdef TEST + *logofs << "ServerProxy: X server descriptor FD#" << xServerFd + << " mapped to channel ID#" << channelId << ".\n" + << logofs_flush; + #endif + + // + // Turn queuing off for path proxy-to-X-server. + // + + if (control -> OptionServerNoDelay == 1) + { + SetNoDelay(xServerFd, control -> OptionServerNoDelay); + } + + // + // If requested, set the size of the TCP send + // and receive buffers. + // + + if (control -> OptionServerSendBuffer != -1) + { + SetSendBuffer(xServerFd, control -> OptionServerSendBuffer); + } + + if (control -> OptionServerReceiveBuffer != -1) + { + SetReceiveBuffer(xServerFd, control -> OptionServerReceiveBuffer); + } + + if (allocateTransport(xServerFd, channelId) < 0) + { + return -1; + } + + // + // Starting from protocol level 3 client and server + // caches are created in proxy and shared between all + // channels. If remote proxy has older protocol level + // pointers are NULL and channels must create their + // own instances. + // + + channels_[channelId] = new ServerChannel(transports_[channelId], compressor_); + + if (channels_[channelId] == NULL) + { + deallocateTransport(channelId); + + return -1; + } + + increaseChannels(channelId); + + // + // Propagate channel stores and caches to the new + // channel. + // + + channels_[channelId] -> setOpcodes(opcodeStore_); + + channels_[channelId] -> setStores(clientStore_, serverStore_); + + channels_[channelId] -> setCaches(clientCache_, serverCache_); + + int port = atoi(fontServerPort_); + + if (port > 0) + { + channels_[channelId] -> setPorts(port); + } + + // + // Let channel configure itself according + // to control parameters. + // + + channels_[channelId] -> handleConfiguration(); + + // + // Check if we have successfully loaded the + // selected cache and, if not, remove it + // from disk. + // + + handleCheckLoad(); + + return 1; +} + +// +// Check if we still need to drop a channel. We need +// to check this explicitly at the time we receive a +// request to load or save the cache because we could +// receive the control message before having entered +// the function handling the channel events. +// + +int ServerProxy::handleCheckDrop() +{ + T_list channelList = activeChannels_.copyList(); + + for (T_list::iterator j = channelList.begin(); + j != channelList.end(); j++) + { + int channelId = *j; + + if (channels_[channelId] != NULL && + (channels_[channelId] -> getDrop() == 1 || + channels_[channelId] -> getClosing() == 1)) + { + #ifdef TEST + *logofs << "ServerProxy: Dropping the descriptor FD#" + << getFd(channelId) << " channel ID#" + << channelId << ".\n" << logofs_flush; + #endif + + handleDrop(channelId); + } + } + + return 1; +} + +int ServerProxy::handleCheckLoad() +{ + // + // Check if we just created the first X channel + // but the client side didn't tell us to load + // the cache selected at the session negotiation. + // This is very likely because the load operation + // failed at the remote side, for example because + // the cache was invalid or corrupted. + // + + int channelCount = getChannels(channel_x11); + + if (channelCount != 1) + { + return 0; + } + + if (control -> PersistentCacheEnableLoad == 1 && + control -> PersistentCachePath != NULL && + control -> PersistentCacheName != NULL && + isTimestamp(timeouts_.loadTs) == 0) + { + #ifdef WARNING + *logofs << "ServerProxy: WARNING! Cache file '" << control -> PersistentCachePath + << "/" << control -> PersistentCacheName << "' not loaded.\n" + << logofs_flush; + #endif + + // + // Remove the cache file. + // + + #ifdef WARNING + *logofs << "ServerProxy: WARNING! Removing supposedly " + << "incompatible cache '" << control -> PersistentCachePath + << "/" << control -> PersistentCacheName + << "'.\n" << logofs_flush; + #endif + + handleResetPersistentCache(); + } + + return 1; +} + +int ServerProxy::handleLoadFromProxy() +{ + // + // Be sure we drop any confirmed channel. + // + + handleCheckDrop(); + + // + // Check that either no X channel is + // remaining or we are inside a reset. + // + + int channelCount = getChannels(channel_x11); + + if (channelCount > 0) + { + #ifdef PANIC + *logofs << "ServerProxy: PANIC! Protocol violation " + << "in command load with " << channelCount + << " channels.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Protocol violation " + << "in command load from proxy.\n"; + + return -1; + } + else if (handleLoadStores() < 0) + { + #ifdef WARNING + *logofs << "ServerProxy: WARNING! Failed to load content " + << "of persistent cache.\n" << logofs_flush; + #endif + + return -1; + } + + return 1; +} + +int ServerProxy::handleSaveFromProxy() +{ + // + // Be sure we drop any confirmed channel. + // + + handleCheckDrop(); + + // + // Now verify that all channels are gone. + // + + int channelCount = getChannels(channel_x11); + + if (channelCount > 0) + { + #ifdef PANIC + *logofs << "ServerProxy: PANIC! Protocol violation " + << "in command save with " << channelCount + << " channels.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Protocol violation " + << "in command save from proxy.\n"; + + return -1; + } + else if (handleSaveStores() < 0) + { + #ifdef PANIC + *logofs << "ServerProxy: PANIC! Failed to save stores " + << "to persistent cache.\n" << logofs_flush; + #endif + + return -1; + } + + return 1; +} + +int ServerProxy::handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient) const +{ + if (clientStore_ -> saveRequestStores(cachefs, md5StateStream, md5StateClient, + discard_checksum, use_data) < 0) + { + return -1; + } + else if (serverStore_ -> saveReplyStores(cachefs, md5StateStream, md5StateClient, + use_checksum, discard_data) < 0) + { + return -1; + } + else if (serverStore_ -> saveEventStores(cachefs, md5StateStream, md5StateClient, + use_checksum, discard_data) < 0) + { + return -1; + } + + return 1; +} + +int ServerProxy::handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const +{ + if (clientStore_ -> loadRequestStores(cachefs, md5StateStream, + discard_checksum, use_data) < 0) + { + return -1; + } + else if (serverStore_ -> loadReplyStores(cachefs, md5StateStream, + use_checksum, discard_data) < 0) + { + return -1; + } + else if (serverStore_ -> loadEventStores(cachefs, md5StateStream, + use_checksum, discard_data) < 0) + { + return -1; + } + + return 1; +} diff --git a/nxcomp/ServerProxy.h b/nxcomp/ServerProxy.h new file mode 100644 index 000000000..8c4a88410 --- /dev/null +++ b/nxcomp/ServerProxy.h @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ServerProxy_H +#define ServerProxy_H + +#include <sys/types.h> +#include <sys/socket.h> + +#include "Proxy.h" + +#include "Misc.h" + +// +// Set the verbosity level. +// + +#undef TEST +#undef DEBUG + +class ServerProxy : public Proxy +{ + public: + + ServerProxy(int proxyFd); + + virtual ~ServerProxy(); + + virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily, + sockaddr *xServerAddr, unsigned int xServerAddrLength); + + virtual void handlePortConfiguration(int cupsServerPort, int smbServerPort, int mediaServerPort, + int httpServerPort, const char *fontServerPort); + + protected: + + // + // Create a new channel. + // + + virtual int handleNewConnection(T_channel_type type, int clientFd); + + virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId); + + virtual int handleNewAgentConnection(Agent *agent); + + virtual int handleNewXConnection(int clientFd); + + virtual int handleNewXConnectionFromProxy(int channelId); + + // + // Implement persistence according + // to our proxy mode. + // + + virtual int handleLoad(T_load_type type) + { + return 0; + } + + virtual int handleSave() + { + return 0; + } + + virtual int handleAsyncEvents() + { + return 0; + } + + virtual int handleLoadFromProxy(); + virtual int handleSaveFromProxy(); + + virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient) const; + + virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const; + + int handleCheckDrop(); + int handleCheckLoad(); + + // + // Utility function used to realize + // a new connection. + // + + protected: + + virtual int checkLocalChannelMap(int channelId) + { + if (control -> isProtoStep7() == 1) + { + return ((channelId & control -> ChannelMask) == 0); + } + else + { + return 0; + } + } + + private: + + int xServerAddrFamily_; + sockaddr *xServerAddr_; + unsigned int xServerAddrLength_; + + // + // This is the name of the X display where + // we are going to forward connections. + // + + char *xServerDisplay_; + + // + // Ports where to forward extended services' + // TCP connections. + // + + int cupsServerPort_; + int smbServerPort_; + int mediaServerPort_; + int httpServerPort_; + + // + // It will have to be passed to the channel + // so that it can set the port where the + // font server connections are tunneled. + // + + char *fontServerPort_; +}; + +#endif /* ServerProxy_H */ diff --git a/nxcomp/ServerReadBuffer.cpp b/nxcomp/ServerReadBuffer.cpp new file mode 100644 index 000000000..53c1dec57 --- /dev/null +++ b/nxcomp/ServerReadBuffer.cpp @@ -0,0 +1,235 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ServerReadBuffer.h" +#include "ServerChannel.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +unsigned int ServerReadBuffer::suggestedLength(unsigned int pendingLength) +{ + // + // Always read all the data that + // is available. + // + + int readable = transport_ -> readable(); + + unsigned int readLength = (readable == -1 ? 0 : (unsigned int) readable); + + if (readLength < pendingLength) + { + readLength = pendingLength; + } + + // + // Even if the readable data is not + // enough to make a complete message, + // resize the buffer to accomodate + // it all. + // + + if (pendingLength < remaining_) + { + readLength = remaining_; + } + + return readLength; +} + +int ServerReadBuffer::locateMessage(const unsigned char *start, + const unsigned char *end, + unsigned int &controlLength, + unsigned int &dataLength, + unsigned int &trailerLength) +{ + unsigned int size = end - start; + + #ifdef TEST + *logofs << "ServerReadBuffer: Locating message for FD#" + << transport_ -> fd() << " with " << size + << " bytes.\n" << logofs_flush; + #endif + + if (firstMessage_) + { + if (size < 8) + { + remaining_ = 8 - size; + + #ifdef TEST + *logofs << "ServerReadBuffer: No message was located " + << "with remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 0; + } + + dataLength = 8 + (GetUINT(start + 6, bigEndian_) << 2); + } + else + { + if (size < 32) + { + remaining_ = 32 - size; + + #ifdef TEST + *logofs << "ServerReadBuffer: No message was located " + << "with remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 0; + } + + if (*start == 1) + { + dataLength = 32 + (GetULONG(start + 4, bigEndian_) << 2); + } + else + { + dataLength = 32; + } + + if (dataLength < 32) + { + #ifdef TEST + *logofs << "ServerReadBuffer: WARNING! Assuming length 32 " + << "for suspicious message of length " << dataLength + << ".\n" << logofs_flush; + #endif + + dataLength = 32; + } + } + + #ifdef TEST + *logofs << "ServerReadBuffer: Length of the next message is " + << dataLength << ".\n" << logofs_flush; + #endif + + if (size < dataLength) + { + remaining_ = dataLength - size; + + #ifdef TEST + *logofs << "ServerReadBuffer: No message was located " + << "with remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 0; + } + + firstMessage_ = 0; + + controlLength = 0; + trailerLength = 0; + + remaining_ = 0; + + #ifdef TEST + *logofs << "ServerReadBuffer: Located message with " + << "remaining " << remaining_ << ".\n" + << logofs_flush; + #endif + + return 1; +} + +// +// Check if the data already read contains a +// message matching the opcode and sequence, +// starting at the given offset. +// + +unsigned char *ServerReadBuffer::peekMessage(unsigned int &offset, unsigned char opcode, + unsigned short sequence) +{ + #ifdef TEST + *logofs << "ServerReadBuffer: Peeking message " + << "for FD#" << transport_ -> fd() << " with size " + << length_ << " offset " << offset << " opcode " + << (unsigned int) opcode << " and sequence " + << sequence << ".\n" << logofs_flush; + #endif + + if (firstMessage_) + { + return NULL; + } + + unsigned char *next = buffer_ + start_ + offset; + unsigned char *end = buffer_ + start_ + length_; + + int found = 0; + + while (end - next >= 32) + { + #ifdef DEBUG + *logofs << "ServerReadBuffer: Checking opcode " + << (unsigned int) *next << " sequence " + << GetUINT(next + 2, bigEndian_) + << " at " << next - buffer_ + start_ + << ".\n" << logofs_flush; + #endif + + if (*next == opcode && GetUINT(next + 2, bigEndian_) == sequence) + { + found = 1; + + break; + } + else if (*next == 1) + { + next += (32 + (GetULONG(next + 4, bigEndian_) << 2)); + } + else + { + next += 32; + } + } + + offset = next - buffer_ + start_; + + if (found == 1) + { + #ifdef TEST + *logofs << "ServerReadBuffer: Found message at " + << "offset " << next - buffer_ + start_ + << ".\n" << logofs_flush; + #endif + + return next; + } + + #ifdef TEST + *logofs << "ServerReadBuffer: Quitting loop at " + << "offset " << next - buffer_ + start_ + << ".\n" << logofs_flush; + #endif + + return NULL; +} diff --git a/nxcomp/ServerReadBuffer.h b/nxcomp/ServerReadBuffer.h new file mode 100644 index 000000000..438e2f0da --- /dev/null +++ b/nxcomp/ServerReadBuffer.h @@ -0,0 +1,65 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ServerReadBuffer_H +#define ServerReadBuffer_H + +#include "ReadBuffer.h" +#include "Control.h" + +class ServerChannel; + +class ServerReadBuffer : public ReadBuffer +{ + public: + + ServerReadBuffer(Transport *transport, ServerChannel *channel) + + : ReadBuffer(transport), firstMessage_(1), channel_(channel) + { + } + + virtual ~ServerReadBuffer() + { + } + + void setBigEndian(int flag) + { + bigEndian_ = flag; + } + + unsigned char *peekMessage(unsigned int &offset, unsigned char opcode, + unsigned short sequence); + + protected: + + virtual unsigned int suggestedLength(unsigned int pendingLength); + + virtual int locateMessage(const unsigned char *start, + const unsigned char *end, + unsigned int &controlLength, + unsigned int &dataLength, + unsigned int &trailerLength); + + int bigEndian_; + + int firstMessage_; + + ServerChannel *channel_; +}; + +#endif /* ServerReadBuffer_H */ diff --git a/nxcomp/ServerStore.cpp b/nxcomp/ServerStore.cpp new file mode 100644 index 000000000..b0e13b1ad --- /dev/null +++ b/nxcomp/ServerStore.cpp @@ -0,0 +1,171 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ServerStore.h" + +// +// Cached reply classes. +// + +#include "GetImageReply.h" +#include "ListFontsReply.h" +#include "QueryFontReply.h" +#include "GetPropertyReply.h" +#include "GenericReply.h" + +// +// Set the verbosity level. +// + +#define WARNING +#define PANIC +#undef TEST + +ServerStore::ServerStore(StaticCompressor *compressor) +{ + if (logofs == NULL) + { + logofs = &cout; + } + + for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) + { + replies_[i] = NULL; + events_[i] = NULL; + } + + replies_[X_ListFonts] = new ListFontsReplyStore(compressor); + replies_[X_QueryFont] = new QueryFontReplyStore(compressor); + replies_[X_GetImage] = new GetImageReplyStore(compressor); + replies_[X_GetProperty] = new GetPropertyReplyStore(compressor); + + replies_[X_NXInternalGenericReply] = new GenericReplyStore(compressor); +} + +ServerStore::~ServerStore() +{ + if (logofs == NULL) + { + logofs = &cout; + } + + for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) + { + delete replies_[i]; + delete events_[i]; + } +} + +int ServerStore::saveReplyStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient, T_checksum_action checksumAction, + T_data_action dataAction) const +{ + for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) + { + if (replies_[i] != NULL && + replies_[i] -> saveStore(cachefs, md5StateStream, md5StateClient, + checksumAction, dataAction, + storeBigEndian()) < 0) + { + #ifdef PANIC + *logofs << "ServerStore: PANIC! Error saving reply store " + << "for OPCODE#" << (unsigned int) i << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error saving reply store " + << "for opcode '" << (unsigned int) i << "'.\n"; + + return -1; + } + } + + return 1; +} + +int ServerStore::saveEventStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient, T_checksum_action checksumAction, + T_data_action dataAction) const +{ + for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) + { + if (events_[i] != NULL && + events_[i] -> saveStore(cachefs, md5StateStream, md5StateClient, + checksumAction, dataAction, + storeBigEndian()) < 0) + { + #ifdef PANIC + *logofs << "ServerStore: PANIC! Error saving event store " + << "for OPCODE#" << (unsigned int) i << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Error saving event store " + << "for opcode '" << (unsigned int) i << "'.\n"; + + return -1; + } + } + + return 1; +} + +int ServerStore::loadReplyStores(istream *cachefs, md5_state_t *md5StateStream, + T_checksum_action checksumAction, T_data_action dataAction) const +{ + for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) + { + if (replies_[i] != NULL && + replies_[i] -> loadStore(cachefs, md5StateStream, + checksumAction, dataAction, + storeBigEndian()) < 0) + { + #ifdef PANIC + *logofs << "ServerStore: PANIC! Error loading reply store " + << "for OPCODE#" << (unsigned int) i << ".\n" + << logofs_flush; + #endif + + return -1; + } + } + + return 1; +} + +int ServerStore::loadEventStores(istream *cachefs, md5_state_t *md5StateStream, + T_checksum_action checksumAction, T_data_action dataAction) const +{ + for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++) + { + if (events_[i] != NULL && + events_[i] -> loadStore(cachefs, md5StateStream, + checksumAction, dataAction, + storeBigEndian()) < 0) + { + #ifdef PANIC + *logofs << "ServerStore: PANIC! Error loading event store " + << "for OPCODE#" << (unsigned int) i << ".\n" + << logofs_flush; + #endif + + return -1; + } + } + + return 1; +} diff --git a/nxcomp/ServerStore.h b/nxcomp/ServerStore.h new file mode 100644 index 000000000..8df1f1e9c --- /dev/null +++ b/nxcomp/ServerStore.h @@ -0,0 +1,75 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ServerStore_H +#define ServerStore_H + +#include "Message.h" + +#include "ChannelStore.h" + +class StaticCompressor; + +class ServerStore : public ChannelStore +{ + public: + + ServerStore(StaticCompressor *compressor); + + virtual ~ServerStore(); + + MessageStore *getReplyStore(unsigned char opcode) const + { + return replies_[opcode]; + } + + MessageStore *getEventStore(unsigned char opcode) const + { + return events_[opcode]; + } + + // + // Actually save the message store + // to disk according to proxy mode. + // + + int saveReplyStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient, T_checksum_action checksumAction, + T_data_action dataAction) const; + + int saveEventStores(ostream *cachefs, md5_state_t *md5StateStream, + md5_state_t *md5StateClient, T_checksum_action checksumAction, + T_data_action dataAction) const; + + + int loadReplyStores(istream *cachefs, md5_state_t *md5StateStream, + T_checksum_action checksumAction, T_data_action dataAction) const; + + int loadEventStores(istream *cachefs, md5_state_t *md5StateStream, + T_checksum_action checksumAction, T_data_action dataAction) const; + + private: + + // + // A server store contains replies and events. + // + + MessageStore *replies_[CHANNEL_STORE_OPCODE_LIMIT]; + MessageStore *events_[CHANNEL_STORE_OPCODE_LIMIT]; +}; + +#endif /* ServerStore_H */ diff --git a/nxcomp/SetClipRectangles.cpp b/nxcomp/SetClipRectangles.cpp new file mode 100644 index 000000000..8774744fb --- /dev/null +++ b/nxcomp/SetClipRectangles.cpp @@ -0,0 +1,142 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "SetClipRectangles.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int SetClipRectanglesStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message; + + // + // Here is the fingerprint. + // + + setClipRectangles -> ordering = *(buffer + 1); + + setClipRectangles -> gcontext = GetULONG(buffer + 4, bigEndian); + + setClipRectangles -> x_origin = GetUINT(buffer + 8, bigEndian); + setClipRectangles -> y_origin = GetUINT(buffer + 10, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int SetClipRectanglesStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message; + + // + // Fill all the message's fields. + // + + *(buffer + 1) = setClipRectangles -> ordering; + + PutULONG(setClipRectangles -> gcontext, buffer + 4, bigEndian); + + PutUINT(setClipRectangles -> x_origin, buffer + 8, bigEndian); + PutUINT(setClipRectangles -> y_origin, buffer + 10, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void SetClipRectanglesStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message; + + *logofs << name() << ": Identity ordering " << (unsigned int) setClipRectangles -> ordering + << ", gcontext " << setClipRectangles -> gcontext << ", x_origin " + << setClipRectangles -> x_origin << ", y_origin " + << setClipRectangles -> y_origin << ", size " + << setClipRectangles -> size_ << ".\n" << logofs_flush; + #endif +} + +void SetClipRectanglesStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 1, 1); + md5_append(md5_state_, buffer + 8, 4); +} + +void SetClipRectanglesStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message; + SetClipRectanglesMessage *cachedSetClipRectangles = (SetClipRectanglesMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " << setClipRectangles -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeXidValue(setClipRectangles -> gcontext, clientCache -> gcCache); + + cachedSetClipRectangles -> gcontext = setClipRectangles -> gcontext; +} + +void SetClipRectanglesStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeXidValue(value, clientCache -> gcCache); + + setClipRectangles -> gcontext = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << setClipRectangles -> gcontext + << " as gcontext field.\n" << logofs_flush; + #endif +} diff --git a/nxcomp/SetClipRectangles.h b/nxcomp/SetClipRectangles.h new file mode 100644 index 000000000..06b4421f5 --- /dev/null +++ b/nxcomp/SetClipRectangles.h @@ -0,0 +1,179 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef SetClipRectangles_H +#define SetClipRectangles_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define SETCLIPRECTANGLES_ENABLE_CACHE 1 +#define SETCLIPRECTANGLES_ENABLE_DATA 0 +#define SETCLIPRECTANGLES_ENABLE_SPLIT 0 +#define SETCLIPRECTANGLES_ENABLE_COMPRESS 0 + +#define SETCLIPRECTANGLES_DATA_LIMIT 2048 +#define SETCLIPRECTANGLES_DATA_OFFSET 12 + +#define SETCLIPRECTANGLES_CACHE_SLOTS 3000 +#define SETCLIPRECTANGLES_CACHE_THRESHOLD 3 +#define SETCLIPRECTANGLES_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class SetClipRectanglesMessage : public Message +{ + friend class SetClipRectanglesStore; + + public: + + SetClipRectanglesMessage() + { + } + + ~SetClipRectanglesMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char ordering; + unsigned int gcontext; + unsigned short x_origin; + unsigned short y_origin; +}; + +class SetClipRectanglesStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + SetClipRectanglesStore() : MessageStore() + { + enableCache = SETCLIPRECTANGLES_ENABLE_CACHE; + enableData = SETCLIPRECTANGLES_ENABLE_DATA; + enableSplit = SETCLIPRECTANGLES_ENABLE_SPLIT; + enableCompress = SETCLIPRECTANGLES_ENABLE_COMPRESS; + + dataLimit = SETCLIPRECTANGLES_DATA_LIMIT; + dataOffset = SETCLIPRECTANGLES_DATA_OFFSET; + + cacheSlots = SETCLIPRECTANGLES_CACHE_SLOTS; + cacheThreshold = SETCLIPRECTANGLES_CACHE_THRESHOLD; + cacheLowerThreshold = SETCLIPRECTANGLES_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~SetClipRectanglesStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "SetClipRectangles"; + } + + virtual unsigned char opcode() const + { + return X_SetClipRectangles; + } + + virtual unsigned int storage() const + { + return sizeof(SetClipRectanglesMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new SetClipRectanglesMessage(); + } + + virtual Message *create(const Message &message) const + { + return new SetClipRectanglesMessage((const SetClipRectanglesMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (SetClipRectanglesMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* SetClipRectangles_H */ diff --git a/nxcomp/SetUnpackAlpha.cpp b/nxcomp/SetUnpackAlpha.cpp new file mode 100644 index 000000000..a0dd1fc2e --- /dev/null +++ b/nxcomp/SetUnpackAlpha.cpp @@ -0,0 +1,257 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "SetUnpackAlpha.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +SetUnpackAlphaStore::SetUnpackAlphaStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = SETUNPACKALPHA_ENABLE_CACHE; + enableData = SETUNPACKALPHA_ENABLE_DATA; + enableSplit = SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_7; + enableCompress = SETUNPACKALPHA_ENABLE_COMPRESS_IF_PROTO_STEP_7; + + dataLimit = SETUNPACKALPHA_DATA_LIMIT; + dataOffset = SETUNPACKALPHA_DATA_OFFSET_IF_PROTO_STEP_7; + + cacheSlots = SETUNPACKALPHA_CACHE_SLOTS; + cacheThreshold = SETUNPACKALPHA_CACHE_THRESHOLD; + cacheLowerThreshold = SETUNPACKALPHA_CACHE_LOWER_THRESHOLD; + + if (control -> isProtoStep8() == 1) + { + enableSplit = SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_8; + } + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +SetUnpackAlphaStore::~SetUnpackAlphaStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int SetUnpackAlphaStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; + #endif + + // + // Encode the source length first because + // we need it to determine the size of + // the output buffer. + // + + // SrcLength. + encodeBuffer.encodeValue(GetULONG(buffer + 8, bigEndian), 32, 9); + + // Client. + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + clientCache -> resourceCache); + // Method. + encodeBuffer.encodeCachedValue(*(buffer + 4), 8, + clientCache -> methodCache); + // DstLength. + encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9); + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackAlphaStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; + #endif + + unsigned int value; + unsigned char cValue; + + // SrcLength. + decodeBuffer.decodeValue(value, 32, 9); + + size = RoundUp4(value) + 16; + + buffer = writeBuffer -> addMessage(size); + + PutULONG(value, buffer + 8, bigEndian); + + // Client. + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> resourceCache); + + *(buffer + 1) = cValue; + + // Method. + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> methodCache); + + *(buffer + 4) = cValue; + + // DstLength. + decodeBuffer.decodeValue(value, 32, 9); + + PutULONG(value, buffer + 12, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackAlphaStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message; + + setUnpackAlpha -> client = *(buffer + 1); + setUnpackAlpha -> method = *(buffer + 4); + + setUnpackAlpha -> src_length = GetULONG(buffer + 8, bigEndian); + setUnpackAlpha -> dst_length = GetULONG(buffer + 12, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackAlphaStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message; + + *(buffer + 1) = setUnpackAlpha -> client; + *(buffer + 4) = setUnpackAlpha -> method; + + PutULONG(setUnpackAlpha -> src_length, buffer + 8, bigEndian); + PutULONG(setUnpackAlpha -> dst_length, buffer + 12, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void SetUnpackAlphaStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message; + + *logofs << name() << ": Identity client " + << (unsigned int) setUnpackAlpha -> client << " method " + << (unsigned int) setUnpackAlpha -> method << " source length " + << setUnpackAlpha -> src_length << " destination length " + << setUnpackAlpha -> dst_length << " size " + << setUnpackAlpha -> size_ << ".\n"; + + #endif +} + +void SetUnpackAlphaStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // Include the pack method and the source + // and destination length. + // + + md5_append(md5_state_, buffer + 4, 1); + md5_append(md5_state_, buffer + 8, 8); +} + +void SetUnpackAlphaStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message; + SetUnpackAlphaMessage *cachedSetUnpackAlpha = (SetUnpackAlphaMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(setUnpackAlpha -> client, 8, + clientCache -> resourceCache); + + cachedSetUnpackAlpha -> client = setUnpackAlpha -> client; +} + +void SetUnpackAlphaStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(setUnpackAlpha -> client, 8, + clientCache -> resourceCache); +} diff --git a/nxcomp/SetUnpackAlpha.h b/nxcomp/SetUnpackAlpha.h new file mode 100644 index 000000000..2e32a6590 --- /dev/null +++ b/nxcomp/SetUnpackAlpha.h @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef SetUnpackAlpha_H +#define SetUnpackAlpha_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define SETUNPACKALPHA_ENABLE_CACHE 1 +#define SETUNPACKALPHA_ENABLE_DATA 1 +#define SETUNPACKALPHA_ENABLE_SPLIT 0 +#define SETUNPACKALPHA_ENABLE_COMPRESS 1 + +#define SETUNPACKALPHA_DATA_LIMIT 16384 +#define SETUNPACKALPHA_DATA_OFFSET 8 + +#define SETUNPACKALPHA_CACHE_SLOTS 2000 +#define SETUNPACKALPHA_CACHE_THRESHOLD 10 +#define SETUNPACKALPHA_CACHE_LOWER_THRESHOLD 5 + +#define SETUNPACKALPHA_DATA_OFFSET_IF_PROTO_STEP_7 16 +#define SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_7 1 +#define SETUNPACKALPHA_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 + +#define SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_8 0 + +// +// The message class. +// + +class SetUnpackAlphaMessage : public Message +{ + friend class SetUnpackAlphaStore; + + public: + + SetUnpackAlphaMessage() + { + } + + ~SetUnpackAlphaMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char client; + unsigned char method; + + unsigned int src_length; + unsigned int dst_length; +}; + +class SetUnpackAlphaStore : public MessageStore +{ + public: + + SetUnpackAlphaStore(StaticCompressor *compressor); + + virtual ~SetUnpackAlphaStore(); + + virtual const char *name() const + { + return "SetUnpackAlpha"; + } + + virtual unsigned char opcode() const + { + return X_NXSetUnpackAlpha; + } + + virtual unsigned int storage() const + { + return sizeof(SetUnpackAlphaMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new SetUnpackAlphaMessage(); + } + + virtual Message *create(const Message &message) const + { + return new SetUnpackAlphaMessage((const SetUnpackAlphaMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (SetUnpackAlphaMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* SetUnpackAlpha_H */ diff --git a/nxcomp/SetUnpackAlphaCompat.cpp b/nxcomp/SetUnpackAlphaCompat.cpp new file mode 100644 index 000000000..a8fcabdeb --- /dev/null +++ b/nxcomp/SetUnpackAlphaCompat.cpp @@ -0,0 +1,250 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "SetUnpackAlphaCompat.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +SetUnpackAlphaCompatStore::SetUnpackAlphaCompatStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = SETUNPACKALPHA_ENABLE_CACHE; + enableData = SETUNPACKALPHA_ENABLE_DATA; + enableSplit = SETUNPACKALPHA_ENABLE_SPLIT; + enableCompress = SETUNPACKALPHA_ENABLE_COMPRESS; + + dataLimit = SETUNPACKALPHA_DATA_LIMIT; + dataOffset = SETUNPACKALPHA_DATA_OFFSET; + + cacheSlots = SETUNPACKALPHA_CACHE_SLOTS; + cacheThreshold = SETUNPACKALPHA_CACHE_THRESHOLD; + cacheLowerThreshold = SETUNPACKALPHA_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +SetUnpackAlphaCompatStore::~SetUnpackAlphaCompatStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int SetUnpackAlphaCompatStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; + #endif + + // Client. + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + clientCache -> resourceCache); + // Entries. + encodeBuffer.encodeValue(GetULONG(buffer + 4, bigEndian), 32, 9); + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackAlphaCompatStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; + #endif + + unsigned int value; + unsigned char cValue; + + // Client. + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> resourceCache); + // Entries. + decodeBuffer.decodeValue(value, 32, 9); + + size = RoundUp4(value) + 8; + + buffer = writeBuffer -> addMessage(size); + + *(buffer + 1) = cValue; + + PutULONG(value, buffer + 4, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackAlphaCompatStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message; + + setUnpackAlpha -> client = *(buffer + 1); + + setUnpackAlpha -> entries = GetULONG(buffer + 4, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackAlphaCompatStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message; + + *(buffer + 1) = setUnpackAlpha -> client; + + PutULONG(setUnpackAlpha -> entries, buffer + 4, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void SetUnpackAlphaCompatStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message; + + *logofs << name() << ": Identity client " + << (unsigned int) setUnpackAlpha -> client << " entries " + << setUnpackAlpha -> entries << " size " + << setUnpackAlpha -> size_ << ".\n"; + + #endif +} + +void SetUnpackAlphaCompatStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 4, 4); +} + +void SetUnpackAlphaCompatStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message; + SetUnpackAlphaCompatMessage *cachedSetUnpackAlpha = (SetUnpackAlphaCompatMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(setUnpackAlpha -> client, 8, + clientCache -> resourceCache); + + cachedSetUnpackAlpha -> client = setUnpackAlpha -> client; + + if (cachedSetUnpackAlpha -> entries != setUnpackAlpha -> entries) + { + #ifdef TEST + *logofs << name() << ": Encoding value " << setUnpackAlpha -> entries + << " as entries field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeBoolValue(1); + + encodeBuffer.encodeValue(setUnpackAlpha -> entries, 32, 9); + + cachedSetUnpackAlpha -> entries = setUnpackAlpha -> entries; + } + else + { + encodeBuffer.encodeBoolValue(0); + } +} + +void SetUnpackAlphaCompatStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + SetUnpackAlphaCompatMessage *setUnpackAlpha = (SetUnpackAlphaCompatMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeCachedValue(setUnpackAlpha -> client, 8, + clientCache -> resourceCache); + + decodeBuffer.decodeBoolValue(value); + + if (value) + { + decodeBuffer.decodeValue(value, 32, 9); + + setUnpackAlpha -> entries = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << setUnpackAlpha -> entries + << " as entries field.\n" << logofs_flush; + #endif + } +} diff --git a/nxcomp/SetUnpackAlphaCompat.h b/nxcomp/SetUnpackAlphaCompat.h new file mode 100644 index 000000000..80d1522c0 --- /dev/null +++ b/nxcomp/SetUnpackAlphaCompat.h @@ -0,0 +1,149 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef SetUnpackAlphaCompat_H +#define SetUnpackAlphaCompat_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define SETUNPACKALPHA_ENABLE_CACHE 1 +#define SETUNPACKALPHA_ENABLE_DATA 1 +#define SETUNPACKALPHA_ENABLE_SPLIT 0 +#define SETUNPACKALPHA_ENABLE_COMPRESS 1 + +#define SETUNPACKALPHA_DATA_LIMIT 16384 +#define SETUNPACKALPHA_DATA_OFFSET 8 + +#define SETUNPACKALPHA_CACHE_SLOTS 2000 +#define SETUNPACKALPHA_CACHE_THRESHOLD 10 +#define SETUNPACKALPHA_CACHE_LOWER_THRESHOLD 5 + +// +// The message class. +// + +class SetUnpackAlphaCompatMessage : public Message +{ + friend class SetUnpackAlphaCompatStore; + + public: + + SetUnpackAlphaCompatMessage() + { + } + + ~SetUnpackAlphaCompatMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char client; + unsigned int entries; +}; + +class SetUnpackAlphaCompatStore : public MessageStore +{ + public: + + SetUnpackAlphaCompatStore(StaticCompressor *compressor); + + virtual ~SetUnpackAlphaCompatStore(); + + virtual const char *name() const + { + return "SetUnpackAlphaCompat"; + } + + virtual unsigned char opcode() const + { + return X_NXSetUnpackAlpha; + } + + virtual unsigned int storage() const + { + return sizeof(SetUnpackAlphaCompatMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new SetUnpackAlphaCompatMessage(); + } + + virtual Message *create(const Message &message) const + { + return new SetUnpackAlphaCompatMessage((const SetUnpackAlphaCompatMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (SetUnpackAlphaCompatMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* SetUnpackAlphaCompat_H */ diff --git a/nxcomp/SetUnpackColormap.cpp b/nxcomp/SetUnpackColormap.cpp new file mode 100644 index 000000000..d522d328e --- /dev/null +++ b/nxcomp/SetUnpackColormap.cpp @@ -0,0 +1,257 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "SetUnpackColormap.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +SetUnpackColormapStore::SetUnpackColormapStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = SETUNPACKCOLORMAP_ENABLE_CACHE; + enableData = SETUNPACKCOLORMAP_ENABLE_DATA; + enableSplit = SETUNPACKCOLORMAP_ENABLE_SPLIT; + enableCompress = SETUNPACKCOLORMAP_ENABLE_COMPRESS_IF_PROTO_STEP_7; + + dataLimit = SETUNPACKCOLORMAP_DATA_LIMIT; + dataOffset = SETUNPACKCOLORMAP_DATA_OFFSET_IF_PROTO_STEP_7; + + cacheSlots = SETUNPACKCOLORMAP_CACHE_SLOTS; + cacheThreshold = SETUNPACKCOLORMAP_CACHE_THRESHOLD; + cacheLowerThreshold = SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD; + + if (control -> isProtoStep8() == 1) + { + enableSplit = SETUNPACKCOLORMAP_ENABLE_SPLIT_IF_PROTO_STEP_8; + } + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +SetUnpackColormapStore::~SetUnpackColormapStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int SetUnpackColormapStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; + #endif + + // + // Encode the source length first because + // we need it to determine the size of + // the output buffer. + // + + // SrcLength. + encodeBuffer.encodeValue(GetULONG(buffer + 8, bigEndian), 32, 9); + + // Client. + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + clientCache -> resourceCache); + // Method. + encodeBuffer.encodeCachedValue(*(buffer + 4), 8, + clientCache -> methodCache); + // DstLength. + encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9); + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackColormapStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; + #endif + + unsigned int value; + unsigned char cValue; + + // SrcLength. + decodeBuffer.decodeValue(value, 32, 9); + + size = RoundUp4(value) + 16; + + buffer = writeBuffer -> addMessage(size); + + PutULONG(value, buffer + 8, bigEndian); + + // Client. + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> resourceCache); + + *(buffer + 1) = cValue; + + // Method. + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> methodCache); + + *(buffer + 4) = cValue; + + // DstLength. + decodeBuffer.decodeValue(value, 32, 9); + + PutULONG(value, buffer + 12, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackColormapStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message; + + setUnpackColormap -> client = *(buffer + 1); + setUnpackColormap -> method = *(buffer + 4); + + setUnpackColormap -> src_length = GetULONG(buffer + 8, bigEndian); + setUnpackColormap -> dst_length = GetULONG(buffer + 12, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackColormapStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message; + + *(buffer + 1) = setUnpackColormap -> client; + *(buffer + 4) = setUnpackColormap -> method; + + PutULONG(setUnpackColormap -> src_length, buffer + 8, bigEndian); + PutULONG(setUnpackColormap -> dst_length, buffer + 12, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void SetUnpackColormapStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message; + + *logofs << name() << ": Identity client " + << (unsigned int) setUnpackColormap -> client << " method " + << (unsigned int) setUnpackColormap -> method << " source length " + << setUnpackColormap -> src_length << " destination length " + << setUnpackColormap -> dst_length << " size " + << setUnpackColormap -> size_ << ".\n"; + + #endif +} + +void SetUnpackColormapStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // Include the pack method and the source + // and destination length. + // + + md5_append(md5_state_, buffer + 4, 1); + md5_append(md5_state_, buffer + 8, 8); +} + +void SetUnpackColormapStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message; + SetUnpackColormapMessage *cachedSetUnpackColormap = (SetUnpackColormapMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + encodeBuffer.encodeCachedValue(setUnpackColormap -> client, 8, + clientCache -> resourceCache); + + cachedSetUnpackColormap -> client = setUnpackColormap -> client; +} + +void SetUnpackColormapStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(setUnpackColormap -> client, 8, + clientCache -> resourceCache); +} diff --git a/nxcomp/SetUnpackColormap.h b/nxcomp/SetUnpackColormap.h new file mode 100644 index 000000000..53277b3f0 --- /dev/null +++ b/nxcomp/SetUnpackColormap.h @@ -0,0 +1,157 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef SetUnpackColormap_H +#define SetUnpackColormap_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define SETUNPACKCOLORMAP_ENABLE_CACHE 1 +#define SETUNPACKCOLORMAP_ENABLE_DATA 1 +#define SETUNPACKCOLORMAP_ENABLE_SPLIT 1 +#define SETUNPACKCOLORMAP_ENABLE_COMPRESS 1 + +#define SETUNPACKCOLORMAP_DATA_LIMIT 4096 +#define SETUNPACKCOLORMAP_DATA_OFFSET 8 + +#define SETUNPACKCOLORMAP_CACHE_SLOTS 2000 +#define SETUNPACKCOLORMAP_CACHE_THRESHOLD 5 +#define SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD 0 + +#define SETUNPACKCOLORMAP_DATA_OFFSET_IF_PROTO_STEP_7 16 +#define SETUNPACKCOLORMAP_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 + +#define SETUNPACKCOLORMAP_ENABLE_SPLIT_IF_PROTO_STEP_8 0 + +// +// The message class. +// + +class SetUnpackColormapMessage : public Message +{ + friend class SetUnpackColormapStore; + + public: + + SetUnpackColormapMessage() + { + } + + ~SetUnpackColormapMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char client; + unsigned char method; + + unsigned int src_length; + unsigned int dst_length; +}; + +class SetUnpackColormapStore : public MessageStore +{ + public: + + SetUnpackColormapStore(StaticCompressor *compressor); + + virtual ~SetUnpackColormapStore(); + + virtual const char *name() const + { + return "SetUnpackColormap"; + } + + virtual unsigned char opcode() const + { + return X_NXSetUnpackColormap; + } + + virtual unsigned int storage() const + { + return sizeof(SetUnpackColormapMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new SetUnpackColormapMessage(); + } + + virtual Message *create(const Message &message) const + { + return new SetUnpackColormapMessage((const SetUnpackColormapMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (SetUnpackColormapMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* SetUnpackColormap_H */ diff --git a/nxcomp/SetUnpackColormapCompat.cpp b/nxcomp/SetUnpackColormapCompat.cpp new file mode 100644 index 000000000..65b108a82 --- /dev/null +++ b/nxcomp/SetUnpackColormapCompat.cpp @@ -0,0 +1,262 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "SetUnpackColormapCompat.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +SetUnpackColormapCompatStore::SetUnpackColormapCompatStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = SETUNPACKCOLORMAP_ENABLE_CACHE; + enableData = SETUNPACKCOLORMAP_ENABLE_DATA; + enableSplit = SETUNPACKCOLORMAP_ENABLE_SPLIT; + enableCompress = SETUNPACKCOLORMAP_ENABLE_COMPRESS; + + dataLimit = SETUNPACKCOLORMAP_DATA_LIMIT; + dataOffset = SETUNPACKCOLORMAP_DATA_OFFSET; + + cacheSlots = SETUNPACKCOLORMAP_CACHE_SLOTS; + cacheThreshold = SETUNPACKCOLORMAP_CACHE_THRESHOLD; + cacheLowerThreshold = SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +SetUnpackColormapCompatStore::~SetUnpackColormapCompatStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int SetUnpackColormapCompatStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; + #endif + + // Client. + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + clientCache -> resourceCache); + // Entries. + encodeBuffer.encodeValue(GetULONG(buffer + 4, bigEndian), 32, 9); + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackColormapCompatStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; + #endif + + unsigned int value; + unsigned char cValue; + + // Client. + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> resourceCache); + // Entries. + decodeBuffer.decodeValue(value, 32, 9); + + size = (value << 2) + 8; + + buffer = writeBuffer -> addMessage(size); + + *(buffer + 1) = cValue; + + PutULONG(value, buffer + 4, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackColormapCompatStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message; + + setUnpackColormap -> client = *(buffer + 1); + + setUnpackColormap -> entries = GetULONG(buffer + 4, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackColormapCompatStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message; + + *(buffer + 1) = setUnpackColormap -> client; + + PutULONG(setUnpackColormap -> entries, buffer + 4, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void SetUnpackColormapCompatStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message; + + *logofs << name() << ": Identity client " + << (unsigned int) setUnpackColormap -> client << " entries " + << setUnpackColormap -> entries << " size " + << setUnpackColormap -> size_ << ".\n"; + + #endif +} + +void SetUnpackColormapCompatStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 4, 4); +} + +void SetUnpackColormapCompatStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message; + SetUnpackColormapCompatMessage *cachedSetUnpackColormap = (SetUnpackColormapCompatMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " + << (unsigned int) setUnpackColormap -> client + << " as client field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(setUnpackColormap -> client, 8, + clientCache -> resourceCache); + + cachedSetUnpackColormap -> client = setUnpackColormap -> client; + + if (cachedSetUnpackColormap -> entries != setUnpackColormap -> entries) + { + #ifdef TEST + *logofs << name() << ": Encoding value " << setUnpackColormap -> entries + << " as entries field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeBoolValue(1); + + encodeBuffer.encodeValue(setUnpackColormap -> entries, 32, 9); + + cachedSetUnpackColormap -> entries = setUnpackColormap -> entries; + } + else + { + encodeBuffer.encodeBoolValue(0); + } +} + +void SetUnpackColormapCompatStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + SetUnpackColormapCompatMessage *setUnpackColormap = (SetUnpackColormapCompatMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + decodeBuffer.decodeCachedValue(setUnpackColormap -> client, 8, + clientCache -> resourceCache); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " + << (unsigned int) setUnpackColormap -> client + << " as client field.\n" << logofs_flush; + #endif + + decodeBuffer.decodeBoolValue(value); + + if (value) + { + decodeBuffer.decodeValue(value, 32, 9); + + setUnpackColormap -> entries = value; + + #ifdef DEBUG + *logofs << name() << ": Decoded value " << setUnpackColormap -> entries + << " as entries field.\n" << logofs_flush; + #endif + } +} diff --git a/nxcomp/SetUnpackColormapCompat.h b/nxcomp/SetUnpackColormapCompat.h new file mode 100644 index 000000000..d1ffad876 --- /dev/null +++ b/nxcomp/SetUnpackColormapCompat.h @@ -0,0 +1,149 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef SetUnpackColormapCompat_H +#define SetUnpackColormapCompat_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define SETUNPACKCOLORMAP_ENABLE_CACHE 1 +#define SETUNPACKCOLORMAP_ENABLE_DATA 1 +#define SETUNPACKCOLORMAP_ENABLE_SPLIT 1 +#define SETUNPACKCOLORMAP_ENABLE_COMPRESS 1 + +#define SETUNPACKCOLORMAP_DATA_LIMIT 4096 +#define SETUNPACKCOLORMAP_DATA_OFFSET 8 + +#define SETUNPACKCOLORMAP_CACHE_SLOTS 2000 +#define SETUNPACKCOLORMAP_CACHE_THRESHOLD 5 +#define SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD 0 + +// +// The message class. +// + +class SetUnpackColormapCompatMessage : public Message +{ + friend class SetUnpackColormapCompatStore; + + public: + + SetUnpackColormapCompatMessage() + { + } + + ~SetUnpackColormapCompatMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char client; + unsigned int entries; +}; + +class SetUnpackColormapCompatStore : public MessageStore +{ + public: + + SetUnpackColormapCompatStore(StaticCompressor *compressor); + + virtual ~SetUnpackColormapCompatStore(); + + virtual const char *name() const + { + return "SetUnpackColormapCompat"; + } + + virtual unsigned char opcode() const + { + return X_NXSetUnpackColormap; + } + + virtual unsigned int storage() const + { + return sizeof(SetUnpackColormapCompatMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new SetUnpackColormapCompatMessage(); + } + + virtual Message *create(const Message &message) const + { + return new SetUnpackColormapCompatMessage((const SetUnpackColormapCompatMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (SetUnpackColormapCompatMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* SetUnpackColormapCompat_H */ diff --git a/nxcomp/SetUnpackGeometry.cpp b/nxcomp/SetUnpackGeometry.cpp new file mode 100644 index 000000000..67b79a410 --- /dev/null +++ b/nxcomp/SetUnpackGeometry.cpp @@ -0,0 +1,293 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "SetUnpackGeometry.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +SetUnpackGeometryStore::SetUnpackGeometryStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = SETUNPACKGEOMETRY_ENABLE_CACHE; + enableData = SETUNPACKGEOMETRY_ENABLE_DATA; + enableSplit = SETUNPACKGEOMETRY_ENABLE_SPLIT; + enableCompress = SETUNPACKGEOMETRY_ENABLE_COMPRESS; + + dataLimit = SETUNPACKGEOMETRY_DATA_LIMIT; + dataOffset = SETUNPACKGEOMETRY_DATA_OFFSET; + + cacheSlots = SETUNPACKGEOMETRY_CACHE_SLOTS; + cacheThreshold = SETUNPACKGEOMETRY_CACHE_THRESHOLD; + cacheLowerThreshold = SETUNPACKGEOMETRY_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +SetUnpackGeometryStore::~SetUnpackGeometryStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int SetUnpackGeometryStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + clientCache -> resourceCache); + + const unsigned char *nextChar = buffer + 4; + + for (int i = 0; i < 6; i++) + { + encodeBuffer.encodeCachedValue(*nextChar++, 8, + clientCache -> depthCache); + } + + encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32); + encodeBuffer.encodeValue(GetULONG(buffer + 16, bigEndian), 32); + encodeBuffer.encodeValue(GetULONG(buffer + 20, bigEndian), 32); + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackGeometryStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; + #endif + + size = 24; + buffer = writeBuffer -> addMessage(size); + + unsigned char cValue; + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> resourceCache); + *(buffer + 1) = cValue; + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + *(buffer + 4) = cValue; + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + *(buffer + 5) = cValue; + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + *(buffer + 6) = cValue; + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + *(buffer + 7) = cValue; + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + *(buffer + 8) = cValue; + + decodeBuffer.decodeCachedValue(cValue, 8, + clientCache -> depthCache); + *(buffer + 9) = cValue; + + unsigned int value; + + decodeBuffer.decodeValue(value, 32); + PutULONG(value, buffer + 12, bigEndian); + + decodeBuffer.decodeValue(value, 32); + PutULONG(value, buffer + 16, bigEndian); + + decodeBuffer.decodeValue(value, 32); + PutULONG(value, buffer + 20, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackGeometryStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message; + + setUnpackGeometry -> client = *(buffer + 1); + + setUnpackGeometry -> depth_1_bpp = *(buffer + 4); + setUnpackGeometry -> depth_4_bpp = *(buffer + 5); + setUnpackGeometry -> depth_8_bpp = *(buffer + 6); + setUnpackGeometry -> depth_16_bpp = *(buffer + 7); + setUnpackGeometry -> depth_24_bpp = *(buffer + 8); + setUnpackGeometry -> depth_32_bpp = *(buffer + 9); + + setUnpackGeometry -> red_mask = GetULONG(buffer + 12, bigEndian); + setUnpackGeometry -> green_mask = GetULONG(buffer + 16, bigEndian); + setUnpackGeometry -> blue_mask = GetULONG(buffer + 20, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +int SetUnpackGeometryStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message; + + *(buffer + 1) = setUnpackGeometry -> client; + + *(buffer + 4) = setUnpackGeometry -> depth_1_bpp; + *(buffer + 5) = setUnpackGeometry -> depth_4_bpp; + *(buffer + 6) = setUnpackGeometry -> depth_8_bpp; + *(buffer + 7) = setUnpackGeometry -> depth_16_bpp; + *(buffer + 8) = setUnpackGeometry -> depth_24_bpp; + *(buffer + 9) = setUnpackGeometry -> depth_32_bpp; + + PutULONG(setUnpackGeometry -> red_mask, buffer + 12, bigEndian); + PutULONG(setUnpackGeometry -> green_mask, buffer + 16, bigEndian); + PutULONG(setUnpackGeometry -> blue_mask, buffer + 20, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush; + #endif + + return 1; +} + +void SetUnpackGeometryStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message; + + *logofs << name() << ": Identity client " + << (unsigned) setUnpackGeometry -> client << " depth_1_bpp " + << (unsigned) setUnpackGeometry -> depth_1_bpp << " depth_4_bpp " + << (unsigned int) setUnpackGeometry -> depth_4_bpp << " depth_8_bpp " + << (unsigned int) setUnpackGeometry -> depth_8_bpp << " depth_16_bpp " + << (unsigned int) setUnpackGeometry -> depth_16_bpp << " depth_24_bpp " + << (unsigned int) setUnpackGeometry -> depth_24_bpp << " depth_32_bpp " + << (unsigned int) setUnpackGeometry -> depth_32_bpp + + << " red_mask " << setUnpackGeometry -> red_mask + << " green_mask " << setUnpackGeometry -> green_mask + << " blue_mask " << setUnpackGeometry -> blue_mask + + << " size " << setUnpackGeometry -> size_ << ".\n"; + + #endif +} + +void SetUnpackGeometryStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 4, 6); + md5_append(md5_state_, buffer + 12, 12); +} + +void SetUnpackGeometryStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message; + SetUnpackGeometryMessage *cachedSetUnpackGeometry = (SetUnpackGeometryMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef TEST + *logofs << name() << ": Encoding value " + << (unsigned int) setUnpackGeometry -> client + << " as client field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue(setUnpackGeometry -> client, 8, + clientCache -> resourceCache); + + cachedSetUnpackGeometry -> client = setUnpackGeometry -> client; +} + +void SetUnpackGeometryStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + decodeBuffer.decodeCachedValue(setUnpackGeometry -> client, 8, + clientCache -> resourceCache); + + #ifdef DEBUG + *logofs << name() << ": Decoded value " + << (unsigned int) setUnpackGeometry -> client + << " as client field.\n" << logofs_flush; + #endif +} diff --git a/nxcomp/SetUnpackGeometry.h b/nxcomp/SetUnpackGeometry.h new file mode 100644 index 000000000..3a8224dfb --- /dev/null +++ b/nxcomp/SetUnpackGeometry.h @@ -0,0 +1,159 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef SetUnpackGeometry_H +#define SetUnpackGeometry_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define SETUNPACKGEOMETRY_ENABLE_CACHE 1 +#define SETUNPACKGEOMETRY_ENABLE_DATA 0 +#define SETUNPACKGEOMETRY_ENABLE_SPLIT 0 +#define SETUNPACKGEOMETRY_ENABLE_COMPRESS 0 + +#define SETUNPACKGEOMETRY_DATA_LIMIT 24 +#define SETUNPACKGEOMETRY_DATA_OFFSET 24 + +#define SETUNPACKGEOMETRY_CACHE_SLOTS 20 +#define SETUNPACKGEOMETRY_CACHE_THRESHOLD 1 +#define SETUNPACKGEOMETRY_CACHE_LOWER_THRESHOLD 0 + +// +// The message class. +// + +class SetUnpackGeometryMessage : public Message +{ + friend class SetUnpackGeometryStore; + + public: + + SetUnpackGeometryMessage() + { + } + + ~SetUnpackGeometryMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned char client; + + unsigned char depth_1_bpp; + unsigned char depth_4_bpp; + unsigned char depth_8_bpp; + unsigned char depth_16_bpp; + unsigned char depth_24_bpp; + unsigned char depth_32_bpp; + + unsigned int red_mask; + unsigned int green_mask; + unsigned int blue_mask; +}; + +class SetUnpackGeometryStore : public MessageStore +{ + public: + + SetUnpackGeometryStore(StaticCompressor *compressor); + + virtual ~SetUnpackGeometryStore(); + + virtual const char *name() const + { + return "SetUnpackGeometry"; + } + + virtual unsigned char opcode() const + { + return X_NXSetUnpackGeometry; + } + + virtual unsigned int storage() const + { + return sizeof(SetUnpackGeometryMessage); + } + + // + // Message handling methods. + // + + protected: + + virtual Message *create() const + { + return new SetUnpackGeometryMessage(); + } + + virtual Message *create(const Message &message) const + { + return new SetUnpackGeometryMessage((const SetUnpackGeometryMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (SetUnpackGeometryMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* SetUnpackGeometry_H */ diff --git a/nxcomp/ShapeExtension.cpp b/nxcomp/ShapeExtension.cpp new file mode 100644 index 000000000..daa19fbb4 --- /dev/null +++ b/nxcomp/ShapeExtension.cpp @@ -0,0 +1,296 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "ShapeExtension.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "WriteBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Constructors and destructors. +// + +ShapeExtensionStore::ShapeExtensionStore(StaticCompressor *compressor) + + : MessageStore(compressor) +{ + enableCache = SHAPEEXTENSION_ENABLE_CACHE; + enableData = SHAPEEXTENSION_ENABLE_DATA; + enableSplit = SHAPEEXTENSION_ENABLE_SPLIT; + enableCompress = SHAPEEXTENSION_ENABLE_COMPRESS; + + if (control -> isProtoStep7() == 1) + { + enableCompress = SHAPEEXTENSION_ENABLE_COMPRESS_IF_PROTO_STEP_7; + } + + dataLimit = SHAPEEXTENSION_DATA_LIMIT; + dataOffset = SHAPEEXTENSION_DATA_OFFSET; + + cacheSlots = SHAPEEXTENSION_CACHE_SLOTS; + cacheThreshold = SHAPEEXTENSION_CACHE_THRESHOLD; + cacheLowerThreshold = SHAPEEXTENSION_CACHE_LOWER_THRESHOLD; + + opcode_ = X_NXInternalShapeExtension; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; +} + +ShapeExtensionStore::~ShapeExtensionStore() +{ + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); +} + +// +// Here are the methods to handle messages' content. +// + +int ShapeExtensionStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const +{ + // + // Handle this extension in a way similar to shape. + // + + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Encoding full message identity.\n" << logofs_flush; + #endif + + // + // We handle all possible requests of this extension + // using the same opcode. We give to message a data + // offset of 4 (or 16 if proto is >= 3) and handle + // the first 16 bytes through an array of caches. + // + + encodeBuffer.encodeValue(size >> 2, 16, 10); + + encodeBuffer.encodeCachedValue(*(buffer + 1), 8, + clientCache -> shapeOpcodeCache); + + for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) + { + encodeBuffer.encodeCachedValue(GetUINT(buffer + (i * 2) + 4, bigEndian), 16, + *clientCache -> shapeDataCache[i]); + } + + #ifdef DEBUG + *logofs << name() << ": Encoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int ShapeExtensionStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const +{ + ClientCache *clientCache = (ClientCache *) channelCache; + + #ifdef DEBUG + *logofs << name() << ": Decoding full message identity.\n" << logofs_flush; + #endif + + decodeBuffer.decodeValue(size, 16, 10); + + size <<= 2; + + buffer = writeBuffer -> addMessage(size); + + decodeBuffer.decodeCachedValue(*(buffer + 1), 8, + clientCache -> shapeOpcodeCache); + + unsigned int value; + + for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache -> shapeDataCache[i]); + + PutUINT(value, buffer + 4 + (i * 2), bigEndian); + } + + #ifdef DEBUG + *logofs << name() << ": Decoded full message identity.\n" << logofs_flush; + #endif + + return 1; +} + +int ShapeExtensionStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message; + + shapeExtension -> opcode = *(buffer + 1); + + for (unsigned int i = 0; i < 8; i++) + { + if ((i * 2 + 4) < size) + { + shapeExtension -> data[i] = GetUINT(buffer + i * 2 + 4, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed data[" << i << "].\n" + << logofs_flush; + #endif + } + else + { + shapeExtension -> data[i] = 0; + } + } + + #ifdef DEBUG + *logofs << name() << ": Parsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int ShapeExtensionStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message; + + *(buffer + 1) = shapeExtension -> opcode; + + for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++) + { + PutUINT(shapeExtension -> data[i], buffer + i * 2 + 4, bigEndian); + } + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " + << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void ShapeExtensionStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message; + + *logofs << name() << ": Identity opcode " << (unsigned) shapeExtension -> opcode; + + for (int i = 0; i < 8; i++) + { + *logofs << ", data[" << i << "] " << shapeExtension -> data[i]; + } + + *logofs << ", size " << shapeExtension -> size_ << ".\n" << logofs_flush; + + #endif +} + +void ShapeExtensionStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + // + // Include minor opcode in the checksum. As data + // offset can be beyond the real end of message, + // we need to include size or we will match any + // message of size less or equal to data offset. + // + + md5_append(md5_state_, buffer + 1, 3); +} + +void ShapeExtensionStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const +{ + // + // Encode the variant part. + // + + ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message; + ShapeExtensionMessage *cachedShapeExtension = (ShapeExtensionMessage *) cachedMessage; + + ClientCache *clientCache = (ClientCache *) channelCache; + + for (int i = 0; i < 8 && (i * 2 + 4) < shapeExtension -> size_; i++) + { + #ifdef TEST + *logofs << name() << ": Encoding value " << shapeExtension -> data[i] + << " as data[" << i << "] field.\n" << logofs_flush; + #endif + + encodeBuffer.encodeCachedValue((unsigned int) shapeExtension -> data[i], 16, + *clientCache -> shapeDataCache[i]); + + cachedShapeExtension -> data[i] = shapeExtension -> data[i]; + } +} + +void ShapeExtensionStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const +{ + ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message; + + ClientCache *clientCache = (ClientCache *) channelCache; + + unsigned int value; + + for (int i = 0; i < 8 && (i * 2 + 4) < shapeExtension -> size_; i++) + { + decodeBuffer.decodeCachedValue(value, 16, + *clientCache -> shapeDataCache[i]); + + shapeExtension -> data[i] = (unsigned short) value; + + #ifdef TEST + *logofs << name() << ": Decoded value " << shapeExtension -> data[i] + << " as data[" << i << "] field.\n" << logofs_flush; + #endif + } +} diff --git a/nxcomp/ShapeExtension.h b/nxcomp/ShapeExtension.h new file mode 100644 index 000000000..32c3b55ef --- /dev/null +++ b/nxcomp/ShapeExtension.h @@ -0,0 +1,157 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef ShapeExtension_H +#define ShapeExtension_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define SHAPEEXTENSION_ENABLE_CACHE 1 +#define SHAPEEXTENSION_ENABLE_DATA 1 +#define SHAPEEXTENSION_ENABLE_SPLIT 0 +#define SHAPEEXTENSION_ENABLE_COMPRESS 1 + +#define SHAPEEXTENSION_DATA_LIMIT 3200 +#define SHAPEEXTENSION_DATA_OFFSET 20 + +#define SHAPEEXTENSION_CACHE_SLOTS 3000 +#define SHAPEEXTENSION_CACHE_THRESHOLD 10 +#define SHAPEEXTENSION_CACHE_LOWER_THRESHOLD 5 + +#define SHAPEEXTENSION_ENABLE_COMPRESS_IF_PROTO_STEP_7 0 + +// +// The message class. +// + +class ShapeExtensionMessage : public Message +{ + friend class ShapeExtensionStore; + + public: + + ShapeExtensionMessage() + { + } + + ~ShapeExtensionMessage() + { + } + + // + // Note for encoding in protocol level 1: we consider + // for this message a data offset of 4. Bytes from 5 + // to 20, if present, are taken as part of identity + // and encoded through an array of int caches. + // + + private: + + unsigned char opcode; + unsigned short data[8]; +}; + +class ShapeExtensionStore : public MessageStore +{ + public: + + ShapeExtensionStore(StaticCompressor *compressor); + + virtual ~ShapeExtensionStore(); + + virtual const char *name() const + { + return "ShapeExtension"; + } + + virtual unsigned char opcode() const + { + return opcode_; + } + + virtual unsigned int storage() const + { + return sizeof(ShapeExtensionMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new ShapeExtensionMessage(); + } + + virtual Message *create(const Message &message) const + { + return new ShapeExtensionMessage((const ShapeExtensionMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (ShapeExtensionMessage *) message; + } + + virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer, + const unsigned int size, int bigEndian, + ChannelCache *channelCache) const; + + virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer, + unsigned int &size, int bigEndian, WriteBuffer *writeBuffer, + ChannelCache *channelCache) const; + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, + const Message *cachedMessage, + ChannelCache *channelCache) const; + + virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, + ChannelCache *channelCache) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; + + private: + + unsigned char opcode_; +}; + +#endif /* ShapeExtension_H */ diff --git a/nxcomp/Socket.cpp b/nxcomp/Socket.cpp new file mode 100644 index 000000000..ea00a9b4e --- /dev/null +++ b/nxcomp/Socket.cpp @@ -0,0 +1,745 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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/Socket.h b/nxcomp/Socket.h new file mode 100644 index 000000000..27c330850 --- /dev/null +++ b/nxcomp/Socket.h @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Socket_H +#define Socket_H + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#ifdef __sun +#include <stropts.h> +#include <sys/filio.h> +#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/Split.cpp b/nxcomp/Split.cpp new file mode 100644 index 000000000..50627e793 --- /dev/null +++ b/nxcomp/Split.cpp @@ -0,0 +1,1846 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <unistd.h> +#include <string.h> +#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. + // + + if (control -> isProtoStep7() == 1) + { + decodeBuffer.decodeValue(compressedSize, 32, 14); + } + else + { + // + // As we can't refuse to handle the decoding + // of the split message when connected to an + // old proxy version, we need to decode this + // in a way that is compatible. + // + + unsigned int diffSize; + + decodeBuffer.decodeValue(diffSize, 32, 14); + + split -> store_ -> lastResize += diffSize; + + compressedSize = split -> store_ -> lastResize; + } + + split -> store_ -> validateSize(split -> d_size_, compressedSize); + + split -> r_size_ = compressedSize; + } + } + + // + // Update the size if the split + // was not already loaded. + // + + if (split -> state_ != split_loaded) + { + split -> data_.clear(); + + if (compressedSize > 0) + { + split -> c_size_ = compressedSize; + + #ifdef TEST + *logofs << "SplitStore: Split data of size " + << split -> d_size_ << " was compressed to " + << split -> c_size_ << " bytes.\n" + << logofs_flush; + #endif + + split -> data_.resize(split -> c_size_); + } + else + { + split -> data_.resize(split -> d_size_); + } + + unsigned char *data = split -> data_.begin(); + + data[0] = SPLIT_PATTERN; + data[1] = SPLIT_PATTERN; + } + #ifdef TEST + else + { + // + // The message had been already + // loaded from disk. + // + + if (compressedSize > 0) + { + if ((int) compressedSize != split -> c_size_) + { + *logofs << "SplitStore: WARNING! Compressed data size is " + << "different than the loaded compressed size.\n" + << logofs_flush; + } + + *logofs << "SplitStore: Ignoring the new size with " + << "loaded compressed size " << split -> c_size_ + << ".\n" << logofs_flush; + } + } + #endif + + return 1; +} + +int SplitStore::receive(DecodeBuffer &decodeBuffer) +{ + if (splits_ -> size() == 0) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Function receive called with no splits available.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Function receive called with no splits available.\n"; + + HandleAbort(); + } + + if (current_ == splits_ -> end()) + { + start(decodeBuffer); + } + + // + // Check first if split was aborted, else add + // any new data to message being recomposed. + // + + Split *split = *current_; + + unsigned int abort = 0; + + decodeBuffer.decodeBoolValue(abort); + + if (abort == 1) + { + #ifdef TEST + *logofs << "SplitStore: Aborting split for checksum [" + << DumpChecksum(split -> checksum_) << "] position " + << split -> position_ << " with " << (split -> + data_.size() - split -> next_) << " bytes to go " + << "out of " << split -> data_.size() + << ".\n" << logofs_flush; + #endif + + statistics -> addSplitAborted(); + + statistics -> addSplitAbortedBytesOut(split -> r_size_ - split -> next_); + + split -> next_ = split -> r_size_; + + split -> state_ = split_aborted; + } + else + { + // + // Get the size of the packet. + // + + unsigned int count; + + decodeBuffer.decodeValue(count, 32, 10); + + // + // If the split was not already loaded from + // disk, decode the packet and update our + // copy of the data. The encoding side may + // have not received the abort event, yet, + // and may be unaware that the message is + // stored in compressed form at our side. + // + + #ifdef TEST + *logofs << "SplitStore: Receiving split for checksum [" + << DumpChecksum(split -> checksum_) << "] count " + << count << " position " << split -> position_ + << ". Data size is " << split -> data_.size() << " (" + << split -> d_size_ << "/" << split -> c_size_ << "/" + << split -> r_size_ << "), " << split -> r_size_ - + (split -> next_ + count) << " to go.\n" + << logofs_flush; + #endif + + if (split -> next_ + count > (unsigned) split -> r_size_) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Invalid data count " + << count << "provided in the split.\n" + << logofs_flush; + + *logofs << "SplitStore: PANIC! While receiving split for " + << "checksum [" << DumpChecksum(split -> checksum_) + << "] with count " << count << " action [" + << DumpAction(split -> action_) << "] state [" + << DumpState(split -> state_) << "]. Data size is " + << split -> data_.size() << " (" << split -> d_size_ + << "/" << split -> c_size_ << "), " << split -> + data_.size() - (split -> next_ + count) + << " to go.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Invalid data count " + << count << "provided in the split.\n"; + + HandleAbort(); + } + + if (split -> state_ != split_loaded) + { + #ifdef TEST + + if (split -> next_ + count > split -> data_.size()) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Inconsistent split data size " + << split -> data_.size() << " with expected size " + << split -> r_size_ << ".\n" + << logofs_flush; + #endif + + HandleAbort(); + } + + #endif + + memcpy(split -> data_.begin() + split -> next_, + decodeBuffer.decodeMemory(count), count); + } + else + { + #ifdef TEST + *logofs << "SplitStore: WARNING! Data discarded with split " + << "loaded from disk.\n" << logofs_flush; + #endif + + decodeBuffer.decodeMemory(count); + } + + split -> next_ += count; + } + + // + // Is unsplit complete? + // + + if (split -> next_ != split -> r_size_) + { + return 0; + } + + // + // If the persistent cache is enabled, + // we have a valid checksum and the + // split was not originally retrieved + // from disk, save the message on disk. + // + + if (split -> state_ != split_loaded && + split -> state_ != split_aborted) + { + save(split); + } + + // + // Move the split at the head of the + // list to the commits. + // + + remove(split); + + // + // Reset the current position to the + // end of the repository. + // + + current_ = splits_ -> end(); + + #ifdef TEST + *logofs << "SplitStore: Removed split at head of the list. " + << "Resource is " << split -> resource_ << " request " + << (unsigned) split -> store_ -> opcode() << " position " + << split -> position_ << ".\n" << logofs_flush; + #endif + + return 1; +} + +Split *SplitStore::pop() +{ + if (splits_ -> size() == 0) + { + #ifdef TEST + *logofs << "SplitStore: The split store is empty.\n" + << logofs_flush; + #endif + + return NULL; + } + + // + // Move the pointer at the end of the list. + // The next send operation will eventually + // start a new split. + // + + current_ = splits_ -> end(); + + Split *split = *(splits_ -> begin()); + + splits_ -> pop_front(); + + #ifdef TEST + *logofs << "SplitStore: Removed split at the head of the " + << "list with resource " << split -> resource_ + << " request " << (unsigned) split -> store_ -> + opcode() << " position " << split -> position_ + << ".\n" << logofs_flush; + #endif + + splitStorageSize_ -= getNodeSize(split); + + totalSplitSize_--; + + totalSplitStorageSize_ -= getNodeSize(split); + + #ifdef TEST + *logofs << "SplitStore: There are " << splits_ -> size() + << " messages in store [" << resource_ << "] with " + << "storage size " << splitStorageSize_ << ".\n" + << logofs_flush; + + *logofs << "SplitStore: Total messages in stores are " + << totalSplitSize_ << " with total storage size " + << totalSplitStorageSize_ << ".\n" + << logofs_flush; + #endif + + return split; +} + +void SplitStore::remove(Split *split) +{ + #ifdef TEST + *logofs << "SplitStore: Going to remove the split from the list.\n" + << logofs_flush; + #endif + + #ifdef TEST + + if (split != getFirstSplit()) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Trying to remove a split " + << "not at the head of the list.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Trying to remove a split " + << "not at the head of the list.\n"; + + HandleAbort(); + } + + #endif + + // + // Move the split to the commit store. + // + + splits_ -> pop_front(); + + commits_ -> splits_ -> push_back(split); + + splitStorageSize_ -= getNodeSize(split); + + totalSplitSize_--; + + totalSplitStorageSize_ -= getNodeSize(split); + + #ifdef TEST + *logofs << "SplitStore: There are " << splits_ -> size() + << " messages in store [" << resource_ << "] with " + << "storage size " << splitStorageSize_ << ".\n" + << logofs_flush; + + *logofs << "SplitStore: Total messages in stores are " + << totalSplitSize_ << " with total storage size " + << totalSplitStorageSize_ << ".\n" + << logofs_flush; + #endif + + #ifdef TEST + + if (splits_ -> size() == 0) + { + if (splitStorageSize_ != 0) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Internal error calculating " + << "split data size. It is " << splitStorageSize_ + << " while should be 0.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Internal error calculating " + << "split data size. It is " << splitStorageSize_ + << " while should be 0.\n"; + + HandleAbort(); + } + } + + #endif +} + +const char *SplitStore::name(const T_checksum checksum) +{ + if (checksum == NULL) + { + return NULL; + } + + char *pathName = control -> ImageCachePath; + + if (pathName == NULL) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Cannot determine directory of " + << "NX image files.\n" << logofs_flush; + #endif + + return NULL; + } + + int pathSize = strlen(pathName); + + // + // File name is "[path][/I-c/I-][checksum][\0]", + // where c is the first hex digit of checksum. + // + + int nameSize = pathSize + 7 + MD5_LENGTH * 2 + 1; + + char *fileName = new char[nameSize]; + + if (fileName == NULL) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Cannot allocate space for " + << "NX image file name.\n" << logofs_flush; + #endif + + return NULL; + } + + strcpy(fileName, pathName); + + sprintf(fileName + pathSize, "/I-%1X/I-", + *((unsigned char *) checksum) >> 4); + + for (unsigned int i = 0; i < MD5_LENGTH; i++) + { + sprintf(fileName + pathSize + 7 + (i * 2), "%02X", + ((unsigned char *) checksum)[i]); + } + + return fileName; +} + +int SplitStore::save(Split *split) +{ + // + // Check if saving the message on the + // persistent cache is enabled. + // + + if (split -> save_ == 0) + { + return 0; + } + + T_checksum checksum = split -> checksum_; + + const char *fileName = name(checksum); + + if (fileName == NULL) + { + return 0; + } + + unsigned int splitSize; + + ostream *fileStream = NULL; + + unsigned char *fileHeader = NULL; + + // + // Get the other data from the split. + // + + unsigned char opcode = split -> store_ -> opcode(); + + unsigned char *data = split -> data_.begin(); + + int dataSize = split -> d_size_; + int compressedSize = split -> c_size_; + + #ifdef DEBUG + *logofs << "SplitStore: Going to save split OPCODE#" + << (unsigned int) opcode << " to file '" << fileName + << "' with size " << dataSize << " and compressed size " + << compressedSize << ".\n" << logofs_flush; + #endif + + DisableSignals(); + + // + // Change the mask to make the file only + // readable by the user, then restore the + // old mask. + // + + mode_t fileMode; + + // + // Check if the file already exists. We try to + // load the message when the split is started + // and save it only if it is not found. Still + // the remote side may send the same image mul- + // tiple time and we may not have the time to + // notify the abort. + // + + struct stat fileStat; + + if (stat(fileName, &fileStat) == 0) + { + #ifdef TEST + *logofs << "SplitStore: Image file '" << fileName + << "' already present on disk.\n" + << logofs_flush; + #endif + + goto SplitStoreSaveError; + } + + fileMode = umask(0077); + + fileStream = new ofstream(fileName, ios::out | ios::binary); + + umask(fileMode); + + if (CheckData(fileStream) < 0) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Cannot open file '" << fileName + << "' for output.\n" << logofs_flush; + #endif + + goto SplitStoreSaveError; + } + + fileHeader = new unsigned char[SPLIT_HEADER_SIZE]; + + if (fileHeader == NULL) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Cannot allocate space for " + << "NX image header.\n" << logofs_flush; + #endif + + goto SplitStoreSaveError; + } + + // + // Leave 3 bytes for future use. Please note + // that, on some CPUs, we can't use PutULONG() + // to write integers that are not aligned to + // the word boundary. + // + + *fileHeader = opcode; + + *(fileHeader + 1) = 0; + *(fileHeader + 2) = 0; + *(fileHeader + 3) = 0; + + PutULONG(dataSize, fileHeader + 4, false); + PutULONG(compressedSize, fileHeader + 8, false); + + splitSize = (compressedSize > 0 ? compressedSize : dataSize); + + if (PutData(fileStream, fileHeader, SPLIT_HEADER_SIZE) < 0 || + PutData(fileStream, data, splitSize) < 0) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Cannot write to NX " + << "image file '" << fileName << "'.\n" + << logofs_flush; + #endif + + goto SplitStoreSaveError; + } + + // + // Check if all the data was written on the + // disk and, if not, remove the faulty copy. + // + + FlushData(fileStream); + + if (CheckData(fileStream) < 0) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Failed to write NX " + << "image file '" << fileName << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Failed to write NX " + << "image file '" << fileName << "'.\n"; + + goto SplitStoreSaveError; + } + + #ifdef TEST + *logofs << "SplitStore: Saved split to file '" << fileName + << "' with data size " << dataSize << " and " + << "compressed data size " << compressedSize + << ".\n" << logofs_flush; + #endif + + delete fileStream; + + delete [] fileName; + delete [] fileHeader; + + EnableSignals(); + + // + // Update the timestamp as the operation + // may have taken some time. + // + + getNewTimestamp(); + + return 1; + +SplitStoreSaveError: + + delete fileStream; + + if (fileName != NULL) + { + unlink(fileName); + } + + delete [] fileName; + delete [] fileHeader; + + EnableSignals(); + + return -1; +} + +int SplitStore::find(Split *split) +{ + const char *fileName = name(split -> checksum_); + + if (fileName == NULL) + { + return 0; + } + + #ifdef DEBUG + *logofs << "SplitStore: Going to find split OPCODE#" + << (unsigned) split -> store_ -> opcode() + << " in file '" << fileName << "'.\n" + << logofs_flush; + #endif + + // + // Check if the file exists and, at the + // same time, update the modification + // time to prevent its deletion. + // + + if (utime(fileName, NULL) == 0) + { + #ifdef TEST + *logofs << "SplitStore: Found split OPCODE#" + << (unsigned) split -> store_ -> opcode() + << " in file '" << fileName << "'.\n" + << logofs_flush; + #endif + + delete [] fileName; + + return 1; + } + + #ifdef TEST + *logofs << "SplitStore: WARNING! Can't find split " + << "OPCODE#" << (unsigned) split -> store_ -> + opcode() << " in file '" << fileName + << "'.\n" << logofs_flush; + #endif + + delete [] fileName; + + return 0; +} + +int SplitStore::load(Split *split) +{ + // + // Check if loading the image is enabled. + // + + if (split -> load_ == 0) + { + return 0; + } + + const char *fileName = name(split -> checksum_); + + if (fileName == NULL) + { + return 0; + } + + unsigned char fileOpcode; + + int fileSize; + int fileCSize; + + istream *fileStream = NULL; + + unsigned char *fileHeader = NULL; + + DisableSignals(); + + #ifdef DEBUG + *logofs << "SplitStore: Going to load split OPCODE#" + << (unsigned int) split -> store_ -> opcode() + << " from file '" << fileName << "'.\n" + << logofs_flush; + #endif + + fileStream = new ifstream(fileName, ios::in | ios::binary); + + if (CheckData(fileStream) < 0) + { + #ifdef TEST + *logofs << "SplitStore: WARNING! Can't open image file '" + << fileName << "' on disk.\n" << logofs_flush; + #endif + + goto SplitStoreLoadError; + } + + fileHeader = new unsigned char[SPLIT_HEADER_SIZE]; + + if (fileHeader == NULL) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Cannot allocate space for " + << "NX image header.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot allocate space for " + << "NX image header.\n"; + + goto SplitStoreLoadError; + } + + if (GetData(fileStream, fileHeader, SPLIT_HEADER_SIZE) < 0) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Cannot read header from " + << "NX image file '" << fileName << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Cannot read header from " + << "NX image file '" << fileName << "'.\n"; + + goto SplitStoreLoadError; + } + + fileOpcode = *fileHeader; + + fileSize = GetULONG(fileHeader + 4, false); + fileCSize = GetULONG(fileHeader + 8, false); + + // + // Don't complain if we find that data was saved + // in compressed form even if we were not aware + // of the compressed data size. The remote side + // compresses the data only at the time it starts + // the transferral of the split. We replace our + // copy of the data with whatever we find on the + // disk. + // + + if (fileOpcode != split -> store_ -> opcode() || + fileSize != split -> d_size_ || + fileSize > control -> MaximumRequestSize || + fileCSize > control -> MaximumRequestSize) + + { + #ifdef TEST + *logofs << "SplitStore: PANIC! Corrupted image file '" << fileName + << "'. Expected " << (unsigned int) split -> store_ -> opcode() + << "/" << split -> d_size_ << "/" << split -> c_size_ << " found " + << (unsigned int) fileOpcode << "/" << fileSize << "/" + << fileCSize << ".\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Corrupted image file '" << fileName + << "'. Expected " << (unsigned int) split -> store_ -> opcode() + << "/" << split -> d_size_ << "/" << split -> c_size_ << " found " + << (unsigned int) fileOpcode << "/" << fileSize << "/" + << fileCSize << ".\n"; + + goto SplitStoreLoadError; + } + + // + // Update the data size with the size + // we got from the disk record. + // + + split -> d_size_ = fileSize; + split -> c_size_ = fileCSize; + + unsigned int splitSize; + + if (fileCSize > 0) + { + splitSize = fileCSize; + } + else + { + splitSize = fileSize; + } + + // + // Allocate a new buffer if we didn't + // do that already or if the size is + // different. + // + + if (split -> data_.size() != splitSize) + { + split -> data_.clear(); + + split -> data_.resize(splitSize); + } + + if (GetData(fileStream, split -> data_.begin(), splitSize) < 0) + { + #ifdef PANIC + *logofs << "SplitStore: PANIC! Cannot read data from " + << "NX image file '" << fileName << "'.\n" + << logofs_flush; + #endif + + cerr << "Warning" << ": Cannot read data from " + << "NX image file '" << fileName << "'.\n"; + + goto SplitStoreLoadError; + } + + delete fileStream; + + delete [] fileHeader; + delete [] fileName; + + EnableSignals(); + + // + // Update the timestamp as the operation + // may have taken some time. + // + + getNewTimestamp(); + + return 1; + +SplitStoreLoadError: + + delete fileStream; + + unlink(fileName); + + delete [] fileName; + delete [] fileHeader; + + EnableSignals(); + + return -1; +} + +Split *CommitStore::pop() +{ + if (splits_ -> size() == 0) + { + #ifdef TEST + *logofs << "CommitStore: The commit store is empty.\n" + << logofs_flush; + #endif + + return NULL; + } + + Split *split = *(splits_ -> begin()); + + splits_ -> pop_front(); + + #ifdef TEST + *logofs << "CommitStore: Removed commit split at the head " + << "of the list with resource " << split -> resource_ + << " request " << (unsigned) split -> store_ -> + opcode() << " position " << split -> position_ + << ".\n" << logofs_flush; + #endif + + return split; +} + +int CommitStore::expand(Split *split, unsigned char *buffer, const int size) +{ + #ifdef TEST + *logofs << "CommitStore: Expanding split data with " + << size << " bytes to write.\n" + << logofs_flush; + #endif + + #ifdef TEST + + if (size < split -> i_size_ + split -> d_size_) + { + #ifdef PANIC + *logofs << "CommitStore: PANIC! Wrong size of the provided " + << "buffer. It should be " << split -> i_size_ + + split -> d_size_ << " instead of " << size + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Wrong size of the provided " + << "buffer. It should be " << split -> i_size_ + + split -> d_size_ << " instead of " << size + << ".\n"; + + HandleAbort(); + } + + #endif + + #ifdef DEBUG + *logofs << "CommitStore: Copying " << split -> i_size_ + << " bytes of identity.\n" << logofs_flush; + #endif + + memcpy(buffer, split -> identity_.begin(), split -> i_size_); + + // + // Copy data, if any, to the buffer. + // + + if (size > split -> i_size_) + { + // + // Check if message has been stored + // in compressed format. + // + + if (split -> c_size_ == 0) + { + #ifdef DEBUG + *logofs << "CommitStore: Copying " << split -> d_size_ + << " bytes of plain data.\n" << logofs_flush; + #endif + + memcpy(buffer + split -> i_size_, split -> data_.begin(), split -> d_size_); + } + else + { + #ifdef DEBUG + *logofs << "CommitStore: Decompressing " << split -> c_size_ + << " bytes and copying " << split -> d_size_ + << " bytes of data.\n" << logofs_flush; + #endif + + if (compressor_ -> + decompressBuffer(buffer + split -> i_size_, + split -> d_size_, split -> data_.begin(), + split -> c_size_) < 0) + { + #ifdef PANIC + *logofs << "CommitStore: PANIC! Split data decompression failed.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Split data decompression failed.\n"; + + return -1; + } + } + } + + return 1; +} + +int CommitStore::update(Split *split) +{ + if (split -> action_ != IS_ADDED) + { + return 0; + } + + // + // We don't need the identity data at + // the encoding side. + // + + if (split -> identity_.size() == 0) + { + #ifdef TEST + *logofs << "SplitStore: Going to update the size " + << "for object at position " << split -> position_ + << " with data size " << split -> d_size_ + << " and compressed data size " << split -> + c_size_ << ".\n" << logofs_flush; + #endif + + split -> store_ -> updateData(split -> position_, split -> d_size_, + split -> c_size_); + } + else + { + #ifdef TEST + *logofs << "SplitStore: Going to update data and size " + << "for object at position " << split -> position_ + << " with data size " << split -> d_size_ + << " and compressed data size " << split -> + c_size_ << ".\n" << logofs_flush; + #endif + + split -> store_ -> updateData(split -> position_, split -> data_.begin(), + split -> d_size_, split -> c_size_); + } + + // + // Unlock message so that we can remove + // or save it on disk at shutdown. + // + + if (split -> action_ == IS_ADDED) + { + split -> store_ -> unlock(split -> position_); + + #ifdef TEST + + validate(split); + + #endif + } + + return 1; +} + +int CommitStore::validate(Split *split) +{ + MessageStore *store = split -> store_; + + int p, n, s; + + s = store -> cacheSlots; + + for (p = 0, n = 0; p < s; p++) + { + if (store -> getLocks(p) == 1) + { + n++; + } + else if (store -> getLocks(p) != 0) + { + #ifdef PANIC + *logofs << "CommitStore: PANIC! Repository for OPCODE#" + << (unsigned int) store -> opcode() << " has " + << store -> getLocks(p) << " locks for message " + << "at position " << p << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Repository for OPCODE#" + << (unsigned int) store -> opcode() << " has " + << store -> getLocks(p) << " locks for message " + << "at position " << p << ".\n"; + + HandleAbort(); + } + } + + #ifdef TEST + *logofs << "CommitStore: Repository for OPCODE#" + << (unsigned int) store -> opcode() + << " has " << n << " locked messages.\n" + << logofs_flush; + #endif + + return 1; +} diff --git a/nxcomp/Split.h b/nxcomp/Split.h new file mode 100644 index 000000000..c9a3c9ad3 --- /dev/null +++ b/nxcomp/Split.h @@ -0,0 +1,535 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Split_H +#define Split_H + +#include "Types.h" +#include "Timestamp.h" +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Define this to know how many splits +// are allocated and deallocated. +// + +#undef REFERENCES + +// +// Size of header of messages saved on +// disk. +// + +#define SPLIT_HEADER_SIZE 12 + +// +// This class is used to divide big messages +// in smaller chunks and send them at idle +// time. +// + +class EncodeBuffer; +class DecodeBuffer; + +class SplitStore; +class CommitStore; + +// +// Preferred message streaming policy. +// + +typedef enum +{ + split_none = -1, + split_async = 1, + split_sync + +} T_split_mode; + +// +// Current state of the split. Used to +// implement the state machine. +// + +typedef enum +{ + split_undefined = -1, + split_added, + split_missed, + split_loaded, + split_aborted, + split_notified + +} T_split_state; + +class Split +{ + friend class SplitStore; + friend class CommitStore; + + public: + + Split(); + + ~Split(); + + // + // Note that, differently from the message + // store, the split store doesn't account + // for the data offset when dealing with + // the data. This means that both the size_ + // and c_size members represent the actual + // size of the data part. + // + + void compressedSize(int size) + { + c_size_ = size; + + store_ -> validateSize(d_size_, c_size_); + } + + int compressedSize() + { + return c_size_; + } + + int plainSize() + { + return i_size_ + d_size_; + } + + T_checksum getChecksum() + { + return checksum_; + } + + MessageStore *getStore() + { + return store_; + } + + T_split_state getState() + { + return state_; + } + + T_store_action getAction() + { + return action_; + } + + // + // We may need to find the resource + // associated to the split message + // because old protocol version use + // a single store for all splits. + // + + int getResource() + { + return resource_; + } + + int getRequest() + { + return store_ -> opcode(); + } + + int getPosition() + { + return position_; + } + + T_split_mode getMode() + { + return mode_; + } + + void setPolicy(int load, int save) + { + load_ = load; + save_ = save; + } + + void setState(T_split_state state) + { + state_ = state; + } + + private: + + // + // The agent's resource which is splitting + // the message. + // + + int resource_; + + // + // Where to find the message in the message + // store or the X sequence number of the + // original request, in recent versions. + // + + int position_; + + // + // Which store is involved. + // + + MessageStore *store_; + + // + // Identity size of the message. + // + + int i_size_; + + // + // This is the uncompressed data size of the + // original message. + // + + int d_size_; + + // + // This is the size of the compressed data, + // if the data is stored in this form. + // + + int c_size_; + + // + // Size of the data buffer, as known by the + // encoding side. This field is only used at + // the decoding side. The remote size can be + // different from the actual data size, if + // the encoding side did not confirm that it + // received the abort split event. + // + + int r_size_; + + // + // Position in the data buffer that will be + // the target of the next send or receive + // operation while streaming the message. + // + + int next_; + + // + // Load or save the split to disk. + // + + int load_; + int save_; + + // + // Checksum of the original message. + // + + T_checksum checksum_; + + // + // Was this split confirmed or aborted? + // + + T_split_state state_; + + // + // What's the policy for sending this split? + // + + T_split_mode mode_; + + // + // Operation that had been performed on the + // store at the time the split was added. + // + + T_store_action action_; + + // + // Container for the identity and data part + // of the X message. + // + + T_data identity_; + T_data data_; + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +class SplitStore +{ + public: + + SplitStore(StaticCompressor *compressor, CommitStore *commits, int resource); + + ~SplitStore(); + + Split *getFirstSplit() const + { + if (splits_ -> size() > 0) + { + return (*(splits_ -> begin())); + } + + return NULL; + } + + Split *getLastSplit() const + { + if (splits_ -> size() > 0) + { + return (*(--(splits_ -> end()))); + } + + return NULL; + } + + int getNodeSize(const Split *split) const + { + // + // Take in account 64 bytes of overhead + // for each node. + // + + return (sizeof(class Split) + 64 + + split -> i_size_ + split -> d_size_); + } + + int getStorageSize() + { + return splitStorageSize_; + } + + static int getTotalSize() + { + return totalSplitSize_; + } + + static int getTotalStorageSize() + { + return totalSplitStorageSize_; + } + + int getResource() + { + return resource_; + } + + int getSize() + { + return splits_ -> size(); + } + + T_splits *getSplits() + { + return splits_; + } + + // + // Used, respectively, at the encoding + // and decoding side. + // + + Split *add(MessageStore *store, int resource, T_split_mode mode, + int position, T_store_action action, T_checksum checksum, + const unsigned char *buffer, const int size); + + Split *add(MessageStore *store, int resource, int position, + T_store_action action, T_checksum checksum, + unsigned char *buffer, const int size); + + // + // Handle the streaming of the message data. + // + + int send(EncodeBuffer &encodeBuffer, int packetSize); + + int receive(DecodeBuffer &decodeBuffer); + + // + // Remove the top element of the split store + // and update the storage size. + // + + void remove(Split *split); + + // + // Load the message from disk and replace the + // message in the store with the new copy. + // + + int load(Split *split); + + // + // Save the data to disk after the message has + // been recomposed at the local side. + // + + int save(Split *split); + + // + // Find the message on disk and update the last + // modification time. This is currently unused. + // + + int find(Split *split); + + // + // Remove the element on top of the queue and + // discard any split data that still needs to + // be transferred. + // + + Split *pop(); + + // + // Dump the content of the store. + // + + void dump(); + + protected: + + // + // Repository where to add the splits. + // + + T_splits *splits_; + + // + // Compress and decompress the data payload. + // + + StaticCompressor *compressor_; + + private: + + int start(EncodeBuffer &encodeBuffer); + + int start(DecodeBuffer &decodeBuffer); + + void push(Split *split); + + // + // Determine the name of the file object based + // on the checksum. + // + + const char *name(const T_checksum checksum); + + // + // The number of elements and data bytes + // in the repository. + // + + int splitStorageSize_; + + static int totalSplitSize_; + static int totalSplitStorageSize_; + + // + // Current element being transferred. + // + + T_splits::iterator current_; + + // + // Repository where to move the splits + // after they are completely recomposed. + // + + CommitStore *commits_; + + // + // Index in the client store or none, + // if this is a commit store. + // + + int resource_; + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +class CommitStore : public SplitStore +{ + // + // This is just a split store. + // + + public: + + CommitStore(StaticCompressor *compressor) + + : SplitStore(compressor, NULL, nothing) + { + } + + // + // Move identity and data of the split to the + // provided buffer, uncompressing the message, + // if needed. + // + + int expand(Split *split, unsigned char *buffer, const int size); + + // + // We recomposed the data part. If the message + // was originally added to the message store, + // replace the data and/or update the size. + // + + int update(Split *split); + + // + // Remove the split from the commit queue. + // + + Split *pop(); + + // + // This is just used for debug. It checks + // if any message in the message store has + // an invalid number of locks. + // + + int validate(Split *split); +}; + +#endif /* Split_H */ diff --git a/nxcomp/StaticCompressor.cpp b/nxcomp/StaticCompressor.cpp new file mode 100644 index 000000000..b30e61a80 --- /dev/null +++ b/nxcomp/StaticCompressor.cpp @@ -0,0 +1,420 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Z.h" +#include "Misc.h" +#include "Control.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +#include "StaticCompressor.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +StaticCompressor::StaticCompressor(int compressionLevel, + int compressionThreshold) +{ + buffer_ = NULL; + bufferSize_ = 0; + + compressionStream_.zalloc = (alloc_func) 0; + compressionStream_.zfree = (free_func) 0; + compressionStream_.opaque = (voidpf) 0; + + decompressionStream_.zalloc = (alloc_func) 0; + decompressionStream_.zfree = (free_func) 0; + decompressionStream_.opaque = (void *) 0; + + decompressionStream_.next_in = (Bytef *) 0; + decompressionStream_.avail_in = 0; + + #ifdef TEST + *logofs << "StaticCompressor: Compression level is " + << compressionLevel << ".\n" << logofs_flush; + #endif + + int result = deflateInit2(&compressionStream_, compressionLevel, Z_DEFLATED, + 15, 9, Z_DEFAULT_STRATEGY); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Cannot initialize the " + << "compression stream. Error is '" << zError(result) + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot initialize the compression " + << "stream. Error is '" << zError(result) << "'.\n"; + + HandleAbort(); + } + + result = inflateInit2(&decompressionStream_, 15); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Cannot initialize the " + << "decompression stream. Error is '" << zError(result) + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot initialize the decompression " + << "stream. Error is '" << zError(result) << "'.\n"; + + HandleAbort(); + } + + #ifdef TEST + *logofs << "StaticCompressor: Compression threshold is " + << compressionThreshold << ".\n" << logofs_flush; + #endif + + threshold_ = compressionThreshold; +} + +StaticCompressor::~StaticCompressor() +{ + int result = deflateEnd(&compressionStream_); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Cannot deinitialize the " + << "compression stream. Error is '" << zError(result) + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot deinitialize the compression " + << "stream. Error is '" << zError(result) << "'.\n"; + } + + result = inflateEnd(&decompressionStream_); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Cannot deinitialize the " + << "decompression stream. Error is '" << zError(result) + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot deinitialize the decompression " + << "stream. Error is '" << zError(result) << "'.\n"; + } + + delete [] buffer_; +} + +// +// This function compresses and encodes the compressed +// buffer. It returns a pointer to the internal buffer +// where data was compressed. +// + +int StaticCompressor::compressBuffer(const unsigned char *plainBuffer, + const unsigned int plainSize, + unsigned char *&compressedBuffer, + unsigned int &compressedSize, + EncodeBuffer &encodeBuffer) +{ + if (control -> LocalDataCompression == 0 || + compressBuffer(plainBuffer, plainSize, + compressedBuffer, compressedSize) <= 0) + { + encodeBuffer.encodeBoolValue(0); + + encodeBuffer.encodeMemory(plainBuffer, plainSize); + + return 0; + } + else + { + encodeBuffer.encodeBoolValue(1); + + encodeBuffer.encodeValue(compressedSize, 32, 14); + encodeBuffer.encodeValue(plainSize, 32, 14); + + encodeBuffer.encodeMemory(compressedBuffer, compressedSize); + + return 1; + } +} + +// +// This function compresses data into a dynamically +// allocated buffer and returns a pointer to it, so +// application must copy data before the next call. +// + +int StaticCompressor::compressBuffer(const unsigned char *plainBuffer, + const unsigned int plainSize, + unsigned char *&compressedBuffer, + unsigned int &compressedSize) +{ + #ifdef DEBUG + *logofs << "StaticCompressor: Called for buffer at " + << (void *) plainBuffer << ".\n" + << logofs_flush; + #endif + + compressedSize = plainSize; + + if (plainSize < (unsigned int) threshold_) + { + #ifdef TEST + *logofs << "StaticCompressor: Leaving buffer unchanged. " + << "Plain size is " << plainSize << " with threshold " + << (unsigned int) threshold_ << ".\n" << logofs_flush; + #endif + + return 0; + } + + // + // Determine the size of the temporary + // buffer. + // + + unsigned int newSize = plainSize + (plainSize / 1000) + 12; + + // + // Allocate a new buffer if it grows + // beyond 64K. + // + + if (buffer_ == NULL || (bufferSize_ > 65536 && + newSize < bufferSize_ / 2) || newSize > bufferSize_) + { + delete [] buffer_; + + buffer_ = new unsigned char[newSize]; + + if (buffer_ == NULL) + { + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Can't allocate compression " + << "buffer of " << newSize << " bytes. Error is " << EGET() + << " ' " << ESTR() << "'.\n" << logofs_flush; + #endif + + cerr << "Warning" << ": Can't allocate compression buffer of " + << newSize << " bytes. Error is " << EGET() + << " '" << ESTR() << "'.\n"; + + bufferSize_ = 0; + + return 0; + } + + bufferSize_ = newSize; + } + + unsigned int resultingSize = newSize; + + int result = ZCompress(&compressionStream_, buffer_, &resultingSize, + plainBuffer, plainSize); + + if (result == Z_OK) + { + if (resultingSize > newSize) + { + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Overflow in compression " + << "buffer size. " << "Expected size was " << newSize + << " while it is " << resultingSize << ".\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Overflow in compress buffer size. " + << "Expected size was " << newSize << " while it is " + << resultingSize << ".\n"; + + return -1; + } + else if (resultingSize >= plainSize) + { + #ifdef TEST + *logofs << "StaticCompressor: Leaving buffer unchanged. " + << "Plain size is " << plainSize << " compressed " + << "size is " << resultingSize << ".\n" + << logofs_flush; + #endif + + return 0; + } + + compressedBuffer = buffer_; + compressedSize = resultingSize; + + #ifdef TEST + *logofs << "StaticCompressor: Compressed buffer from " + << plainSize << " to " << resultingSize + << " bytes.\n" << logofs_flush; + #endif + + return 1; + } + + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Failed compression of buffer. " + << "Error is '" << zError(result) << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failed compression of buffer. " + << "Error is '" << zError(result) << "'.\n"; + + return -1; +} + +int StaticCompressor::decompressBuffer(unsigned char *plainBuffer, + unsigned int plainSize, + const unsigned char *&compressedBuffer, + unsigned int &compressedSize, + DecodeBuffer &decodeBuffer) +{ + #ifdef DEBUG + *logofs << "StaticCompressor: Called for buffer at " + << (void *) plainBuffer << ".\n" + << logofs_flush; + #endif + + unsigned int value; + + decodeBuffer.decodeBoolValue(value); + + if (value == 0) + { + memcpy(plainBuffer, + decodeBuffer.decodeMemory(plainSize), + plainSize); + + return 0; + } + + unsigned int checkSize = plainSize; + + decodeBuffer.decodeValue(value, 32, 14); + compressedSize = value; + + decodeBuffer.decodeValue(value, 32, 14); + checkSize = value; + + // + // If caller needs the original compressed + // data it must copy this to its own buffer + // before using any further decode function. + // + + compressedBuffer = decodeBuffer.decodeMemory(compressedSize); + + int result = ZDecompress(&decompressionStream_, plainBuffer, &checkSize, + compressedBuffer, compressedSize); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Failure decompressing buffer. " + << "Error is '" << zError(result) << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Failure decompressing buffer. " + << "Error is '" << zError(result) << "'.\n"; + + return -1; + } + else if (plainSize != checkSize) + { + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Expected decompressed size was " + << plainSize << " while it is " << checkSize + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Expected decompressed size was " + << plainSize << " while it is " << checkSize + << ".\n"; + + return -1; + } + + return 1; +} + +// +// This is used to uncompress on-the-fly +// messages whose data has been stored +// in compressed format. +// + +int StaticCompressor::decompressBuffer(unsigned char *plainBuffer, + const unsigned int plainSize, + const unsigned char *compressedBuffer, + const unsigned int compressedSize) +{ + #ifdef TEST + *logofs << "StaticCompressor: Called for buffer at " + << (void *) plainBuffer << ".\n" + << logofs_flush; + #endif + + unsigned int checkSize = plainSize; + + int result = ZDecompress(&decompressionStream_, plainBuffer, &checkSize, + compressedBuffer, compressedSize); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Failure decompressing buffer. " + << "Error is '" << zError(result) << "'.\n" + << logofs_flush; + #endif + + return -1; + } + + if (plainSize != checkSize) + { + #ifdef PANIC + *logofs << "StaticCompressor: PANIC! Expected decompressed size was " + << plainSize << " while it is " << checkSize + << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Expected decompressed size was " + << plainSize << " while it is " << checkSize + << ".\n"; + + return -1; + } + + #ifdef TEST + *logofs << "StaticCompressor: Decompressed buffer from " + << compressedSize << " to " << plainSize + << " bytes.\n" << logofs_flush; + #endif + + return 1; +} diff --git a/nxcomp/StaticCompressor.h b/nxcomp/StaticCompressor.h new file mode 100644 index 000000000..3e5b25ca1 --- /dev/null +++ b/nxcomp/StaticCompressor.h @@ -0,0 +1,72 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef StaticCompressor_H +#define StaticCompressor_H + +#include "Z.h" + +class EncodeBuffer; +class DecodeBuffer; + +class StaticCompressor +{ + public: + + StaticCompressor(int compressionLevel, int compressionThreshold); + + ~StaticCompressor(); + + int compressBuffer(const unsigned char *plainBuffer, const unsigned int plainSize, + unsigned char *&compressedBuffer, unsigned int &compressedSize, + EncodeBuffer &encodeBuffer); + + int compressBuffer(const unsigned char *plainBuffer, const unsigned int plainSize, + unsigned char *&compressedBuffer, unsigned int &compressedSize); + + int decompressBuffer(unsigned char *plainBuffer, unsigned int plainSize, + const unsigned char *&compressedBuffer, unsigned int &compressedSize, + DecodeBuffer &decodeBuffer); + + int decompressBuffer(unsigned char *plainBuffer, const unsigned int plainSize, + const unsigned char *compressedBuffer, const unsigned compressedSize); + + static int isCompressionLevel(int compressionLevel) + { + return (compressionLevel == Z_DEFAULT_COMPRESSION || + (compressionLevel >= Z_NO_COMPRESSION && + compressionLevel <= Z_BEST_COMPRESSION)); + } + + int fullReset() + { + return (deflateReset(&compressionStream_) == Z_OK && + inflateReset(&decompressionStream_) == Z_OK); + } + + private: + + z_stream compressionStream_; + z_stream decompressionStream_; + + unsigned char *buffer_; + unsigned int bufferSize_; + + int threshold_; +}; + +#endif diff --git a/nxcomp/Statistics.cpp b/nxcomp/Statistics.cpp new file mode 100644 index 000000000..294518fb7 --- /dev/null +++ b/nxcomp/Statistics.cpp @@ -0,0 +1,1995 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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/Statistics.h b/nxcomp/Statistics.h new file mode 100644 index 000000000..44ff8834f --- /dev/null +++ b/nxcomp/Statistics.h @@ -0,0 +1,737 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Statistics_H +#define Statistics_H + +#include "NXproto.h" + +#include "Misc.h" +#include "Timestamp.h" + +class Proxy; + +// +// Opcode 255 is for generic requests, 1 is for +// generic replies (those which haven't a speci- +// fic differential encoding), opcode 0 is for +// generic messages from the auxiliary channels. +// + +#define STATISTICS_OPCODE_MAX 256 + +// +// Maximum length of the buffer allocated for +// the statistics output. +// + +#define STATISTICS_LENGTH 16384 + +// +// Query type. +// + +#define TOTAL_STATS 1 +#define PARTIAL_STATS 2 +#define NO_STATS 3 + +// +// Log level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Log the operations related to updating +// the control tokens. +// + +#undef TOKEN + +class Statistics +{ + public: + + // + // Use the proxy class to get access + // to the message stores. + // + + Statistics(Proxy *proxy); + + ~Statistics(); + + void addIdleTime(unsigned int numMs) + { + transportPartial_.idleTime_ += numMs; + transportTotal_.idleTime_ += numMs; + } + + void subIdleTime(unsigned int numMs) + { + transportPartial_.idleTime_ -= numMs; + transportTotal_.idleTime_ -= numMs; + } + + void addReadTime(unsigned int numMs) + { + transportPartial_.readTime_ += numMs; + transportTotal_.readTime_ += numMs; + } + + void subReadTime(unsigned int numMs) + { + transportPartial_.readTime_ -= numMs; + transportTotal_.readTime_ -= numMs; + } + + void addWriteTime(unsigned int numMs) + { + transportPartial_.writeTime_ += numMs; + transportTotal_.writeTime_ += numMs; + } + + void subWriteTime(unsigned int numMs) + { + transportPartial_.writeTime_ -= numMs; + transportTotal_.writeTime_ -= numMs; + } + + void addBytesIn(unsigned int numBytes) + { + transportPartial_.proxyBytesIn_ += numBytes; + transportTotal_.proxyBytesIn_ += numBytes; + } + + double getBytesIn() + { + return transportTotal_.proxyBytesIn_; + } + + void addBytesOut(unsigned int numBytes) + { + transportPartial_.proxyBytesOut_ += numBytes; + transportTotal_.proxyBytesOut_ += numBytes; + } + + double getBytesOut() + { + return transportTotal_.proxyBytesOut_; + } + + void addFrameIn() + { + transportPartial_.proxyFramesIn_++; + transportTotal_.proxyFramesIn_++; + + #ifdef TEST + *logofs << "Statistics: Updated total proxy frames in to " + << transportTotal_.proxyFramesIn_ << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + } + + void addFrameOut() + { + transportPartial_.proxyFramesOut_++; + transportTotal_.proxyFramesOut_++; + + #ifdef TEST + *logofs << "Statistics: Updated total proxy frames out to " + << transportTotal_.proxyFramesOut_ << " at " + << strMsTimestamp() << ".\n" + << logofs_flush; + #endif + } + + void addWriteOut() + { + transportPartial_.proxyWritesOut_++; + transportTotal_.proxyWritesOut_++; + + #ifdef TEST + *logofs << "Statistics: Updated total proxy writes out to " + << transportTotal_.proxyWritesOut_ << " at " + << strMsTimestamp() << ".\n" << logofs_flush; + #endif + } + + void addCompressedBytes(unsigned int bytesIn, unsigned int bytesOut); + + void addDecompressedBytes(unsigned int bytesIn, unsigned int bytesOut) + { + transportPartial_.decompressedBytesIn_ += bytesIn; + transportTotal_.decompressedBytesIn_ += bytesIn; + + transportPartial_.decompressedBytesOut_ += bytesOut; + transportTotal_.decompressedBytesOut_ += bytesOut; + } + + void addFramingBits(unsigned int bitsOut) + { + transportPartial_.framingBitsOut_ += bitsOut; + transportTotal_.framingBitsOut_ += bitsOut; + + proxyData_.protocolCount_ += bitsOut; + } + + void addCachedRequest(unsigned int opcode) + { + protocolPartial_.requestCached_[opcode]++; + protocolTotal_.requestCached_[opcode]++; + } + + void addRenderCachedRequest(unsigned int opcode) + { + protocolPartial_.renderRequestCached_[opcode]++; + protocolTotal_.renderRequestCached_[opcode]++; + } + + void addRepliedRequest(unsigned int opcode) + { + protocolPartial_.requestReplied_[opcode]++; + protocolTotal_.requestReplied_[opcode]++; + } + + void addCachedReply(unsigned int opcode) + { + protocolPartial_.replyCached_[opcode]++; + protocolTotal_.replyCached_[opcode]++; + } + + void addRequestBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut) + { + #ifdef DEBUG + *logofs << "Statistcs: Added " << bitsIn << " bits in and " + << bitsOut << " bits out to opcode " << opcode + << ".\n" << logofs_flush; + #endif + + protocolPartial_.requestCount_[opcode]++; + protocolTotal_.requestCount_[opcode]++; + + protocolPartial_.requestBitsIn_[opcode] += bitsIn; + protocolTotal_.requestBitsIn_[opcode] += bitsIn; + + protocolPartial_.requestBitsOut_[opcode] += bitsOut; + protocolTotal_.requestBitsOut_[opcode] += bitsOut; + + // + // Don't account the split bits + // to the control token. + // + + if (opcode != X_NXSplitData && opcode != X_NXSplitEvent) + { + proxyData_.protocolCount_ += bitsOut; + } + } + + void addRenderRequestBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut) + { + #ifdef DEBUG + *logofs << "Statistcs: Added " << bitsIn << " bits in and " + << bitsOut << " bits out to render opcode " << opcode + << ".\n" << logofs_flush; + #endif + + protocolPartial_.renderRequestCount_[opcode]++; + protocolTotal_.renderRequestCount_[opcode]++; + + protocolPartial_.renderRequestBitsIn_[opcode] += bitsIn; + protocolTotal_.renderRequestBitsIn_[opcode] += bitsIn; + + protocolPartial_.renderRequestBitsOut_[opcode] += bitsOut; + protocolTotal_.renderRequestBitsOut_[opcode] += bitsOut; + } + + void addReplyBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut) + { + protocolPartial_.replyCount_[opcode]++; + protocolTotal_.replyCount_[opcode]++; + + protocolPartial_.replyBitsIn_[opcode] += bitsIn; + protocolTotal_.replyBitsIn_[opcode] += bitsIn; + + protocolPartial_.replyBitsOut_[opcode] += bitsOut; + protocolTotal_.replyBitsOut_[opcode] += bitsOut; + + proxyData_.protocolCount_ += bitsOut; + } + + void addEventBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut) + { + protocolPartial_.eventCount_[opcode]++; + protocolTotal_.eventCount_[opcode]++; + + protocolPartial_.eventBitsIn_[opcode] += bitsIn; + protocolTotal_.eventBitsIn_[opcode] += bitsIn; + + protocolPartial_.eventBitsOut_[opcode] += bitsOut; + protocolTotal_.eventBitsOut_[opcode] += bitsOut; + + proxyData_.protocolCount_ += bitsOut; + } + + void addCupsBits(unsigned int bitsIn, unsigned int bitsOut) + { + protocolPartial_.cupsCount_++; + protocolTotal_.cupsCount_++; + + protocolPartial_.cupsBitsIn_ += bitsIn; + protocolTotal_.cupsBitsIn_ += bitsIn; + + protocolPartial_.cupsBitsOut_ += bitsOut; + protocolTotal_.cupsBitsOut_ += bitsOut; + } + + void addSmbBits(unsigned int bitsIn, unsigned int bitsOut) + { + protocolPartial_.smbCount_++; + protocolTotal_.smbCount_++; + + protocolPartial_.smbBitsIn_ += bitsIn; + protocolTotal_.smbBitsIn_ += bitsIn; + + protocolPartial_.smbBitsOut_ += bitsOut; + protocolTotal_.smbBitsOut_ += bitsOut; + } + + void addMediaBits(unsigned int bitsIn, unsigned int bitsOut) + { + protocolPartial_.mediaCount_++; + protocolTotal_.mediaCount_++; + + protocolPartial_.mediaBitsIn_ += bitsIn; + protocolTotal_.mediaBitsIn_ += bitsIn; + + protocolPartial_.mediaBitsOut_ += bitsOut; + protocolTotal_.mediaBitsOut_ += bitsOut; + } + + void addHttpBits(unsigned int bitsIn, unsigned int bitsOut) + { + protocolPartial_.httpCount_++; + protocolTotal_.httpCount_++; + + protocolPartial_.httpBitsIn_ += bitsIn; + protocolTotal_.httpBitsIn_ += bitsIn; + + protocolPartial_.httpBitsOut_ += bitsOut; + protocolTotal_.httpBitsOut_ += bitsOut; + } + + void addFontBits(unsigned int bitsIn, unsigned int bitsOut) + { + protocolPartial_.fontCount_++; + protocolTotal_.fontCount_++; + + protocolPartial_.fontBitsIn_ += bitsIn; + protocolTotal_.fontBitsIn_ += bitsIn; + + protocolPartial_.fontBitsOut_ += bitsOut; + protocolTotal_.fontBitsOut_ += bitsOut; + } + + void addSlaveBits(unsigned int bitsIn, unsigned int bitsOut) + { + protocolPartial_.slaveCount_++; + protocolTotal_.slaveCount_++; + + protocolPartial_.slaveBitsIn_ += bitsIn; + protocolTotal_.slaveBitsIn_ += bitsIn; + + protocolPartial_.slaveBitsOut_ += bitsOut; + protocolTotal_.slaveBitsOut_ += bitsOut; + } + + void addPackedBytesIn(unsigned int numBytes) + { + packedPartial_.packedBytesIn_ += numBytes; + packedTotal_.packedBytesIn_ += numBytes; + } + + void addPackedBytesOut(unsigned int numBytes) + { + packedPartial_.packedBytesOut_ += numBytes; + packedTotal_.packedBytesOut_ += numBytes; + } + + void addSplit() + { + splitPartial_.splitCount_++; + splitTotal_.splitCount_++; + } + + void addSplitAborted() + { + splitPartial_.splitAborted_++; + splitTotal_.splitAborted_++; + } + + void addSplitAbortedBytesOut(unsigned int numBytes) + { + splitPartial_.splitAbortedBytesOut_ += numBytes; + splitTotal_.splitAbortedBytesOut_ += numBytes; + } + + // + // Add the recorded protocol data to the proxy + // token counters. We want to send bpth the token + // request message and the data payload using a + // single system write, so we must guess how many + // output bytes we will generate. + // + + void updateControlToken(int &count) + { + // + // Total is the total number of protocol bytes + // generated so far. We have saved the number + // of bytes generated the last time the function + // was called so we can calculate the difference. + // + // The number of protocol bits is updated as soon + // as new bits are accumulated, to avoid summing + // up all the opcodes in this routine. We add a + // byte to the control bytes difference to account + // for the framing bits that are very likely to + // be added to the payload. + // + + double total = proxyData_.protocolCount_ / 8; + + double diff = total - proxyData_.controlCount_ + 1; + + #if defined(TEST) || defined(TOKEN) + *logofs << "Statistics: TOKEN! Protocol bytes are " + << total << " control bytes are " + << proxyData_.controlCount_ << " difference is " + << diff << ".\n" << logofs_flush; + #endif + + count += (int) (diff / proxyData_.streamRatio_); + + #if defined(TEST) || defined(TOKEN) + *logofs << "Statistics: TOKEN! Adding " + << (int) (diff / proxyData_.streamRatio_) + << " bytes to the control token with ratio " + << proxyData_.streamRatio_ << ".\n" + << logofs_flush; + #endif + + proxyData_.controlCount_ = total; + + #if defined(TEST) || defined(TOKEN) + *logofs << "Statistics: TOKEN! New control token has " + << count << " bytes with total control bytes " + << proxyData_.controlCount_ << ".\n" + << logofs_flush; + #endif + } + + void updateSplitToken(int &count) + { + double total = (protocolTotal_.requestBitsOut_[X_NXSplitData] + + protocolTotal_.eventBitsOut_[X_NXSplitEvent]) / 8; + + double diff = total - proxyData_.splitCount_; + + #if defined(TEST) || defined(TOKEN) + *logofs << "Statistics: TOKEN! Protocol bytes are " + << total << " split bytes are " + << proxyData_.splitCount_ << " difference is " + << diff << ".\n" << logofs_flush; + #endif + + count += (int) (diff / proxyData_.streamRatio_); + + #if defined(TEST) || defined(TOKEN) + *logofs << "Statistics: TOKEN! Adding " + << (int) (diff / proxyData_.streamRatio_) + << " bytes to the split token with ratio " + << proxyData_.streamRatio_ << ".\n" + << logofs_flush; + #endif + + proxyData_.splitCount_ = total; + + #if defined(TEST) || defined(TOKEN) + *logofs << "Statistics: TOKEN! New split token has " + << count << " bytes with total split bytes " + << proxyData_.splitCount_ << ".\n" + << logofs_flush; + #endif + } + + void updateDataToken(int &count) + { + double total = (protocolTotal_.cupsBitsOut_ + + protocolTotal_.smbBitsOut_ + + protocolTotal_.mediaBitsOut_ + + protocolTotal_.httpBitsOut_ + + protocolTotal_.fontBitsOut_ + + protocolTotal_.slaveBitsOut_) / 8; + + double diff = total - proxyData_.dataCount_; + + #if defined(TEST) || defined(TOKEN) + *logofs << "Statistics: TOKEN! Protocol bytes are " + << total << " data bytes are " + << proxyData_.dataCount_ << " difference is " + << diff << ".\n" << logofs_flush; + #endif + + count += (int) (diff / proxyData_.streamRatio_); + + #if defined(TEST) || defined(TOKEN) + *logofs << "Statistics: TOKEN! Adding " + << (int) (diff / proxyData_.streamRatio_) + << " bytes to the data token with ratio " + << proxyData_.streamRatio_ << ".\n" + << logofs_flush; + #endif + + proxyData_.dataCount_ = total; + + #if defined(TEST) || defined(TOKEN) + *logofs << "Statistics: TOKEN! New data token has " + << count << " bytes with total data bytes " + << proxyData_.dataCount_ << ".\n" + << logofs_flush; + #endif + } + + // + // Update the current bitrate. + // + + void updateBitrate(int bytes); + + // + // Return the current bitrate. + // + + int getBitrateInShortFrame() + { + return bitrateInShortFrame_; + } + + int getBitrateInLongFrame() + { + return bitrateInLongFrame_; + } + + int getTopBitrate() + { + return topBitrate_; + } + + void resetTopBitrate() + { + topBitrate_ = 0; + } + + double getStreamRatio() + { + return proxyData_.streamRatio_; + } + + // + // Manage the congestion level. + // + + void updateCongestion(int remaining, int limit); + + double getCongestionInFrame() + { + return congestionInFrame_; + } + + // + // Produce a dump of the statistics on + // the provided buffer. + // + + int getClientCacheStats(int type, char *&buffer); + int getClientProtocolStats(int type, char *&buffer); + int getClientOverallStats(int type, char *&buffer); + + int getServerCacheStats(int type, char *&buffer); + int getServerProtocolStats(int type, char *&buffer); + int getServerOverallStats(int type, char *&buffer); + + int resetPartialStats(); + + private: + + int getTimeStats(int type, char *&buffer); + int getServicesStats(int type, char *&buffer); + int getFramingStats(int type, char *&buffer); + int getBitrateStats(int type, char *&buffer); + int getStreamStats(int type, char *&buffer); + int getSplitStats(int type, char *&buffer); + + struct T_protocolData + { + double requestCached_[STATISTICS_OPCODE_MAX]; + double requestReplied_[STATISTICS_OPCODE_MAX]; + double requestCount_[STATISTICS_OPCODE_MAX]; + double requestBitsIn_[STATISTICS_OPCODE_MAX]; + double requestBitsOut_[STATISTICS_OPCODE_MAX]; + + double renderRequestCached_[STATISTICS_OPCODE_MAX]; + double renderRequestCount_[STATISTICS_OPCODE_MAX]; + double renderRequestBitsIn_[STATISTICS_OPCODE_MAX]; + double renderRequestBitsOut_[STATISTICS_OPCODE_MAX]; + + double replyCached_[STATISTICS_OPCODE_MAX]; + double replyCount_[STATISTICS_OPCODE_MAX]; + double replyBitsIn_[STATISTICS_OPCODE_MAX]; + double replyBitsOut_[STATISTICS_OPCODE_MAX]; + + double eventCached_[STATISTICS_OPCODE_MAX]; + double eventCount_[STATISTICS_OPCODE_MAX]; + double eventBitsIn_[STATISTICS_OPCODE_MAX]; + double eventBitsOut_[STATISTICS_OPCODE_MAX]; + + double cupsCount_; + double cupsBitsIn_; + double cupsBitsOut_; + + double smbCount_; + double smbBitsIn_; + double smbBitsOut_; + + double mediaCount_; + double mediaBitsIn_; + double mediaBitsOut_; + + double httpCount_; + double httpBitsIn_; + double httpBitsOut_; + + double fontCount_; + double fontBitsIn_; + double fontBitsOut_; + + double slaveCount_; + double slaveBitsIn_; + double slaveBitsOut_; + }; + + struct T_transportData + { + double idleTime_; + double readTime_; + double writeTime_; + + double proxyBytesIn_; + double proxyBytesOut_; + + double proxyFramesIn_; + double proxyFramesOut_; + double proxyWritesOut_; + + double compressedBytesIn_; + double compressedBytesOut_; + + double decompressedBytesIn_; + double decompressedBytesOut_; + + double framingBitsOut_; + }; + + struct T_packedData + { + double packedBytesIn_; + double packedBytesOut_; + }; + + struct T_splitData + { + double splitCount_; + double splitAborted_; + double splitAbortedBytesOut_; + }; + + struct T_overallData + { + double overallBytesIn_; + double overallBytesOut_; + }; + + struct T_proxyData + { + double protocolCount_; + double controlCount_; + double splitCount_; + double dataCount_; + + double streamRatio_; + }; + + T_protocolData protocolPartial_; + T_protocolData protocolTotal_; + + T_transportData transportPartial_; + T_transportData transportTotal_; + + T_packedData packedPartial_; + T_packedData packedTotal_; + + T_splitData splitPartial_; + T_splitData splitTotal_; + + T_overallData overallPartial_; + T_overallData overallTotal_; + + T_proxyData proxyData_; + + // + // Used to calculate the bandwidth usage + // of the proxy link. + // + + T_timestamp startShortFrameTs_; + T_timestamp startLongFrameTs_; + T_timestamp startFrameTs_; + + int bytesInShortFrame_; + int bytesInLongFrame_; + + int bitrateInShortFrame_; + int bitrateInLongFrame_; + + int topBitrate_; + + double congestionInFrame_; + + // + // Need the proxy pointer to print the + // statistics related to the client and + // server stores and to add the protocol + // data to the proxy token accumulators. + // + + Proxy *proxy_; +}; + +#endif /* Statistics_H */ diff --git a/nxcomp/TextCompressor.cpp b/nxcomp/TextCompressor.cpp new file mode 100644 index 000000000..16131222c --- /dev/null +++ b/nxcomp/TextCompressor.cpp @@ -0,0 +1,77 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "TextCompressor.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// The compression obtained by this class is +// very poor. In newer versions the text is +// simply appended to the encode buffer and +// compressed by leveraging the final stream +// compression. +// + +void +TextCompressor::encodeChar(unsigned char ch, EncodeBuffer& encodeBuffer) +{ + // encode each successive character of text using + // a predictive model where most of the last 3 characters + // (low order 7 bits of the previous character, plus the + // low order 5 bits of the character before that, plus + // the low order 3 bits of the character before that) + // are used to find the right cache... + + CharCache& cache = cache_[key_ % cacheSize_]; + if ((key_ >= 128) && (cache.getSize() == 0)) + { + // 3rd-order model doesn't have any statistics yet, + // so use the 1st-order one instead + CharCache& cache2 = cache_[(key_ & 0x7f) % cacheSize_]; + encodeBuffer.encodeCachedValue((unsigned int) ch, 8, cache2); + cache.insert(ch); + } + else + { + encodeBuffer.encodeCachedValue((unsigned int) ch, 8, cache); + } + + key_ = (((key_ & 0x1f) << 7) | ((key_ & 0x380) << 5) | (ch & 0x7f)); +} + + +unsigned char +TextCompressor::decodeChar(DecodeBuffer& decodeBuffer) +{ + unsigned char nextChar; + CharCache& cache = cache_[key_ % cacheSize_]; + if ((key_ >= 128) && (cache.getSize() == 0)) + { + CharCache& cache2 = cache_[(key_ & 0x7f) % cacheSize_]; + decodeBuffer.decodeCachedValue(nextChar, 8, cache2); + cache.insert(nextChar); + } + else + { + decodeBuffer.decodeCachedValue(nextChar, 8, cache); + } + + key_ = (((key_ & 0x1f) << 7) | ((key_ & 0x380) << 5) | (nextChar & 0x7f)); + return nextChar; +} diff --git a/nxcomp/TextCompressor.h b/nxcomp/TextCompressor.h new file mode 100644 index 000000000..b373b98b8 --- /dev/null +++ b/nxcomp/TextCompressor.h @@ -0,0 +1,49 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef TextCompressor_H +#define TextCompressor_H + +#include "CharCache.h" + +class EncodeBuffer; +class DecodeBuffer; + +class TextCompressor +{ + public: + TextCompressor(CharCache* cache, unsigned int cacheSize): + cache_(cache), + cacheSize_(cacheSize), + key_(0) + { + } + + void encodeChar(unsigned char ch, EncodeBuffer &); + unsigned char decodeChar(DecodeBuffer &); + void reset(unsigned int newKey = 0) + { + key_ = newKey; + } + + private: + CharCache* cache_; + unsigned int cacheSize_; + unsigned int key_; +}; + +#endif /* TextCompressor_H */ diff --git a/nxcomp/Timestamp.cpp b/nxcomp/Timestamp.cpp new file mode 100644 index 000000000..295eb65cf --- /dev/null +++ b/nxcomp/Timestamp.cpp @@ -0,0 +1,65 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Timestamp.h" + +// +// Log level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Last timestamp taken from the system. +// + +T_timestamp timestamp; + +// +// The following functions all use the ctime +// static buffer from the C library. +// + +char *strTimestamp(const T_timestamp &ts) +{ + char *ctime_now = ctime((time_t *) &ts.tv_sec); + + ctime_now[24] = '\0'; + + return ctime_now; +} + +// +// This is especially dirty. +// + +char *strMsTimestamp(const T_timestamp &ts) +{ + char *ctime_now = ctime((time_t *) &ts.tv_sec); + + char ctime_new[25]; + + sprintf(ctime_new, "%.8s:%3.3f", ctime_now + 11, + (float) ts.tv_usec / 1000); + + strncpy(ctime_now, ctime_new, 24); + + return ctime_now; +} diff --git a/nxcomp/Timestamp.h b/nxcomp/Timestamp.h new file mode 100644 index 000000000..69953988a --- /dev/null +++ b/nxcomp/Timestamp.h @@ -0,0 +1,299 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Timestamp_H +#define Timestamp_H + +#include <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/TranslateCoords.cpp b/nxcomp/TranslateCoords.cpp new file mode 100644 index 000000000..e67e1dac5 --- /dev/null +++ b/nxcomp/TranslateCoords.cpp @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "TranslateCoords.h" + +#include "ClientCache.h" + +#include "EncodeBuffer.h" +#include "DecodeBuffer.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Here are the methods to handle messages' content. +// + +int TranslateCoordsStore::parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message; + + // + // Here is the fingerprint. + // + + translateCoords -> src_window = GetULONG(buffer + 4, bigEndian); + translateCoords -> dst_window = GetULONG(buffer + 8, bigEndian); + + translateCoords -> src_x = GetUINT(buffer + 12, bigEndian); + translateCoords -> src_y = GetUINT(buffer + 14, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +int TranslateCoordsStore::unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message; + + // + // Fill all the message's fields. + // + + PutULONG(translateCoords -> src_window, buffer + 4, bigEndian); + PutULONG(translateCoords -> dst_window, buffer + 8, bigEndian); + + PutUINT(translateCoords -> src_x, buffer + 12, bigEndian); + PutUINT(translateCoords -> src_y, buffer + 14, bigEndian); + + #ifdef DEBUG + *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush; + #endif + + return 1; +} + +void TranslateCoordsStore::dumpIdentity(const Message *message) const +{ + #ifdef DUMP + + TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message; + + *logofs << name() << ": Identity src_window " << translateCoords -> src_window << ", dst_window " + << translateCoords -> dst_window << ", src_x " << translateCoords -> src_x << ", src_y " + << translateCoords -> src_y << ", size " << translateCoords -> size_ << ".\n" << logofs_flush; + + #endif +} + +void TranslateCoordsStore::identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const +{ + md5_append(md5_state_, buffer + 4, 4); + md5_append(md5_state_, buffer + 8, 4); + md5_append(md5_state_, buffer + 12, 2); + md5_append(md5_state_, buffer + 14, 2); +} diff --git a/nxcomp/TranslateCoords.h b/nxcomp/TranslateCoords.h new file mode 100644 index 000000000..3f21b243c --- /dev/null +++ b/nxcomp/TranslateCoords.h @@ -0,0 +1,177 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef TranslateCoords_H +#define TranslateCoords_H + +#include "Message.h" + +// +// Set the verbosity level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG +#undef DUMP + +// +// Set default values. +// + +#define TRANSLATECOORDS_ENABLE_CACHE 1 +#define TRANSLATECOORDS_ENABLE_DATA 0 +#define TRANSLATECOORDS_ENABLE_SPLIT 0 +#define TRANSLATECOORDS_ENABLE_COMPRESS 0 + +#define TRANSLATECOORDS_DATA_LIMIT 0 +#define TRANSLATECOORDS_DATA_OFFSET 16 + +#define TRANSLATECOORDS_CACHE_SLOTS 3000 +#define TRANSLATECOORDS_CACHE_THRESHOLD 3 +#define TRANSLATECOORDS_CACHE_LOWER_THRESHOLD 1 + +// +// The message class. +// + +class TranslateCoordsMessage : public Message +{ + friend class TranslateCoordsStore; + + public: + + TranslateCoordsMessage() + { + } + + ~TranslateCoordsMessage() + { + } + + // + // Put here the fields which constitute + // the 'identity' part of the message. + // + + private: + + unsigned int src_window; + unsigned int dst_window; + unsigned int src_x; + unsigned int src_y; + + unsigned char r_same_screen; + unsigned int r_child_window; + unsigned int r_dst_x; + unsigned int r_dst_y; +}; + +class TranslateCoordsStore : public MessageStore +{ + // + // Constructors and destructors. + // + + public: + + TranslateCoordsStore() : MessageStore() + { + enableCache = TRANSLATECOORDS_ENABLE_CACHE; + enableData = TRANSLATECOORDS_ENABLE_DATA; + enableSplit = TRANSLATECOORDS_ENABLE_SPLIT; + enableCompress = TRANSLATECOORDS_ENABLE_COMPRESS; + + dataLimit = TRANSLATECOORDS_DATA_LIMIT; + dataOffset = TRANSLATECOORDS_DATA_OFFSET; + + cacheSlots = TRANSLATECOORDS_CACHE_SLOTS; + cacheThreshold = TRANSLATECOORDS_CACHE_THRESHOLD; + cacheLowerThreshold = TRANSLATECOORDS_CACHE_LOWER_THRESHOLD; + + messages_ -> resize(cacheSlots); + + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + *i = NULL; + } + + temporary_ = NULL; + } + + virtual ~TranslateCoordsStore() + { + for (T_messages::iterator i = messages_ -> begin(); + i < messages_ -> end(); i++) + { + destroy(*i); + } + + destroy(temporary_); + } + + virtual const char *name() const + { + return "TranslateCoords"; + } + + virtual unsigned char opcode() const + { + return X_TranslateCoords; + } + + virtual unsigned int storage() const + { + return sizeof(TranslateCoordsMessage); + } + + // + // Message handling methods. + // + + public: + + virtual Message *create() const + { + return new TranslateCoordsMessage(); + } + + virtual Message *create(const Message &message) const + { + return new TranslateCoordsMessage((const TranslateCoordsMessage &) message); + } + + virtual void destroy(Message *message) const + { + delete (TranslateCoordsMessage *) message; + } + + virtual int parseIdentity(Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual int unparseIdentity(const Message *message, unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void identityChecksum(const Message *message, const unsigned char *buffer, + unsigned int size, int bigEndian) const; + + virtual void dumpIdentity(const Message *message) const; +}; + +#endif /* TranslateCoords_H */ diff --git a/nxcomp/Transport.cpp b/nxcomp/Transport.cpp new file mode 100644 index 000000000..4b4967826 --- /dev/null +++ b/nxcomp/Transport.cpp @@ -0,0 +1,3056 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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 + + int result = inflate(&r_stream_, Z_SYNC_FLUSH); + + #ifdef INSPECT + *logofs << "ProxyTransport: Called inflate() result is " + << result << ".\n" << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: r_stream_.avail_in = " + << r_stream_.avail_in << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: r_stream_.avail_out = " + << r_stream_.avail_out << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: r_stream_.total_in = " + << r_stream_.total_in << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: r_stream_.total_out = " + << r_stream_.total_out << ".\n" + << logofs_flush; + #endif + + diffTotalIn = r_stream_.total_in - oldTotalIn; + diffTotalOut = r_stream_.total_out - oldTotalOut; + + #ifdef INSPECT + *logofs << "ProxyTransport: diffTotalIn = " + << diffTotalIn << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: diffTotalOut = " + << diffTotalOut << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: r_buffer_.length_ = " + << r_buffer_.length_ << ".\n" + << logofs_flush; + #endif + + r_buffer_.length_ += diffTotalOut; + + #ifdef INSPECT + *logofs << "ProxyTransport: r_buffer_.length_ = " + << r_buffer_.length_ << ".\n" + << logofs_flush; + #endif + + oldTotalIn = r_stream_.total_in; + oldTotalOut = r_stream_.total_out; + + if (result == Z_OK) + { + if (r_stream_.avail_in == 0 && r_stream_.avail_out > 0) + { + break; + } + } + else if (result == Z_BUF_ERROR && r_stream_.avail_out > 0 && + r_stream_.avail_in == 0) + { + #ifdef TEST + *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR decompressing data.\n" + << logofs_flush; + #endif + + break; + } + else + { + #ifdef PANIC + *logofs << "ProxyTransport: PANIC! Decompression of data failed. " + << "Error is '" << zError(result) << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Decompression of data failed. Error is '" + << zError(result) << "'.\n"; + + finish(); + + return -1; + } + + // + // Add more bytes to the buffer. + // + + if (newAvailOut < thresholdSize_) + { + newAvailOut = thresholdSize_; + } + + #ifdef TEST + *logofs << "ProxyTransport: Need to add " << newAvailOut + << " bytes to the decompress buffer in read.\n" + << logofs_flush; + #endif + } + + diffTotalIn = r_stream_.total_in - saveTotalIn; + diffTotalOut = r_stream_.total_out - saveTotalOut; + + #ifdef DEBUG + *logofs << "ProxyTransport: Decompressed data from " + << diffTotalIn << " to " << diffTotalOut + << " bytes.\n" << logofs_flush; + #endif + + statistics -> addDecompressedBytes(diffTotalIn, diffTotalOut); + + // + // Check if the size of the buffer + // matches the produced data. + // + + if ((int) size < r_buffer_.length_) + { + #ifdef TEST + *logofs << "ProxyTransport: WARNING! Forcing a retry with " + << r_buffer_.length_ << " bytes pending and " + << size << " in the buffer.\n" + << logofs_flush; + #endif + + ESET(EAGAIN); + + return -1; + } + + // + // Copy the decompressed data to the + // provided buffer. + // + + int copied = (r_buffer_.length_ > ((int) size) ? + ((int) size) : r_buffer_.length_); + + #ifdef DEBUG + *logofs << "ProxyTransport: Going to return " << copied + << " bytes from proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied); + + // + // Update the buffer status. + // + + r_buffer_.length_ -= copied; + + if (r_buffer_.length_ == 0) + { + r_buffer_.start_ = 0; + } + else + { + r_buffer_.start_ += copied; + + #ifdef TEST + *logofs << "ProxyTransport: There are still " << r_buffer_.length_ + << " bytes in read buffer for proxy FD#" << fd_ + << ".\n" << logofs_flush; + #endif + } + + return copied; +} + +// +// If required compress data, else write it to socket. +// + +int ProxyTransport::write(T_write type, const unsigned char *data, const unsigned int size) +{ + #ifdef TEST + if (size == 0) + { + *logofs << "ProxyTransport: WARNING! Write called for FD#" + << fd_ << " without any data to write.\n" + << logofs_flush; + + return 0; + } + #endif + + // + // If there is no compression revert to + // plain socket management. + // + + if (control -> LocalStreamCompression == 0) + { + int result = Transport::write(type, data, size); + + if (result <= 0) + { + return result; + } + + statistics -> addBytesOut(result); + + statistics -> updateBitrate(result); + + FlushCallback(result); + + return result; + } + + #ifdef DEBUG + *logofs << "ProxyTransport: Going to compress " << size + << " bytes to write buffer for proxy FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + // + // Compress data into the write buffer. + // + + int saveTotalIn = w_stream_.total_in; + int saveTotalOut = w_stream_.total_out; + + int oldTotalIn = saveTotalIn; + int oldTotalOut = saveTotalOut; + + int diffTotalIn; + int diffTotalOut; + + #ifdef INSPECT + *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn + << ".\n" << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut + << ".\n" << logofs_flush; + #endif + + w_stream_.next_in = (Bytef *) data; + w_stream_.avail_in = size; + + // + // Let ZLIB use all the space already + // available in the buffer. + // + + unsigned int newAvailOut = w_buffer_.data_.size() - w_buffer_.start_ - + w_buffer_.length_; + + #ifdef TEST + *logofs << "ProxyTransport: Initial compress buffer is " + << newAvailOut << " bytes.\n" << logofs_flush; + #endif + + for (;;) + { + #ifdef INSPECT + *logofs << "\nProxyTransport: Running the compress loop.\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_buffer_.length_ = " + << w_buffer_.length_ << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_buffer_.data_.size() = " + << w_buffer_.data_.size() << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: newAvailOut = " + << newAvailOut << ".\n" + << logofs_flush; + #endif + + if (resize(w_buffer_, newAvailOut) < 0) + { + return -1; + } + + #ifdef INSPECT + *logofs << "ProxyTransport: w_buffer_.data_.size() = " + << w_buffer_.data_.size() << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.next_in = " + << (void *) w_stream_.next_in << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.avail_in = " + << w_stream_.avail_in << ".\n" + << logofs_flush; + #endif + + w_stream_.next_out = w_buffer_.data_.begin() + w_buffer_.start_ + + w_buffer_.length_; + + w_stream_.avail_out = newAvailOut; + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.next_out = " + << (void *) w_stream_.next_out << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.avail_out = " + << w_stream_.avail_out << ".\n" + << logofs_flush; + #endif + + int result = deflate(&w_stream_, (type == write_delayed ? + Z_NO_FLUSH : Z_SYNC_FLUSH)); + + #ifdef INSPECT + *logofs << "ProxyTransport: Called deflate() result is " + << result << ".\n" << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.avail_in = " + << w_stream_.avail_in << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.avail_out = " + << w_stream_.avail_out << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.total_in = " + << w_stream_.total_in << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.total_out = " + << w_stream_.total_out << ".\n" + << logofs_flush; + #endif + + diffTotalOut = w_stream_.total_out - oldTotalOut; + diffTotalIn = w_stream_.total_in - oldTotalIn; + + #ifdef INSPECT + *logofs << "ProxyTransport: diffTotalIn = " + << diffTotalIn << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: diffTotalOut = " + << diffTotalOut << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_buffer_.length_ = " + << w_buffer_.length_ << ".\n" + << logofs_flush; + #endif + + w_buffer_.length_ += diffTotalOut; + + #ifdef INSPECT + *logofs << "ProxyTransport: w_buffer_.length_ = " + << w_buffer_.length_ << ".\n" + << logofs_flush; + #endif + + oldTotalOut = w_stream_.total_out; + oldTotalIn = w_stream_.total_in; + + if (result == Z_OK) + { + if (w_stream_.avail_in == 0 && w_stream_.avail_out > 0) + { + break; + } + } + else if (result == Z_BUF_ERROR && w_stream_.avail_out > 0 && + w_stream_.avail_in == 0) + { + #ifdef TEST + *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR compressing data.\n" + << logofs_flush; + #endif + + break; + } + else + { + #ifdef PANIC + *logofs << "ProxyTransport: PANIC! Compression of data failed. " + << "Error is '" << zError(result) << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Compression of data failed. Error is '" + << zError(result) << "'.\n"; + + finish(); + + return -1; + } + + // + // Add more bytes to the buffer. + // + + if (newAvailOut < thresholdSize_) + { + newAvailOut = thresholdSize_; + } + + #ifdef TEST + *logofs << "ProxyTransport: Need to add " << newAvailOut + << " bytes to the compress buffer in write.\n" + << logofs_flush; + #endif + } + + diffTotalIn = w_stream_.total_in - saveTotalIn; + diffTotalOut = w_stream_.total_out - saveTotalOut; + + #ifdef TEST + + *logofs << "ProxyTransport: Compressed data from " + << diffTotalIn << " to " << diffTotalOut + << " bytes.\n" << logofs_flush; + + if (diffTotalIn != (int) size) + { + #ifdef PANIC + *logofs << "ProxyTransport: PANIC! Bytes provided to ZLIB stream " + << "should be " << size << " but they look to be " + << diffTotalIn << ".\n" << logofs_flush; + #endif + } + + #endif + + // + // Find out what we have to do with the + // produced data. + // + + if (type == write_immediate) + { + // + // If user requested an immediate write we + // flushed the ZLIB buffer. We can now reset + // the counter and write data to socket. + // + + flush_ = 0; + + #ifdef TEST + *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_ + << " has data for " << w_buffer_.length_ << " bytes.\n" + << logofs_flush; + + *logofs << "ProxyTransport: Start is " << w_buffer_.start_ + << " length is " << w_buffer_.length_ << " flush is " + << flush_ << " size is " << w_buffer_.data_.size() + << " capacity is " << w_buffer_.data_.capacity() + << ".\n" << logofs_flush; + #endif + + // + // Alternatively may try to write only if + // the socket is not blocked. + // + // if (w_buffer_.length_ > 0 && blocked_ == 0) + // { + // ... + // } + // + + if (w_buffer_.length_ > 0) + + { + #ifdef TEST + *logofs << "ProxyTransport: Writing " << w_buffer_.length_ + << " bytes of produced data to FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + int result = Transport::flush(); + + if (result < 0) + { + return -1; + } + } + } + else + { + // + // We haven't flushed the ZLIB compression + // buffer, so user will have to call proxy + // transport's flush explicitly. + // + + flush_ += diffTotalIn; + } + + // + // We either wrote the data or added it to the + // write buffer. It's convenient to update the + // counters at this stage to get the current + // bitrate earlier. + // + + statistics -> addCompressedBytes(diffTotalIn, diffTotalOut); + + statistics -> addBytesOut(diffTotalOut); + + statistics -> updateBitrate(diffTotalOut); + + FlushCallback(diffTotalOut); + + #ifdef TEST + *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_ + << " has data for " << w_buffer_.length_ << " bytes.\n" + << logofs_flush; + + *logofs << "ProxyTransport: Start is " << w_buffer_.start_ + << " length is " << w_buffer_.length_ << " flush is " + << flush_ << " size is " << w_buffer_.data_.size() + << " capacity is " << w_buffer_.data_.capacity() + << ".\n" << logofs_flush; + #endif + + return size; +} + +// +// Write data to its file descriptor. +// + +int ProxyTransport::flush() +{ + // + // If there is no compression or we already compressed + // outgoing data and just need to write it to socket + // because of previous incomplete writes then revert + // to plain socket management. + // + + if (flush_ == 0 || control -> LocalStreamCompression == 0) + { + int result = Transport::flush(); + + if (result < 0) + { + return -1; + } + + return result; + } + + #ifdef DEBUG + *logofs << "ProxyTransport: Going to flush compression on " + << "proxy FD#" << fd_ << ".\n" << logofs_flush; + #endif + + #ifdef TEST + *logofs << "ProxyTransport: Flush counter for proxy FD#" << fd_ + << " is " << flush_ << " bytes.\n" << logofs_flush; + #endif + + // + // Flush ZLIB stream into the write buffer. + // + + int saveTotalIn = w_stream_.total_in; + int saveTotalOut = w_stream_.total_out; + + int oldTotalIn = saveTotalIn; + int oldTotalOut = saveTotalOut; + + int diffTotalOut; + int diffTotalIn; + + #ifdef INSPECT + *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn + << ".\n" << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut + << ".\n" << logofs_flush; + #endif + + w_stream_.next_in = w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_; + w_stream_.avail_in = 0; + + // + // Let ZLIB use all the space already + // available in the buffer. + // + + unsigned int newAvailOut = w_buffer_.data_.size() - w_buffer_.start_ - + w_buffer_.length_; + + #ifdef DEBUG + *logofs << "ProxyTransport: Initial flush buffer is " + << newAvailOut << " bytes.\n" << logofs_flush; + #endif + + for (;;) + { + #ifdef INSPECT + *logofs << "\nProxyTransport: Running the flush loop.\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_buffer_.length_ = " + << w_buffer_.length_ << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_buffer_.data_.size() = " + << w_buffer_.data_.size() << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: newAvailOut = " + << newAvailOut << ".\n" + << logofs_flush; + #endif + + if (resize(w_buffer_, newAvailOut) < 0) + { + return -1; + } + + #ifdef INSPECT + *logofs << "ProxyTransport: w_buffer_.data_.size() = " + << w_buffer_.data_.size() << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.next_in = " + << (void *) w_stream_.next_in << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.avail_in = " + << w_stream_.avail_in << ".\n" + << logofs_flush; + #endif + + w_stream_.next_out = w_buffer_.data_.begin() + w_buffer_.start_ + + w_buffer_.length_; + + w_stream_.avail_out = newAvailOut; + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.next_out = " + << (void *) w_stream_.next_out << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.avail_out = " + << w_stream_.avail_out << ".\n" + << logofs_flush; + #endif + + int result = deflate(&w_stream_, Z_SYNC_FLUSH); + + #ifdef INSPECT + *logofs << "ProxyTransport: Called deflate() result is " + << result << ".\n" << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.avail_in = " + << w_stream_.avail_in << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.avail_out = " + << w_stream_.avail_out << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.total_in = " + << w_stream_.total_in << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_stream_.total_out = " + << w_stream_.total_out << ".\n" + << logofs_flush; + #endif + + diffTotalOut = w_stream_.total_out - oldTotalOut; + diffTotalIn = w_stream_.total_in - oldTotalIn; + + #ifdef INSPECT + *logofs << "ProxyTransport: diffTotalIn = " + << diffTotalIn << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: diffTotalOut = " + << diffTotalOut << ".\n" + << logofs_flush; + #endif + + #ifdef INSPECT + *logofs << "ProxyTransport: w_buffer_.length_ = " + << w_buffer_.length_ << ".\n" + << logofs_flush; + #endif + + w_buffer_.length_ += diffTotalOut; + + #ifdef INSPECT + *logofs << "ProxyTransport: w_buffer_.length_ = " + << w_buffer_.length_ << ".\n" + << logofs_flush; + #endif + + oldTotalOut = w_stream_.total_out; + oldTotalIn = w_stream_.total_in; + + if (result == Z_OK) + { + if (w_stream_.avail_in == 0 && w_stream_.avail_out > 0) + { + break; + } + } + else if (result == Z_BUF_ERROR && w_stream_.avail_out > 0 && + w_stream_.avail_in == 0) + { + #ifdef TEST + *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR flushing data.\n" + << logofs_flush; + #endif + + break; + } + else + { + #ifdef PANIC + *logofs << "ProxyTransport: PANIC! Flush of compressed data failed. " + << "Error is '" << zError(result) << "'.\n" + << logofs_flush; + #endif + + cerr << "Error" << ": Flush of compressed data failed. Error is '" + << zError(result) << "'.\n"; + + finish(); + + return -1; + } + + // + // Add more bytes to the buffer. + // + + if (newAvailOut < thresholdSize_) + { + newAvailOut = thresholdSize_; + } + + #ifdef TEST + *logofs << "ProxyTransport: Need to add " << newAvailOut + << " bytes to the compress buffer in flush.\n" + << logofs_flush; + #endif + } + + diffTotalIn = w_stream_.total_in - saveTotalIn; + diffTotalOut = w_stream_.total_out - saveTotalOut; + + #ifdef TEST + *logofs << "ProxyTransport: Compressed flush data from " + << diffTotalIn << " to " << diffTotalOut + << " bytes.\n" << logofs_flush; + #endif + + // + // Time to move data from the write + // buffer to the real link. + // + + #ifdef DEBUG + *logofs << "ProxyTransport: Reset flush counter for proxy FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + flush_ = 0; + + #ifdef TEST + *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_ + << " has data for " << w_buffer_.length_ << " bytes.\n" + << logofs_flush; + + *logofs << "ProxyTransport: Start is " << w_buffer_.start_ + << " length is " << w_buffer_.length_ << " flush is " + << flush_ << " size is " << w_buffer_.data_.size() + << " capacity is " << w_buffer_.data_.capacity() + << ".\n" << logofs_flush; + #endif + + int result = Transport::flush(); + + if (result < 0) + { + return -1; + } + + // + // Update all the counters. + // + + statistics -> addCompressedBytes(diffTotalIn, diffTotalOut); + + statistics -> addBytesOut(diffTotalOut); + + statistics -> updateBitrate(diffTotalOut); + + FlushCallback(diffTotalOut); + + return result; +} + +unsigned int ProxyTransport::getPending(unsigned char *&data) +{ + // + // Return a pointer to the data in the + // read buffer. It is up to the caller + // to ensure that the data is consumed + // before the read buffer is reused. + // + + if (r_buffer_.length_ > 0) + { + unsigned int size = r_buffer_.length_; + + data = r_buffer_.data_.begin() + r_buffer_.start_; + + #ifdef DEBUG + *logofs << "ProxyTransport: Returning " << size + << " pending bytes from proxy FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + r_buffer_.length_ = 0; + r_buffer_.start_ = 0; + + // + // Prevent the deletion of the buffer. + // + + owner_ = 0; + + return size; + } + + #ifdef TEST + *logofs << "ProxyTransport: WARNING! No pending data " + << "for proxy FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + data = NULL; + + return 0; +} + +void ProxyTransport::fullReset() +{ + blocked_ = 0; + finish_ = 0; + flush_ = 0; + + if (control -> RemoteStreamCompression) + { + inflateReset(&r_stream_); + } + + if (control -> LocalStreamCompression) + { + deflateReset(&w_stream_); + } + + if (owner_ == 1) + { + Transport::fullReset(r_buffer_); + } + + Transport::fullReset(w_buffer_); +} + +AgentTransport::AgentTransport(int fd) : Transport(fd) +{ + #ifdef TEST + *logofs << "AgentTransport: Going to create agent transport " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + type_ = transport_agent; + + // + // Set up the read buffer. + // + + r_buffer_.length_ = 0; + r_buffer_.start_ = 0; + + r_buffer_.data_.resize(initialSize_); + + // + // For now we own the buffer. + // + + owner_ = 1; + + // + // Set up the mutexes. + // + + #ifdef THREADS + + pthread_mutexattr_t m_attributes; + + pthread_mutexattr_init(&m_attributes); + + // + // Interfaces in pthread to handle mutex + // type do not work in current version. + // + + m_attributes.__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP; + + if (pthread_mutex_init(&m_read_, &m_attributes) != 0) + { + #ifdef TEST + *logofs << "AgentTransport: Child: Creation of read mutex failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + } + + if (pthread_mutex_init(&m_write_, &m_attributes) != 0) + { + #ifdef TEST + *logofs << "AgentTransport: Child: Creation of write mutex failed. " + << "Error is " << EGET() << " '" << ESTR() + << "'.\n" << logofs_flush; + #endif + } + + #endif + + #ifdef REFERENCES + *logofs << "AgentTransport: Child: Created new object at " + << this << " out of " << ++references_ + << " allocated references.\n" << logofs_flush; + #endif +} + +AgentTransport::~AgentTransport() +{ + #ifdef TEST + *logofs << "AgentTransport: Going to destroy derived class " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + // + // Unlock and free all mutexes. + // + + #ifdef THREADS + + pthread_mutex_unlock(&m_read_); + pthread_mutex_unlock(&m_write_); + + pthread_mutex_destroy(&m_read_); + pthread_mutex_destroy(&m_write_); + + #endif + + #ifdef REFERENCES + *logofs << "AgentTransport: Child: Deleted object at " + << this << " out of " << --references_ + << " allocated references.\n" << logofs_flush; + #endif +} + +// +// Read data enqueued by the other thread. +// + +int AgentTransport::read(unsigned char *data, unsigned int size) +{ + #ifdef THREADS + + lockRead(); + + #endif + + #ifdef DEBUG + *logofs << "AgentTransport: Child: Going to read " << size + << " bytes from " << "FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + int copied = -1; + + if (r_buffer_.length_ > 0) + { + if ((int) size < r_buffer_.length_) + { + #ifdef TEST + *logofs << "AgentTransport: WARNING! Forcing a retry with " + << r_buffer_.length_ << " bytes pending and " + << size << " in the buffer.\n" + << logofs_flush; + #endif + + ESET(EAGAIN); + } + else + { + copied = (r_buffer_.length_ > ((int) size) ? + ((int) size) : r_buffer_.length_); + + memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied); + + // + // Update the buffer status. + // + + #ifdef TEST + *logofs << "AgentTransport: Child: Going to immediately return " + << copied << " bytes from FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + #ifdef DUMP + + *logofs << "AgentTransport: Child: Dumping content of read data.\n" + << logofs_flush; + + DumpData(data, copied); + + #endif + + r_buffer_.length_ -= copied; + + if (r_buffer_.length_ == 0) + { + r_buffer_.start_ = 0; + } + else + { + r_buffer_.start_ += copied; + + #ifdef TEST + *logofs << "AgentTransport: Child: There are still " + << r_buffer_.length_ << " bytes in read buffer for " + << "FD#" << fd_ << ".\n" << logofs_flush; + #endif + } + } + } + else + { + #ifdef DEBUG + *logofs << "AgentTransport: Child: No data can be got " + << "from read buffer for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + ESET(EAGAIN); + } + + #ifdef THREADS + + unlockRead(); + + #endif + + return copied; +} + +// +// Write data to buffer so that the other +// thread can get it. +// + +int AgentTransport::write(T_write type, const unsigned char *data, const unsigned int size) +{ + #ifdef THREADS + + lockWrite(); + + #endif + + // + // Just append data to socket's write buffer. + // Note that we don't care if buffer exceeds + // the size limits set for this type of + // transport. + // + + #ifdef TEST + *logofs << "AgentTransport: Child: Going to append " << size + << " bytes to write buffer for " << "FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + int copied = -1; + + if (resize(w_buffer_, size) < 0) + { + finish(); + + ESET(EPIPE); + } + else + { + memmove(w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_, data, size); + + w_buffer_.length_ += size; + + #ifdef DUMP + + *logofs << "AgentTransport: Child: Dumping content of written data.\n" + << logofs_flush; + + DumpData(data, size); + + #endif + + #ifdef TEST + *logofs << "AgentTransport: Child: Write buffer for FD#" << fd_ + << " has data for " << w_buffer_.length_ << " bytes.\n" + << logofs_flush; + + *logofs << "AgentTransport: Child: Start is " << w_buffer_.start_ + << " length is " << w_buffer_.length_ << " size is " + << w_buffer_.data_.size() << " capacity is " + << w_buffer_.data_.capacity() << ".\n" + << logofs_flush; + #endif + + copied = size; + } + + // + // Let child access again the read buffer. + // + + #ifdef THREADS + + unlockWrite(); + + #endif + + return copied; +} + +int AgentTransport::flush() +{ + // + // In case of memory-to-memory transport + // this function should never be called. + // + + #ifdef PANIC + *logofs << "AgentTransport: Child: PANIC! Called flush() for " + << "memory to memory transport on " << "FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Called flush() for " + << "memory to memory transport on " << "FD#" + << fd_ << ".\n"; + + HandleAbort(); +} + +int AgentTransport::drain(int limit, int timeout) +{ + // + // We can't drain the channel in the case + // of the memory-to-memory transport. Data + // is enqueued for the agent to read but + // the agent could require multiple loops + // to read it all. + // + + // + // In case of memory-to-memory transport + // this function should never be called. + // + + #ifdef PANIC + *logofs << "AgentTransport: Child: PANIC! Called drain() for " + << "memory to memory transport on " << "FD#" + << fd_ << ".\n" << logofs_flush; + #endif + + cerr << "Error" << ": Called drain() for " + << "memory to memory transport on " << "FD#" + << fd_ << ".\n"; + + HandleAbort(); +} + +unsigned int AgentTransport::getPending(unsigned char *&data) +{ + #ifdef THREADS + + lockRead(); + + #endif + + if (r_buffer_.length_ > 0) + { + unsigned int size = r_buffer_.length_; + + data = r_buffer_.data_.begin() + r_buffer_.start_; + + #ifdef DEBUG + *logofs << "AgentTransport: Child: Returning " << size + << " pending bytes from FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + r_buffer_.length_ = 0; + r_buffer_.start_ = 0; + + #ifdef THREADS + + unlockRead(); + + #endif + + // + // Prevent the deletion of the buffer. + // + + owner_ = 0; + + return size; + } + + #ifdef TEST + *logofs << "AgentTransport: WARNING! No pending data " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + #ifdef THREADS + + unlockRead(); + + #endif + + data = NULL; + + return 0; +} + +void AgentTransport::fullReset() +{ + #ifdef THREADS + + lockRead(); + lockWrite(); + + #endif + + #ifdef TEST + *logofs << "AgentTransport: Child: Resetting transport " + << "for FD#" << fd_ << ".\n" << logofs_flush; + #endif + + blocked_ = 0; + finish_ = 0; + + if (owner_ == 1) + { + Transport::fullReset(r_buffer_); + } + + Transport::fullReset(w_buffer_); +} + +int AgentTransport::enqueue(const char *data, const int size) +{ + #ifdef THREADS + + lockRead(); + + #endif + + if (finish_ == 1) + { + #if defined(PARENT) && defined(TEST) + *logofs << "AgentTransport: Parent: Returning EPIPE in " + << "write for finishing FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + ESET(EPIPE); + + return -1; + } + + // + // Always allow the agent to write + // all its data. + // + + int toPut = size; + + #if defined(PARENT) && defined(TEST) + *logofs << "AgentTransport: Parent: Going to put " << toPut + << " bytes into read buffer for FD#" << fd_ + << ". Buffer length is " << r_buffer_.length_ + << ".\n" << logofs_flush; + #endif + + if (resize(r_buffer_, toPut) < 0) + { + finish(); + + #ifdef THREADS + + unlockRead(); + + #endif + + return -1; + } + + memcpy(r_buffer_.data_.begin() + r_buffer_.start_ + r_buffer_.length_, data, toPut); + + r_buffer_.length_ += toPut; + + #if defined(DUMP) && defined(PARENT) + + *logofs << "AgentTransport: Parent: Dumping content of enqueued data.\n" + << logofs_flush; + + DumpData((const unsigned char *) data, toPut); + + #endif + + #if defined(PARENT) && defined(TEST) + *logofs << "AgentTransport: Parent: Read buffer for FD#" << fd_ + << " has now data for " << r_buffer_.length_ + << " bytes.\n" << logofs_flush; + + *logofs << "AgentTransport: Parent: Start is " << r_buffer_.start_ + << " length is " << r_buffer_.length_ << " size is " + << r_buffer_.data_.size() << " capacity is " + << r_buffer_.data_.capacity() << ".\n" + << logofs_flush; + #endif + + #ifdef THREADS + + unlockRead(); + + #endif + + return toPut; +} + +int AgentTransport::dequeue(char *data, int size) +{ + #ifdef THREADS + + lockWrite(); + + #endif + + if (w_buffer_.length_ == 0) + { + if (finish_ == 1) + { + #if defined(PARENT) && defined(TEST) + *logofs << "AgentTransport: Parent: Returning 0 in read " + << "for finishing FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + return 0; + } + + #if defined(PARENT) && defined(TEST) + *logofs << "AgentTransport: Parent: No data can be read " + << "from write buffer for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + ESET(EAGAIN); + + #ifdef THREADS + + unlockWrite(); + + #endif + + return -1; + } + + // + // Return as many bytes as possible. + // + + int toGet = ((int) size > w_buffer_.length_ ? w_buffer_.length_ : size); + + #if defined(PARENT) && defined(TEST) + *logofs << "AgentTransport: Parent: Going to get " << toGet + << " bytes from write buffer for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + memcpy(data, w_buffer_.data_.begin() + w_buffer_.start_, toGet); + + w_buffer_.start_ += toGet; + w_buffer_.length_ -= toGet; + + #if defined(DUMP) && defined(PARENT) + + *logofs << "AgentTransport: Parent: Dumping content of dequeued data.\n" + << logofs_flush; + + DumpData((const unsigned char *) data, toGet); + + #endif + + #if defined(PARENT) && defined(TEST) + *logofs << "AgentTransport: Parent: Write buffer for FD#" << fd_ + << " has now data for " << length() << " bytes.\n" + << logofs_flush; + + *logofs << "AgentTransport: Parent: Start is " << w_buffer_.start_ + << " length is " << w_buffer_.length_ << " size is " + << w_buffer_.data_.size() << " capacity is " + << w_buffer_.data_.capacity() << ".\n" + << logofs_flush; + #endif + + #ifdef THREADS + + unlockWrite(); + + #endif + + return toGet; +} + +int AgentTransport::dequeuable() +{ + if (finish_ == 1) + { + #if defined(PARENT) && defined(TEST) + *logofs << "AgentTransport: Parent: Returning EPIPE in " + << "readable for finishing FD#" << fd_ + << ".\n" << logofs_flush; + #endif + + ESET(EPIPE); + + return -1; + } + + #if defined(PARENT) && defined(TEST) + *logofs << "AgentTransport: Parent: Returning " + << w_buffer_.length_ << " as data readable " + << "from read buffer for FD#" << fd_ << ".\n" + << logofs_flush; + #endif + + return w_buffer_.length_; +} + +#ifdef THREADS + +int AgentTransport::lockRead() +{ + for (;;) + { + int result = pthread_mutex_lock(&m_read_); + + if (result == 0) + { + #ifdef DEBUG + *logofs << "AgentTransport: Read mutex locked by thread id " + << pthread_self() << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (EGET() == EINTR) + { + continue; + } + else + { + #ifdef WARNING + *logofs << "AgentTransport: WARNING! Locking of read mutex by thread id " + << pthread_self() << " returned " << result << ". Error is '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + return result; + } + } +} + +int AgentTransport::lockWrite() +{ + for (;;) + { + int result = pthread_mutex_lock(&m_write_); + + if (result == 0) + { + #ifdef DEBUG + *logofs << "AgentTransport: Write mutex locked by thread id " + << pthread_self() << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (EGET() == EINTR) + { + continue; + } + else + { + #ifdef WARNING + *logofs << "AgentTransport: WARNING! Locking of write mutex by thread id " + << pthread_self() << " returned " << result << ". Error is '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + return result; + } + } +} + +int AgentTransport::unlockRead() +{ + for (;;) + { + int result = pthread_mutex_unlock(&m_read_); + + if (result == 0) + { + #ifdef DEBUG + *logofs << "AgentTransport: Read mutex unlocked by thread id " + << pthread_self() << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (EGET() == EINTR) + { + continue; + } + else + { + #ifdef WARNING + *logofs << "AgentTransport: WARNING! Unlocking of read mutex by thread id " + << pthread_self() << " returned " << result << ". Error is '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + return result; + } + } +} + +int AgentTransport::unlockWrite() +{ + for (;;) + { + int result = pthread_mutex_unlock(&m_write_); + + if (result == 0) + { + #ifdef DEBUG + *logofs << "AgentTransport: Write mutex unlocked by thread id " + << pthread_self() << ".\n" << logofs_flush; + #endif + + return 0; + } + else if (EGET() == EINTR) + { + continue; + } + else + { + #ifdef WARNING + *logofs << "AgentTransport: WARNING! Unlocking of write mutex by thread id " + << pthread_self() << " returned " << result << ". Error is '" + << ESTR() << "'.\n" << logofs_flush; + #endif + + return result; + } + } +} + +#endif diff --git a/nxcomp/Transport.h b/nxcomp/Transport.h new file mode 100644 index 000000000..2f313b29f --- /dev/null +++ b/nxcomp/Transport.h @@ -0,0 +1,569 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Transport_H +#define Transport_H + +#include <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 accomodate + // at least size bytes. + // + + int resize(T_buffer &buffer, const int &size); + + void partialReset(T_buffer &buffer) + { + if (buffer.length_ == 0 && + (buffer.data_.size() > initialSize_ || + buffer.data_.capacity() > initialSize_)) + { + fullReset(buffer); + } + } + + void fullReset(T_buffer &buffer); + + // + // Data members. + // + + int fd_; + + int blocked_; + int finish_; + + T_buffer w_buffer_; + + unsigned int initialSize_; + unsigned int thresholdSize_; + unsigned int maximumSize_; + + T_transport_type type_; + + private: + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +// +// This class handles buffered I/O and +// compression of the proxy stream. +// + +class ProxyTransport : public Transport +{ + public: + + ProxyTransport(int fd); + + virtual ~ProxyTransport(); + + virtual int read(unsigned char *data, unsigned int size); + + virtual int write(T_write type, const unsigned char *data, const unsigned int size); + + virtual int flush(); + + // + // Same as in the base class. + // + // virtual int drain(int limit, int timeout); + // + // virtual void finish(); + // + + // + // Same as in the base class. + // + // virtual int length() const + // + + virtual int pending() const + { + return r_buffer_.length_; + } + + // + // Same as in the base class. + // + // virtual int readable() const; + // + // virtual int writable() const; + // + // virtual int queued() const; + // + + virtual int flushable() const + { + return flush_; + } + + // + // Same as in the base class, but + // should not be called. + // + // int drained() const; + // + // Same as in the base class. + // + // virtual int wait(int timeout) const; + // + // Same as in the base class. + // + // void setSize(unsigned int initialSize, + // unsigned int thresholdSize, + // unsigned int maximumSize); + // + + virtual unsigned int getPending(unsigned char *&data); + + virtual void pendingReset() + { + owner_ = 1; + } + + virtual void partialReset() + { + if (owner_ == 1) + { + Transport::partialReset(r_buffer_); + } + + Transport::partialReset(w_buffer_); + } + + virtual void fullReset(); + + // + // Same as in the base class. + // + // int blocked() const; + // + + protected: + + int flush_; + int owner_; + + T_buffer r_buffer_; + + z_stream r_stream_; + z_stream w_stream_; + + private: + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +// +// Handle memory-to-memory data transfers between +// an agent and the proxy. +// + +class AgentTransport : public Transport +{ + public: + + AgentTransport(int fd); + + virtual ~AgentTransport(); + + virtual int read(unsigned char *data, unsigned int size); + + virtual int write(T_write type, const unsigned char *data, const unsigned int size); + + // + // These two should never be called. + // + + virtual int flush(); + + virtual int drain(int limit, int timeout); + + // + // Same as in the base class. + // + // virtual void finish(); + // + + // + // Same as in the base class. + // + // virtual int length() const + // + + virtual int pending() const + { + return r_buffer_.length_; + } + + // + // These are intended to operate only + // on the internal buffers. + // + + virtual int readable() const + { + return r_buffer_.length_; + } + + virtual int writable() const + { + return control -> TransportMaximumBufferSize; + } + + virtual int queued() const + { + return 0; + } + + // + // Same as in the base class. + // + // virtual int flushable() const; + // + // Same as in the base class, but + // should not be called. + // + // int drained() const; + // + + // + // Return immediately or will + // block until the timeout. + // + + virtual int wait(int timeout) const + { + return 0; + } + + // + // Same as in the base class. + // + // void setSize(unsigned int initialSize, + // unsigned int thresholdSize, + // unsigned int maximumSize); + // + + virtual unsigned int getPending(unsigned char *&data); + + virtual void pendingReset() + { + owner_ = 1; + } + + virtual void partialReset() + { + if (owner_ == 1) + { + Transport::partialReset(r_buffer_); + } + + Transport::partialReset(w_buffer_); + } + + virtual void fullReset(); + + // + // Same as in the base class. + // + // int blocked() const; + // + + // + // The following are specific of the + // memory-to-memory transport. + // + + int enqueue(const char *data, const int size); + + int dequeue(char *data, int size); + + int queuable() + { + // + // Always allow the agent to enqueue + // more data. + // + + return control -> TransportMaximumBufferSize; + } + + int dequeuable(); + + protected: + + // + // Lock the buffer to handle reads and + // writes safely. + // + + #ifdef THREADS + + int lockRead(); + int lockWrite(); + + int unlockRead(); + int unlockWrite(); + + #endif + + // + // Data members. + // + + int owner_; + + T_buffer r_buffer_; + + // + // Mutexes for safe read and write. + // + + #ifdef THREADS + + pthread_mutex_t m_read_; + pthread_mutex_t m_write_; + + #endif + + private: + + #ifdef REFERENCES + + static int references_; + + #endif +}; + +#endif /* Transport_H */ diff --git a/nxcomp/Types.h b/nxcomp/Types.h new file mode 100644 index 000000000..05f62bd00 --- /dev/null +++ b/nxcomp/Types.h @@ -0,0 +1,255 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Types_H +#define Types_H + +using namespace std; + +#include <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()); + } + + void clear() + { + #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) + + #if defined(__GLIBCPP_INTERNAL_VECTOR_H) + + _Destroy(_M_start, _M_finish); + + #else /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */ + + destroy(_M_start, _M_finish); + + #endif /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */ + + _M_deallocate(_M_start, _M_end_of_storage - _M_start); + + _M_start = _M_finish = _M_end_of_storage = 0; + + #else /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */ + + #if defined(_GLIBCXX_VECTOR) + + _Destroy(this->_M_impl._M_start, this->_M_impl._M_finish); + + _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); + + this->_M_impl._M_start = this->_M_impl._M_finish = this->_M_impl._M_end_of_storage = 0; + + #else /* #if defined(_GLIBCXX_VECTOR) */ + + destroy(start, finish); + + deallocate(); + + start = finish = end_of_storage = 0; + + #endif /* #if defined(_GLIBCXX_VECTOR) */ + + #endif /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */ + } +}; + +class T_messages : public vector < Message * > +{ + public: + + void clear() + { + #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) + + #if defined(__GLIBCPP_INTERNAL_VECTOR_H) + + _Destroy(_M_start, _M_finish); + + #else /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */ + + destroy(_M_start, _M_finish); + + #endif /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */ + + _M_deallocate(_M_start, _M_end_of_storage - _M_start); + + _M_start = _M_finish = _M_end_of_storage = 0; + + #else /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */ + + #if defined(_GLIBCXX_VECTOR) + + _Destroy(this->_M_impl._M_start, this->_M_impl._M_finish); + + _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); + + this->_M_impl._M_start = this->_M_impl._M_finish = this->_M_impl._M_end_of_storage = 0; + + #else /* #if defined(_GLIBCXX_VECTOR) */ + + destroy(start, finish); + + deallocate(); + + start = finish = end_of_storage = 0; + + #endif /* #if defined(_GLIBCXX_VECTOR) */ + + #endif /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */ + } +}; + +typedef md5_byte_t * T_checksum; + +struct T_less +{ + bool operator()(T_checksum a, T_checksum b) const + { + return (memcmp(a, b, MD5_LENGTH) < 0); + } +}; + +typedef map < T_checksum, int, T_less > T_checksums; + +class Split; + +typedef list < Split * > T_splits; + +class File; + +struct T_older +{ + bool operator()(File *a, File *b) const; +}; + +typedef set < File *, T_older > T_files; + +typedef list < int > T_list; + +// +// Used to accomodate data to be read and +// written to a socket. +// + +typedef struct +{ + T_data data_; + int length_; + int start_; +} +T_buffer; + +// +// The message store operation that was +// executed for the message. The channels +// use these values to determine how to +// handle the message after it has been +// received at the decoding side. +// + +enum T_store_action +{ + is_hit, + is_added, + is_discarded, + is_removed, + is_added_compat = 0, + is_hit_compat = 1 +}; + +#define IS_HIT (control -> isProtoStep8() == 1 ? is_hit : is_hit_compat) +#define IS_ADDED (control -> isProtoStep8() == 1 ? is_added : is_added_compat) + +enum T_checksum_action +{ + use_checksum, + discard_checksum +}; + +enum T_data_action +{ + use_data, + discard_data +}; + +// +// Message is going to be weighted for +// deletion at insert or cleanup time? +// + +enum T_rating +{ + rating_for_insert, + rating_for_clean +}; + +// +// How to handle the writes to the X +// and proxy connections. +// + +enum T_write +{ + write_immediate, + write_delayed +}; + +enum T_flush +{ + flush_if_needed, + flush_if_any +}; + +// +// This is the value to indicate an +// invalid position in the message +// store. +// + +static const int nothing = -1; + +#endif /* Types_H */ diff --git a/nxcomp/Unpack.cpp b/nxcomp/Unpack.cpp new file mode 100644 index 000000000..5fc494465 --- /dev/null +++ b/nxcomp/Unpack.cpp @@ -0,0 +1,1502 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Misc.h" +#include "Unpack.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +// +// Used for the ZLIB decompression +// of RGB, alpha and colormap data. +// + +z_stream unpackStream; + +static int unpackInitialized; + +int Unpack8To8(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack8To8(T_colormap *colormap, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack8To16(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack8To16(T_colormap *colormap, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack8To24(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack8To24(T_colormap *colormap, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack8To32(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack8To32(T_colormap *colormap, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack15To16(const unsigned char *data, unsigned char *out, + unsigned char *end); + +int Unpack15To24(const unsigned char *data, unsigned char *out, + unsigned char *end); + +int Unpack15To32(const unsigned char *data, unsigned char *out, + unsigned char *end); + +int Unpack16To16(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack16To16(const unsigned char *data, unsigned char *out, + unsigned char *end, int imageByteOrder); + +int Unpack16To24(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack16To24(const unsigned char *data, unsigned char *out, + unsigned char *end, int imageByteOrder); + +int Unpack16To32(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack16To32(const unsigned char *data, unsigned char *out, + unsigned char *end, int imageByteOrder); + +int Unpack24To24(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack24To24(const unsigned char *data, unsigned char *out, + unsigned char *end); + +int Unpack24To32(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + +int Unpack24To32(const unsigned char *data, unsigned char *out, unsigned char *end); + +int Unpack32To32(const T_colormask *colormask, const unsigned int *data, + unsigned int *out, unsigned int *end); + + +void UnpackInit() +{ + if (unpackInitialized == 0) + { + unpackStream.zalloc = (alloc_func) 0; + unpackStream.zfree = (free_func) 0; + unpackStream.opaque = (voidpf) 0; + + unpackStream.next_in = (Bytef *) 0; + unpackStream.avail_in = 0; + + int result = inflateInit2(&unpackStream, 15); + + if (result != Z_OK) + { + #ifdef PANIC + *logofs << "UnpackInit: PANIC! Cannot initialize the Z stream " + << "for decompression. Error is '" << zError(result) + << "'.\n" << logofs_flush; + #endif + + cerr << "Error" << ": Cannot initialize the Z stream for " + << "decompression. Error is '" << zError(result) + << "'.\n"; + } + else + { + unpackInitialized = 1; + } + } +} + +void UnpackDestroy() +{ + if (unpackInitialized == 1) + { + inflateEnd(&unpackStream); + + unpackInitialized = 0; + } +} + +// +// Get bits per pixel set by client +// according to display geometry. +// + +int UnpackBitsPerPixel(T_geometry *geometry, unsigned int depth) +{ + switch (depth) + { + case 1: + { + return geometry -> depth1_bpp; + } + case 4: + { + return geometry -> depth4_bpp; + } + case 8: + { + return geometry -> depth8_bpp; + } + case 15: + case 16: + { + return geometry -> depth16_bpp; + } + case 24: + { + return geometry -> depth24_bpp; + } + case 32: + { + return geometry -> depth32_bpp; + } + default: + { + return 0; + } + } +} + +int Unpack8To8(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack8To8: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + memcpy(out, data, end - out); + + return 1; +} + +int Unpack8To16(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack8To16: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + unsigned short *out16 = (unsigned short *) out; + unsigned short *end16 = (unsigned short *) end; + + while (out16 < end16) + { + if (*data == 0) + { + *out16 = 0x0; + } + else if (*data == 0xff) + { + *out16 = 0xffff; + } + else + { + // + // Pixel layout: + // + // 8bits 00RRGGBB -> 16bits RR000GG0 000BB000. + // + + *out16 = (((((*data & 0x30) << 2) | colormask -> correction_mask) << 8) & 0xf800) | + (((((*data & 0xc) << 4) | colormask -> correction_mask) << 3) & 0x7e0) | + (((((*data & 0x3) << 6) | colormask -> correction_mask) >> 3) & 0x1f); + } + + out16++; + data++; + } + + return 1; +} + +int Unpack8To24(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack8To24: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + while (out < (end - 2)) + { + if (*data == 0x00) + { + out[0] = out[1] = out[2] = 0x00; + } + else if (*data == 0xff) + { + out[0] = out[1] = out[2] = 0xff; + } + else + { + // + // Pixel layout: + // + // 8bits 00RRGGBB -> 24bits RR000000 GG00000 BB000000. + // + + out[0] = (((*data & 0x30) << 2) | colormask -> correction_mask); + out[1] = (((*data & 0x0c) << 4) | colormask -> correction_mask); + out[2] = (((*data & 0x03) << 6) | colormask -> correction_mask); + } + + out += 3; + data += 1; + } + + return 1; +} + +int Unpack8To32(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack8To32: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + unsigned int *out32 = (unsigned int *) out; + unsigned int *end32 = (unsigned int *) end; + + while (out32 < end32) + { + if (*data == 0) + { + *out32 = 0x0; + } + else if (*data == 0xff) + { + *out32 = 0xffffff; + } + else + { + *out32 = ((((*data & 0x30) << 2) | colormask -> correction_mask) << 16) | + ((((*data & 0xc) << 4) | colormask -> correction_mask) << 8) | + (((*data & 0x3) << 6) | colormask -> correction_mask); + } + + out32++; + data++; + } + + return 1; +} + +int Unpack8(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, + int src_height, unsigned char *src_data, int src_size, int dst_depth, + int dst_width, int dst_height, unsigned char *dst_data, int dst_size) +{ + int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); + + int (*unpack)(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + + switch (dst_bpp) + { + case 8: + { + unpack = Unpack8To8; + + break; + } + case 16: + { + unpack = Unpack8To16; + + break; + } + case 24: + { + unpack = Unpack8To24; + + break; + } + case 32: + { + unpack = Unpack8To32; + + break; + } + default: + { + #ifdef PANIC + *logofs << "Unpack8: PANIC! Bad destination bits per pixel " + << dst_bpp << ". Only 16/24/32 are supported.\n" + << logofs_flush; + #endif + + return -1; + } + } + + if (dst_bpp == 24) + { + unsigned char *dst_end = dst_data; + + #ifdef TEST + *logofs << "Unpack8: Handling 24 bits with dst_size " + << dst_size << ".\n" << logofs_flush; + #endif + + for (int y = 0; y < dst_height; y++) + { + dst_data = dst_end; + + dst_end += RoundUp4(dst_width * 3); + + (*unpack)(colormask, src_data, dst_data, dst_end); + + src_data += src_width; + } + } + else + { + unsigned char *dst_end = dst_data + dst_size; + + (*unpack)(colormask, src_data, dst_data, dst_end); + } + + return 1; +} + +int Unpack16To16(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack16To16: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + if (colormask -> correction_mask) + { + unsigned short *data16 = (unsigned short *) data; + + unsigned short *out16 = (unsigned short *) out; + unsigned short *end16 = (unsigned short *) end; + + while (out16 < end16) + { + if (*data16 == 0x0000) + { + *out16 = 0x0000; + } + else if (*data16 == 0xffff) + { + *out16 = 0xffff; + } + else + { + // + // Pixel layout: + // + // 16bit RRRRRGGG GG0BBBBB -> RRRRRGGG GGGBBBBB. + // + + *out16 = (((((*data16 & 0xf100) >> 8) | colormask -> correction_mask) << 8) & 0xf800) | + (((((*data16 & 0x7c0) >> 3) | colormask -> correction_mask) << 3) & 0x7e0) | + (((((*data16 & 0x1f) << 3) | colormask -> correction_mask) >> 3) & 0x1f); + } + + out16++; + data16++; + } + } + else + { + #ifdef TEST + *logofs << "Unpack16To16: Using bitwise copy due to null correction mask.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) out, (unsigned char *) data, end - out); + } + + return 1; +} + +int Unpack16To24(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack16To24: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + unsigned short *data16 = (unsigned short *) data; + + + while (out < end - 2) + { + if (*data16 == 0x0) + { + out[0] = 0x00; + out[1] = 0x00; + out[2] = 0x00; + } + else if (*data16 == 0xffff) + { + out[0] = 0xff; + out[1] = 0xff; + out[2] = 0xff; + } + else + { + #ifdef TEST + *logofs << "Unpack16To24: Pixel [" << *data16 << "]\n" + << logofs_flush; + #endif + + // + // Pixel layout: + // + // 16bit 0RRRRRGG GGGBBBBB -> 24 bit RRRRR000 GGGGG000 BBBBB000 + // + + out[0] = (((*data16 & 0x7c00) >> 7) | colormask -> correction_mask); + out[1] = (((*data16 & 0x03e0) >> 2) | colormask -> correction_mask); + out[2] = (((*data16 & 0x001f) << 3) | colormask -> correction_mask); + } + + out += 3; + data16 += 1; + } + + return 1; +} + +int Unpack16To32(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack16To32: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + unsigned short *data16 = (unsigned short *) data; + + unsigned int *out32 = (unsigned int *) out; + unsigned int *end32 = (unsigned int *) end; + + while (out32 < end32) + { + if (*data16 == 0x0) + { + *out32 = 0x0; + } + else if (*data16 == 0xffff) + { + *out32 = 0xffffff; + } + else + { + *out32 = ((((*data16 & 0x7c00) >> 7) | colormask -> correction_mask) << 16) | + ((((*data16 & 0x3e0) >> 2) | colormask -> correction_mask) << 8) | + (((*data16 & 0x1f) << 3) | colormask -> correction_mask); + } + + out32++; + data16++; + } + + return 1; +} + +int Unpack16(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, + int src_height, unsigned char *src_data, int src_size, int dst_depth, + int dst_width, int dst_height, unsigned char *dst_data, int dst_size) +{ + int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); + + int (*unpack)(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + + switch (dst_bpp) + { + case 16: + { + unpack = Unpack16To16; + + break; + } + case 24: + { + unpack = Unpack16To24; + + break; + } + case 32: + { + unpack = Unpack16To32; + + break; + } + default: + { + #ifdef PANIC + *logofs << "Unpack16: PANIC! Bad destination bits per pixel " + << dst_bpp << ". Only 24/32 are supported.\n" + << logofs_flush; + #endif + + return -1; + } + } + + if (dst_bpp == 24) + { + unsigned char *dst_end = dst_data; + + for (int y = 0; y < dst_height; y++) + { + dst_data = dst_end; + + dst_end += RoundUp4(dst_width * 3); + + (*unpack)(colormask, src_data, dst_data, dst_end); + + src_data += (src_width * 2); + } + + } + else + { + unsigned char *dst_end = dst_data + dst_size; + + (*unpack)(colormask, src_data, dst_data, dst_end); + } + + return 1; +} + +int Unpack24To24(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack24To24: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + if (colormask -> correction_mask) + { + while (out < end) + { + if (data[0] == 0x00 && + data[1] == 0x00 && + data[2] == 0x00) + { + out[0] = out[1] = out[2] = 0x00; + } + else if (data[0] == 0xff && + data[1] == 0xff && + data[2] == 0xff) + { + out[0] = out[1] = out[2] = 0xff; + } + else + { + out[0] = (data[0] | colormask -> correction_mask); + out[1] = (data[1] | colormask -> correction_mask); + out[2] = (data[2] | colormask -> correction_mask); + } + + out += 3; + data += 3; + } + } + else + { + #ifdef TEST + *logofs << "Unpack24To24: Using bitwise copy due to null correction mask.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) out, (unsigned char *) data, end - out); + } + + return 1; +} + +int Unpack24To32(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack24To32: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + unsigned int *out32 = (unsigned int *) out; + unsigned int *end32 = (unsigned int *) end; + + while (out32 < end32) + { + if (colormask -> color_mask == 0xff) + { + *out32 = (data[0] << 16) | (data[1] << 8) | data[2]; + } + else + { + if (data[0] == 0x0 && data[1] == 0x0 && data[2] == 0x0) + { + *out32 = 0x0; + } + else if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) + { + *out32 = 0xffffff; + } + else + { + *out32 = (((unsigned int) data[0] | colormask -> correction_mask) << 16) | + (((unsigned int) data[1] | colormask -> correction_mask) << 8) | + ((unsigned int) data[2] | colormask -> correction_mask); + } + } + + out32 += 1; + data += 3; + } + + return 1; +} + +int Unpack24(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, + int src_height, unsigned char *src_data, int src_size, int dst_depth, + int dst_width, int dst_height, unsigned char *dst_data, int dst_size) +{ + int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); + + int (*unpack)(const T_colormask *colormask, const unsigned char *data, + unsigned char *out, unsigned char *end); + + switch (dst_bpp) + { + case 24: + { + unpack = Unpack24To24; + + break; + } + case 32: + { + unpack = Unpack24To32; + + break; + } + default: + { + #ifdef PANIC + *logofs << "Unpack24: PANIC! Bad destination bits per pixel " + << dst_bpp << ". Only 32 is supported.\n" + << logofs_flush; + #endif + + return -1; + } + } + + if (dst_bpp == 24) + { + unsigned char *dst_end; + unsigned long scanline_size = RoundUp4(dst_width * dst_bpp / 8); + + dst_end = dst_data; + + #ifdef TEST + *logofs << "Unpack24: Handling 24 bits with dst_height " + << dst_height << " scanline_size " << scanline_size + << " dst_size " << dst_size << ".\n" << logofs_flush; + #endif + + for (int y = 0; y < dst_height; y++) + { + dst_data = dst_end; + + dst_end += scanline_size; + + (*unpack)(colormask, src_data, dst_data, dst_end); + + src_data += scanline_size; + } + } + else + { + unsigned char *dst_end = dst_data + dst_size; + + (*unpack)(colormask, src_data, dst_data, dst_end); + } + + return 1; +} + +int Unpack8To8(T_colormap *colormap, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack8To8: Unpacking " << end - out + << " bytes of colormapped data.\n" + << logofs_flush; + #endif + + while (out < end) + { + *(out++) = (unsigned char) colormap -> data[*(data++)]; + } + + return 1; +} + +int Unpack8To16(T_colormap *colormap, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack8To16: Unpacking " << end - out + << " bytes of colormapped data.\n" + << logofs_flush; + #endif + + unsigned short *out16 = (unsigned short *) out; + unsigned short *end16 = (unsigned short *) end; + + while (out16 < end16) + { + *(out16++) = (unsigned short) colormap -> data[*(data++)]; + } + + return 1; +} + +int Unpack8To24(T_colormap *colormap, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack8To24: Unpacking " << end - out + << " bytes of colormapped data.\n" + << logofs_flush; + #endif + + unsigned int value; + + while (out < end) + { + value = colormap -> data[*(data++)]; + + *(out++) = value; + *(out++) = value >> 8; + *(out++) = value >> 16; + } + + return 1; +} + +int Unpack8To32(T_colormap *colormap, const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack8To32: Unpacking " << end - out + << " bytes of colormapped data.\n" + << logofs_flush; + #endif + + unsigned int *out32 = (unsigned int *) out; + unsigned int *end32 = (unsigned int *) end; + + while (out32 < end32) + { + *(out32++) = colormap -> data[*(data++)]; + } + + return 1; +} + +int Unpack8(T_geometry *geometry, T_colormap *colormap, int src_depth, int src_width, int src_height, + unsigned char *src_data, int src_size, int dst_depth, int dst_width, + int dst_height, unsigned char *dst_data, int dst_size) +{ + if (src_depth != 8) + { + #ifdef PANIC + *logofs << "Unpack8: PANIC! Cannot unpack colormapped image of source depth " + << src_depth << ".\n" << logofs_flush; + #endif + + return -1; + } + + int (*unpack)(T_colormap *colormap, const unsigned char *data, + unsigned char *out, unsigned char *end); + + int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); + + switch (dst_bpp) + { + case 8: + { + unpack = Unpack8To8; + + break; + } + case 16: + { + unpack = Unpack8To16; + + break; + } + case 24: + { + unpack = Unpack8To24; + + break; + } + case 32: + { + unpack = Unpack8To32; + + break; + } + default: + { + #ifdef PANIC + *logofs << "Unpack8: PANIC! Bad destination bits per pixel " + << dst_bpp << ". Only 8/16/24/32 are supported.\n" + << logofs_flush; + #endif + + return -1; + } + } + + if (src_width == dst_width && + src_height == dst_height) + { + unsigned char *dst_end = dst_data + dst_size; + + (*unpack)(colormap, src_data, dst_data, dst_end); + } + else if (src_width >= dst_width && + src_height >= dst_height) + { + unsigned char *dst_end = dst_data; + + for (int y = 0; y < dst_height; y++) + { + dst_data = dst_end; + + dst_end += RoundUp4(dst_width * dst_bpp / 8); + + (*unpack)(colormap, src_data, dst_data, dst_end); + + src_data += src_width; + } + } + else + { + #ifdef PANIC + *logofs << "Unpack8: PANIC! Cannot unpack image. " + << "Destination area " << dst_width << "x" + << dst_height << " is not fully contained in " + << src_width << "x" << src_height << " source.\n" + << logofs_flush; + #endif + + return -1; + } + + return 1; +} + +int Unpack15To16(const unsigned char *data, unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack15To16: Unpacking " << end - out + << " bytes of colormapped data.\n" + << logofs_flush; + #endif + + unsigned short *data16 = (unsigned short *) data; + unsigned short *out16 = (unsigned short *) out; + unsigned short *end16 = (unsigned short *) end; + + while (out16 < end16) + { + if (*data16 == 0x0) + { + *out16 = 0x0; + } + else if (*data16 == 0x7fff) + { + *out16 = 0xffff; + } + else + { + #ifdef TEST + *logofs << "Unpack15To16: Pixel [" << *data16 << "]\n" + << logofs_flush; + #endif + + *out16 = ((*data16 & 0x7ff0) << 1) | + (*data16 & 0x001f); + } + + out16 += 1; + data16 += 1; + } + + return 1; +} + +int Unpack15To24(const unsigned char *data, unsigned char *out, unsigned char *end) + +{ + #ifdef TEST + *logofs << "Unpack15To24: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + unsigned short *data16 = (unsigned short *) data; + + while (out < end - 2) + { + if (*data16 == 0x0) + { + out[0] = 0x00; + out[1] = 0x00; + out[2] = 0x00; + } + else if (*data16 == 0x7fff) + { + out[0] = 0xff; + out[1] = 0xff; + out[2] = 0xff; + } + else + { + #ifdef TEST + *logofs << "Unpack15To24: Pixel [" << *data16 << "]\n" + << logofs_flush; + #endif + + out[0] = ((*data16 >> 7) & 0xf8) | ((*data16 >> 12) & 0x07); + out[1] = ((*data16 >> 2) & 0xf8) | ((*data16 >> 8) & 0x07); + out[2] = ((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07); + } + + out += 3; + data16 += 1; + } + + return 1; +} + +int Unpack15To32(const unsigned char *data, unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack15To32: Unpacking " << end - out + << " bytes of data.\n" + << logofs_flush; + #endif + + unsigned short *data16 = (unsigned short *) data; + unsigned int *out32 = (unsigned int *) out; + unsigned int *end32 = (unsigned int *) end; + + while (out32 < end32) + { + if (*data16 == 0x0) + { + *out32 = 0x0; + } + else if (*data16 == 0xffff) + { + *out32 = 0xffffff; + } + else + { + *out32 = ((((*data16 >> 7) & 0xf8) | ((*data16 >> 12) & 0x07)) << 16) | + ((((*data16 >> 2) & 0xf8) | ((*data16 >> 8) & 0x07)) << 8) | + (((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07)); + } + + out32++; + data16++; + } + + return 1; +} + +int Unpack15(T_geometry *geometry, int src_depth, int src_width, int src_height, + unsigned char *src_data, int src_size, int dst_depth, int dst_width, + int dst_height, unsigned char *dst_data, int dst_size) +{ + if (src_depth != 16) + { + #ifdef PANIC + *logofs << "Unpack15: PANIC! Cannot unpack colormapped image of source depth " + << src_depth << ".\n" << logofs_flush; + #endif + + return -1; + } + + int (*unpack)(const unsigned char *data, unsigned char *out, unsigned char *end); + + int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); + + switch (dst_bpp) + { + case 16: + { + unpack = Unpack15To16; + + break; + } + case 24: + { + unpack = Unpack15To24; + + break; + } + case 32: + { + unpack = Unpack15To32; + + break; + } + default: + { + #ifdef PANIC + *logofs << "Unpack15: PANIC! Bad destination bits per pixel " + << dst_bpp << ". Only 16/24/32 are supported.\n" + << logofs_flush; + #endif + + return -1; + } + } + + if (src_width == dst_width && src_height == dst_height) + { + unsigned char *dst_end = dst_data + dst_size; + + (*unpack)(src_data, dst_data, dst_end); + } + else if (src_width >= dst_width && src_height >= dst_height) + { + unsigned char *dst_end = dst_data; + + for (int y = 0; y < dst_height; y++) + { + dst_data = dst_end; + dst_end += RoundUp4(dst_width * dst_bpp / 8); + + (*unpack)(src_data, dst_data, dst_end); + + src_data += src_width * 2; + } + } + else + { + #ifdef PANIC + *logofs << "Unpack15: PANIC! Cannot unpack image. " + << "Destination area " << dst_width << "x" + << dst_height << " is not fully contained in " + << src_width << "x" << src_height << " source.\n" + << logofs_flush; + #endif + + return -1; + } + + return 1; +} + +int Unpack16To16(const unsigned char *data, unsigned char *out, + unsigned char *end, int imageByteOrder) +{ + #ifdef TEST + *logofs << "Unpack16To16: Unpacking " << end - out + << " bytes of colormapped data.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) out, (unsigned char *) data, end - out); + + return 1; +} + +int Unpack16To24(const unsigned char *data, unsigned char *out, + unsigned char *end, int imageByteOrder) + +{ + #ifdef TEST + *logofs << "Unpack16To24: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + unsigned short *data16 = (unsigned short *) data; + + while (out < end - 2) + { + if (*data16 == 0x0) + { + out[0] = 0x00; + out[1] = 0x00; + out[2] = 0x00; + } + else if (*data16 == 0xffff) + { + out[0] = 0xff; + out[1] = 0xff; + out[2] = 0xff; + } + else + { + #ifdef TEST + *logofs << "Unpack16To24: Pixel [" << *data16 << "]\n" + << logofs_flush; + #endif + + out[0] = ((*data16 >> 8) & 0xf8) | ((*data16 >> 13) & 0x07); + out[1] = ((*data16 >> 3) & 0xfc) | ((*data16 >> 9) & 0x03); + out[2] = ((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07); + } + + out += 3; + data16 += 1; + } + + return 1; +} + + +int Unpack16To32(const unsigned char *data, unsigned char *out, + unsigned char *end, int imageByteOrder) +{ + #ifdef TEST + *logofs << "Unpack16To32: Unpacking " << end - out + << " bytes of data.\n" + << logofs_flush; + #endif + + unsigned short *data16 = (unsigned short *) data; + unsigned int *out32 = (unsigned int *) out; + unsigned int *end32 = (unsigned int *) end; + + unsigned short pixel16; + unsigned int pixel32; + + while (out32 < end32) + { + + pixel16 = GetUINT((unsigned char *)data16, 0); + + if (pixel16 == 0x0) + { + PutULONG(0x0, (unsigned char *) out32, imageByteOrder); + } + else if (pixel16 == 0xffff) + { + PutULONG(0xffffff, (unsigned char *) out32, imageByteOrder); + } + else + { + pixel32 = ((((pixel16 >> 8) & 0xf8) | ((pixel16 >> 13) & 0x07)) << 16) | + ((((pixel16 >> 3) & 0xfc) | ((pixel16 >> 9) & 0x03)) << 8) | + (((pixel16 << 3) & 0xf8) | ((pixel16 >> 2) & 0x07)); + + PutULONG(pixel32, (unsigned char *) out32, imageByteOrder); + } + + out32++; + data16++; + } + return 1; +} + +int Unpack16(T_geometry *geometry, int src_depth, int src_width, int src_height, + unsigned char *src_data, int src_size, int dst_depth, int dst_width, + int dst_height, unsigned char *dst_data, int dst_size) +{ + int imageByteOrder = geometry -> image_byte_order; + + if (src_depth != 16) + { + #ifdef PANIC + *logofs << "Unpack16: PANIC! Cannot unpack colormapped image of source depth " + << src_depth << ".\n" << logofs_flush; + #endif + + return -1; + } + + int (*unpack)(const unsigned char *data, unsigned char *out, + unsigned char *end, int imageByteOrder); + + int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); + + switch (dst_bpp) + { + case 16: + { + unpack = Unpack16To16; + + break; + } + case 24: + { + unpack = Unpack16To24; + + break; + } + case 32: + { + unpack = Unpack16To32; + + break; + } + default: + { + #ifdef PANIC + *logofs << "Unpack16: PANIC! Bad destination bits per pixel " + << dst_bpp << ". Only 16/24/32 are supported.\n" + << logofs_flush; + #endif + + return -1; + } + } + + if (src_width == dst_width && src_height == dst_height) + { + unsigned char *dst_end = dst_data + dst_size; + + (*unpack)(src_data, dst_data, dst_end, imageByteOrder); + } + else if (src_width >= dst_width && src_height >= dst_height) + { + unsigned char *dst_end = dst_data; + + for (int y = 0; y < dst_height; y++) + { + dst_data = dst_end; + dst_end += RoundUp4(dst_width * dst_bpp / 8); + + (*unpack)(src_data, dst_data, dst_end, imageByteOrder); + + src_data += src_width * 2; + } + } + else + { + #ifdef PANIC + *logofs << "Unpack16: PANIC! Cannot unpack image. " + << "Destination area " << dst_width << "x" + << dst_height << " is not fully contained in " + << src_width << "x" << src_height << " source.\n" + << logofs_flush; + #endif + + return -1; + } + + return 1; +} + +int Unpack24To24(const unsigned char *data, + unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack124To24: Unpacking " << end - out + << " bytes of colormapped data.\n" + << logofs_flush; + #endif + + while (out < end) + { + *(out++) = *(data++); + } + + return 1; +} + +int Unpack24To32(const unsigned char *data, unsigned char *out, unsigned char *end) +{ + #ifdef TEST + *logofs << "Unpack24To32: Unpacking " << end - out + << " bytes of colormapped data.\n" + << logofs_flush; + #endif + + unsigned int *out32 = (unsigned int *) out; + unsigned int *end32 = (unsigned int *) end; + + while (out32 < end32) + { + if (data[0] == 0x0 && data[1] == 0x0 && data[2] == 0x0) + { + *out32 = 0x0; + } + else if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) + { + *out32 = 0xffffff; + } + else + { + *out32 = (data[2] << 16) | (data[1] << 8) | data[0]; + } + + out32 += 1; + data += 3; + } + + return 1; +} + +int Unpack24(T_geometry *geometry, int src_depth, int src_width, int src_height, + unsigned char *src_data, int src_size, int dst_depth, int dst_width, + int dst_height, unsigned char *dst_data, int dst_size) + +{ + if (src_depth != 24) + { + #ifdef PANIC + *logofs << "Unpack24: PANIC! Cannot unpack colormapped image of source depth " + << src_depth << ".\n" << logofs_flush; + #endif + + return -1; + } + + int (*unpack)(const unsigned char *data, unsigned char *out, unsigned char *end); + + int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth); + + switch (dst_bpp) + { + case 24: + { + unpack = Unpack24To24; + + break; + } + case 32: + { + unpack = Unpack24To32; + + break; + } + default: + { + #ifdef PANIC + *logofs << "Unpack24: PANIC! Bad destination bits per pixel " + << dst_bpp << ". Only 24/32 are supported.\n" + << logofs_flush; + #endif + + return -1; + } + } + + if (src_width == dst_width && src_height == dst_height) + { + unsigned char *dst_end = dst_data + dst_size; + + (*unpack)(src_data, dst_data, dst_end); + } + else if (src_width >= dst_width && src_height >= dst_height) + { + unsigned char *dst_end = dst_data; + + for (int y = 0; y < dst_height; y++) + { + dst_data = dst_end; + dst_end += RoundUp4(dst_width * dst_bpp / 8); + + (*unpack)(src_data, dst_data, dst_end); + + src_data += src_width * 3; + } + } + else + { + #ifdef PANIC + *logofs << "Unpack24: PANIC! Cannot unpack image. " + << "Destination area " << dst_width << "x" + << dst_height << " is not fully contained in " + << src_width << "x" << src_height << " source.\n" + << logofs_flush; + #endif + + return -1; + } + + return 1; +} + +int Unpack32To32(const T_colormask *colormask, const unsigned int *data, + unsigned int *out, unsigned int *end) +{ + #ifdef TEST + *logofs << "Unpack32To32: Unpacking " << end - out + << " bytes of data.\n" << logofs_flush; + #endif + + if (colormask -> correction_mask) + { + while (out < end) + { + if (*data == 0x00000000) + { + *out = 0x00000000; + } + else if (*data == 0xFFFFFFFF) + { + *out = 0xFFFFFFFF; + } + else + { + *out = *data | ((colormask -> correction_mask << 16) | + (colormask -> correction_mask << 8) | + colormask -> correction_mask); + } + + out += 1; + data += 1; + } + } + else + { + #ifdef TEST + *logofs << "Unpack32To32: Using bitwise copy due to null correction mask.\n" + << logofs_flush; + #endif + + memcpy((unsigned char *) out, (unsigned char *) data, end - out); + } + + return 1; +} diff --git a/nxcomp/Unpack.h b/nxcomp/Unpack.h new file mode 100644 index 000000000..65a410fb6 --- /dev/null +++ b/nxcomp/Unpack.h @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Unpack_H +#define Unpack_H + +#include "NXpack.h" + +#include "Z.h" + +#define LSBFirst 0 +#define MSBFirst 1 + +#define SPLIT_PATTERN 0x88 + +typedef ColorMask T_colormask; + +// +// Pixel geometry of channel's display. +// + +typedef struct +{ + unsigned int depth1_bpp; + unsigned int depth4_bpp; + unsigned int depth8_bpp; + unsigned int depth16_bpp; + unsigned int depth24_bpp; + unsigned int depth32_bpp; + + unsigned int red_mask; + unsigned int green_mask; + unsigned int blue_mask; + + unsigned int image_byte_order; + unsigned int bitmap_bit_order; + unsigned int scanline_unit; + unsigned int scanline_pad; + +} T_geometry; + +// +// Colormap is used to remap colors +// from source to destination depth. +// + +typedef struct +{ + unsigned int entries; + unsigned int *data; + +} T_colormap; + +// +// Alpha channel data is added to 32 +// bits images at the time they are +// unpacked. +// + +typedef struct +{ + unsigned int entries; + unsigned char *data; + +} T_alpha; + +// +// The ZLIB stream structure used for +// the decompression. +// + +extern z_stream unpackStream; + +// +// Initialize the ZLIB stream used for +// decompression. +// + +void UnpackInit(); + +// +// Free the ZLIB stream. +// + +void UnpackDestroy(); + +// +// Get the destination bits per pixel +// based on the drawable depth. +// + +int UnpackBitsPerPixel(T_geometry *geometry, unsigned int depth); + +// +// Unpack the source data into the X +// bitmap. +// + +int Unpack8(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, + int src_height, unsigned char *src_data, int src_size, int dst_depth, + int dst_width, int dst_height, unsigned char *dst_data, int dst_size); + +int Unpack16(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, + int src_height, unsigned char *src_data, int src_size, int dst_depth, + int dst_width, int dst_height, unsigned char *dst_data, int dst_size); + +int Unpack24(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width, + int src_height, unsigned char *src_data, int src_size, int dst_depth, + int dst_width, int dst_height, unsigned char *dst_data, int dst_size); + +int Unpack8(T_geometry *geometry, T_colormap *colormap, int src_depth, int src_width, + int src_height, unsigned char *src_data, int src_size, int dst_depth, + int dst_width, int dst_height, unsigned char *dst_data, int dst_size); + +int Unpack15(T_geometry *geometry, int src_depth, int src_width, + int src_height, unsigned char *src_data, int src_size, int dst_depth, + int dst_width, int dst_height, unsigned char *dst_data, int dst_size); + +int Unpack16(T_geometry *geometry, int src_depth, int src_width, + int src_height, unsigned char *src_data, int src_size, int dst_depth, + int dst_width, int dst_height, unsigned char *dst_data, int dst_size); + +int Unpack24(T_geometry *geometry, int src_depth, int src_width, + int src_height, unsigned char *src_data, int src_size, int dst_depth, + int dst_width, int dst_height, unsigned char *dst_data, int dst_size); + +#endif /* Unpack_H */ diff --git a/nxcomp/Utils.cpp b/nxcomp/Utils.cpp new file mode 100644 index 000000000..a2820782a --- /dev/null +++ b/nxcomp/Utils.cpp @@ -0,0 +1,35 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "NX.h" + +#include "Timestamp.h" + +// +// Log level. +// + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +int NXTransSearch(char *data, int size, int step, int threshold) +{ + return -1; +} + diff --git a/nxcomp/VERSION b/nxcomp/VERSION new file mode 100644 index 000000000..1545d9665 --- /dev/null +++ b/nxcomp/VERSION @@ -0,0 +1 @@ +3.5.0 diff --git a/nxcomp/Vars.c b/nxcomp/Vars.c new file mode 100644 index 000000000..0d93a6dc6 --- /dev/null +++ b/nxcomp/Vars.c @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <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/WriteBuffer.cpp b/nxcomp/WriteBuffer.cpp new file mode 100644 index 000000000..ac38fe688 --- /dev/null +++ b/nxcomp/WriteBuffer.cpp @@ -0,0 +1,488 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include <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/WriteBuffer.h b/nxcomp/WriteBuffer.h new file mode 100644 index 000000000..4673cecee --- /dev/null +++ b/nxcomp/WriteBuffer.h @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef WriteBuffer_H +#define WriteBuffer_H + +#include "Misc.h" + +#define PANIC +#define WARNING +#undef TEST +#undef DEBUG + +#define WRITE_BUFFER_DEFAULT_SIZE 16384 + +// +// Adjust for the biggest reply that we could receive. +// This is likely to be a reply to a X_ListFonts where +// user has a large amount of installed fonts. +// + +#define WRITE_BUFFER_OVERFLOW_SIZE 4194304 + +class WriteBuffer +{ + public: + + WriteBuffer(); + + ~WriteBuffer(); + + void setSize(unsigned int initialSize, unsigned int thresholdSize, + unsigned int maximumSize); + + unsigned char *addMessage(unsigned int numBytes); + + unsigned char *removeMessage(unsigned int numBytes); + + unsigned char *addScratchMessage(unsigned int numBytes); + + // + // This form allows user to provide its own + // buffer as write buffer's scratch area. + // + + unsigned char *addScratchMessage(unsigned char *newBuffer, unsigned int numBytes); + + void removeScratchMessage(); + + void partialReset(); + + void fullReset(); + + unsigned char *getData() const + { + return buffer_; + } + + unsigned int getLength() const + { + return length_; + } + + unsigned int getAvailable() const + { + return (size_ - length_); + } + + unsigned char *getScratchData() const + { + return scratchBuffer_; + } + + unsigned int getScratchLength() const + { + return scratchLength_; + } + + unsigned int getTotalLength() const + { + return (length_ + scratchLength_); + } + + void registerPointer(unsigned char **pointer) + { + index_ = pointer; + } + + void unregisterPointer() + { + index_ = 0; + } + + private: + + unsigned int size_; + unsigned int length_; + + unsigned char *buffer_; + unsigned char **index_; + + unsigned int scratchLength_; + unsigned char *scratchBuffer_; + + int scratchOwner_; + + unsigned int initialSize_; + unsigned int thresholdSize_; + unsigned int maximumSize_; +}; + +#endif /* WriteBuffer_H */ diff --git a/nxcomp/XidCache.cpp b/nxcomp/XidCache.cpp new file mode 100644 index 000000000..a9a723c76 --- /dev/null +++ b/nxcomp/XidCache.cpp @@ -0,0 +1,39 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Control.h" + +#include "XidCache.h" + +XidCache::XidCache() +{ + for (int i = 0; i < 256; i++) + { + base_[i] = new IntCache(8); + } + + slot_ = 0; + last_ = 0; +} + +XidCache::~XidCache() +{ + for (int i = 0; i < 256; i++) + { + delete base_[i]; + } +} diff --git a/nxcomp/XidCache.h b/nxcomp/XidCache.h new file mode 100644 index 000000000..78a94d8d0 --- /dev/null +++ b/nxcomp/XidCache.h @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef XidCache_H +#define XidCache_H + +#include "IntCache.h" + +class XidCache +{ + friend class EncodeBuffer; + friend class DecodeBuffer; + + public: + + XidCache(); + ~XidCache(); + + private: + + IntCache *base_[256]; + + unsigned int slot_; + unsigned int last_; +}; + +#endif /* XidCache_H */ diff --git a/nxcomp/Z.cpp b/nxcomp/Z.cpp new file mode 100644 index 000000000..e6c93cd10 --- /dev/null +++ b/nxcomp/Z.cpp @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#include "Z.h" +#include "Misc.h" + +int ZCompress(z_stream *stream, unsigned char *dest, unsigned int *destLen, + const unsigned char *source, unsigned int sourceLen) +{ + // + // Deal with the possible overflow. + // + + if (stream -> total_out & 0x80000000) + { + #ifdef TEST + *logofs << "ZCompress: Reset stream counters with " + << "total in " << stream -> total_in + << " and total out " << stream -> total_out + << ".\n" << logofs_flush; + #endif + + stream -> total_in = 0; + stream -> total_out = 0; + } + + unsigned int saveOut = stream -> total_out; + + stream -> next_in = (Bytef *) source; + stream -> avail_in = sourceLen; + + // + // Check if the source is bigger than + // 64K on 16-bit machine. + // + + #ifdef MAXSEG_64K + + if ((uLong) stream -> avail_in != sourceLen) return Z_BUF_ERROR; + + #endif + + stream -> next_out = dest; + stream -> avail_out = *destLen; + + #ifdef MAXSEG_64K + + if ((uLong) stream -> avail_out != *destLen) return Z_BUF_ERROR; + + #endif + + int result = deflate(stream, Z_FINISH); + + if (result != Z_STREAM_END) + { + deflateReset(stream); + + return (result == Z_OK ? Z_BUF_ERROR : result); + } + + *destLen = stream -> total_out - saveOut; + + result = deflateReset(stream); + + return result; +} + +int ZDecompress(z_stream *stream, unsigned char *dest, unsigned int *destLen, + const unsigned char *source, unsigned int sourceLen) +{ + stream -> next_in = (Bytef *) source; + stream -> avail_in = sourceLen; + + // + // Deal with the possible overflow. + // + + if (stream -> total_out & 0x80000000) + { + #ifdef TEST + *logofs << "ZDecompress: Reset stream counters with " + << "total in " << stream -> total_in + << " and total out " << stream -> total_out + << ".\n" << logofs_flush; + #endif + + stream -> total_in = 0; + stream -> total_out = 0; + } + + unsigned int saveOut = stream -> total_out; + + if (stream -> avail_in != sourceLen) + { + return Z_BUF_ERROR; + } + + stream -> next_out = dest; + stream -> avail_out = *destLen; + + if (stream -> avail_out != *destLen) + { + return Z_BUF_ERROR; + } + + int result = inflate(stream, Z_FINISH); + + if (result != Z_STREAM_END) + { + inflateReset(stream); + + return (result == Z_OK ? Z_BUF_ERROR : result); + } + + *destLen = stream -> total_out - saveOut; + + result = inflateReset(stream); + + return result; +} diff --git a/nxcomp/Z.h b/nxcomp/Z.h new file mode 100644 index 000000000..d7f7fa185 --- /dev/null +++ b/nxcomp/Z.h @@ -0,0 +1,29 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) 2001, 2010 NoMachine, http://www.nomachine.com/. */ +/* */ +/* NXCOMP, NX protocol compression and NX extensions to this software */ +/* are copyright of NoMachine. Redistribution and use of the present */ +/* software is allowed according to terms specified in the file LICENSE */ +/* which comes in the source distribution. */ +/* */ +/* Check http://www.nomachine.com/licensing.html for applicability. */ +/* */ +/* NX and NoMachine are trademarks of Medialogic S.p.A. */ +/* */ +/* All rights reserved. */ +/* */ +/**************************************************************************/ + +#ifndef Z_H +#define Z_H + +#include <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 */ diff --git a/nxcomp/configure b/nxcomp/configure new file mode 100755 index 000000000..633baf840 --- /dev/null +++ b/nxcomp/configure @@ -0,0 +1,5923 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59. +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="NX.h" +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS armcxx armcc CXX CXXFLAGS LDFLAGS CPPFLAGS ac_ct_CXX EXEEXT OBJEXT CC CFLAGS ac_ct_CC INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CXXCPP X_CFLAGS X_PRE_LIBS X_LIBS X_EXTRA_LIBS LIBVERSION VERSION MAKEDEPEND ALL LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CXX_set=${CXX+set} +ac_env_CXX_value=$CXX +ac_cv_env_CXX_set=${CXX+set} +ac_cv_env_CXX_value=$CXX +ac_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_env_CXXFLAGS_value=$CXXFLAGS +ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_cv_env_CXXFLAGS_value=$CXXFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_CXXCPP_set=${CXXCPP+set} +ac_env_CXXCPP_value=$CXXCPP +ac_cv_env_CXXCPP_set=${CXXCPP+set} +ac_cv_env_CXXCPP_value=$CXXCPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF + +X features: + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-x use the X Window System + +Some influential environment variables: + CXX C++ compiler command + CXXFLAGS C++ compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have + headers in a nonstandard directory <include dir> + CC C compiler command + CFLAGS C compiler flags + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + + +CXXFLAGS="-O3 -fno-rtti -fno-exceptions" +CFLAGS="-O3" + + +LIBSTATIC="" +LIBSHARED="" + + +if test -d "../nx-X11/include" ; then + CXXFLAGS="$CXXFLAGS -I../nx-X11/exports/include" + CFLAGS="$CFLAGS -I../nx-X11/exports/include" + LIBS="$LIBS -L../nx-X11/exports/lib" +fi + + +if test "${with_ipaq}" = yes; then + echo -e "enabling IPAQ configuration" + CXX="arm-linux-c++" + CC="arm-linux-gcc" + unset ac_cv_prog_armcxx + unset ac_cv_prog_armcc + unset ac_cv_prog_CXXCPP + # Extract the first word of ""$CXX"", so it can be a program name with args. +set dummy "$CXX"; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_armcxx+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$armcxx"; then + ac_cv_prog_armcxx="$armcxx" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_armcxx="yes" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_armcxx" && ac_cv_prog_armcxx="no" +fi +fi +armcxx=$ac_cv_prog_armcxx +if test -n "$armcxx"; then + echo "$as_me:$LINENO: result: $armcxx" >&5 +echo "${ECHO_T}$armcxx" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + # Extract the first word of ""$CC"", so it can be a program name with args. +set dummy "$CC"; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_armcc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$armcc"; then + ac_cv_prog_armcc="$armcc" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_armcc="yes" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_armcc" && ac_cv_prog_armcc="no" +fi +fi +armcc=$ac_cv_prog_armcc +if test -n "$armcc"; then + echo "$as_me:$LINENO: result: $armcc" >&5 +echo "${ECHO_T}$armcc" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + if test $armcxx = "yes" && test $armcc = "yes" ; then + ac_cv_prog_CXX="$CXX" + ac_cv_prog_CC="$CC" + else + { { echo "$as_me:$LINENO: error: Installation or configuration problem. Cannot find compiler for arm-linux." >&5 +echo "$as_me: error: Installation or configuration problem. Cannot find compiler for arm-linux." >&2;} + { (exit 1); exit 1; }; } + fi +else + unset ac_cv_prog_CXX + unset ac_cv_prog_CC + unset ac_cv_prog_CXXCPP +fi + + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -n "$ac_tool_prefix"; then + for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + echo "$as_me:$LINENO: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 +echo "${ECHO_T}$ac_ct_CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CXX" && break +done +test -n "$ac_ct_CXX" || ac_ct_CXX="g++" + + CXX=$ac_ct_CXX +fi + + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C++ compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5 + (eval $ac_compiler --version </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5 + (eval $ac_compiler -v </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5 + (eval $ac_compiler -V </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5 +echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C++ compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5 +echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 +GXX=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +CXXFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 +echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cxx_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cxx_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include <stdlib.h> +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5 + (eval $ac_compiler --version </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5 + (eval $ac_compiler -v </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5 + (eval $ac_compiler -V </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include <stdlib.h> +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + +echo "$as_me:$LINENO: checking whether compiler needs -Wno-deprecated" >&5 +echo $ECHO_N "checking whether compiler needs -Wno-deprecated... $ECHO_C" >&6 +gcc_version=`${CC} --version | grep 'gcc (GCC) [3-4].' | head -n 1` +case "${gcc_version}" in + gcc*) + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + CXXFLAGS="$CXXFLAGS -Wno-deprecated" + ;; + + *) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; +esac + +echo "$as_me:$LINENO: checking whether compiler accepts -Wmissing-declarations" >&5 +echo $ECHO_N "checking whether compiler accepts -Wmissing-declarations... $ECHO_C" >&6 +gcc_version=`${CC} --version | grep 'gcc (GCC) [3-4].' | head -n 1` +case "${gcc_version}" in + gcc*) + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + ;; + + *) + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + CXXFLAGS="$CXXFLAGS -Wmissing-declarations" + ;; +esac + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + + + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 +echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 +if test -z "$CXXCPP"; then + if test "${ac_cv_prog_CXXCPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +echo "$as_me:$LINENO: result: $CXXCPP" >&5 +echo "${ECHO_T}$CXXCPP" >&6 +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +echo "$as_me:$LINENO: checking for X" >&5 +echo $ECHO_N "checking for X... $ECHO_C" >&6 + + +# Check whether --with-x or --without-x was given. +if test "${with_x+set}" = set; then + withval="$with_x" + +fi; +# $have_x is `yes', `no', `disabled', or empty when we do not yet know. +if test "x$with_x" = xno; then + # The user explicitly disabled X. + have_x=disabled +else + if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then + # Both variables are already set. + have_x=yes + else + if test "${ac_cv_have_x+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # One or both of the vars are not set, and there is no cached value. +ac_x_includes=no ac_x_libraries=no +rm -fr conftest.dir +if mkdir conftest.dir; then + cd conftest.dir + # Make sure to not put "make" in the Imakefile rules, since we grep it out. + cat >Imakefile <<'_ACEOF' +acfindx: + @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' +_ACEOF + if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then + # GNU make sometimes prints "make[1]: Entering...", which would confuse us. + eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` + # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. + for ac_extension in a so sl; do + if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && + test -f $ac_im_libdir/libX11.$ac_extension; then + ac_im_usrlibdir=$ac_im_libdir; break + fi + done + # Screen out bogus values from the imake configuration. They are + # bogus both because they are the default anyway, and because + # using them would break gcc on systems where it needs fixed includes. + case $ac_im_incroot in + /usr/include) ;; + *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes=$ac_im_incroot;; + esac + case $ac_im_usrlibdir in + /usr/lib | /lib) ;; + *) test -d "$ac_im_usrlibdir" && ac_x_libraries=$ac_im_usrlibdir ;; + esac + fi + cd .. + rm -fr conftest.dir +fi + +# Standard set of common directories for X headers. +# Check X11 before X11Rn because it is often a symlink to the current release. +ac_x_header_dirs=' +/usr/X11/include +/usr/X11R6/include +/usr/X11R5/include +/usr/X11R4/include + +/usr/include/X11 +/usr/include/X11R6 +/usr/include/X11R5 +/usr/include/X11R4 + +/usr/local/X11/include +/usr/local/X11R6/include +/usr/local/X11R5/include +/usr/local/X11R4/include + +/usr/local/include/X11 +/usr/local/include/X11R6 +/usr/local/include/X11R5 +/usr/local/include/X11R4 + +/usr/X386/include +/usr/x386/include +/usr/XFree86/include/X11 + +/usr/include +/usr/local/include +/usr/unsupported/include +/usr/athena/include +/usr/local/x11r5/include +/usr/lpp/Xamples/include + +/usr/openwin/include +/usr/openwin/share/include' + +if test "$ac_x_includes" = no; then + # Guess where to find include files, by looking for Intrinsic.h. + # First, try using that file with no special directory specified. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <X11/Intrinsic.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # We can compile using X headers with no special include directory. +ac_x_includes= +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + for ac_dir in $ac_x_header_dirs; do + if test -r "$ac_dir/X11/Intrinsic.h"; then + ac_x_includes=$ac_dir + break + fi +done +fi +rm -f conftest.err conftest.$ac_ext +fi # $ac_x_includes = no + +if test "$ac_x_libraries" = no; then + # Check for the libraries. + # See if we find them without any special options. + # Don't add to $LIBS permanently. + ac_save_LIBS=$LIBS + LIBS="-lXt $LIBS" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <X11/Intrinsic.h> +int +main () +{ +XtMalloc (0) + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + LIBS=$ac_save_LIBS +# We can link X programs with no special library path. +ac_x_libraries= +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +LIBS=$ac_save_LIBS +for ac_dir in `echo "$ac_x_includes $ac_x_header_dirs" | sed s/include/lib/g` +do + # Don't even attempt the hair of trying to link an X program! + for ac_extension in a so sl; do + if test -r $ac_dir/libXt.$ac_extension; then + ac_x_libraries=$ac_dir + break 2 + fi + done +done +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi # $ac_x_libraries = no + +if test "$ac_x_includes" = no || test "$ac_x_libraries" = no; then + # Didn't find X anywhere. Cache the known absence of X. + ac_cv_have_x="have_x=no" +else + # Record where we found X for the cache. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" +fi +fi + + fi + eval "$ac_cv_have_x" +fi # $with_x != no + +if test "$have_x" != yes; then + echo "$as_me:$LINENO: result: $have_x" >&5 +echo "${ECHO_T}$have_x" >&6 + no_x=yes +else + # If each of the values was on the command line, it overrides each guess. + test "x$x_includes" = xNONE && x_includes=$ac_x_includes + test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries + # Update the cache value to reflect the command line values. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$x_includes ac_x_libraries=$x_libraries" + echo "$as_me:$LINENO: result: libraries $x_libraries, headers $x_includes" >&5 +echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6 +fi + +if test "$no_x" = yes; then + # Not all programs may use this symbol, but it does not hurt to define it. + +cat >>confdefs.h <<\_ACEOF +#define X_DISPLAY_MISSING 1 +_ACEOF + + X_CFLAGS= X_PRE_LIBS= X_LIBS= X_EXTRA_LIBS= +else + if test -n "$x_includes"; then + X_CFLAGS="$X_CFLAGS -I$x_includes" + fi + + # It would also be nice to do this for all -L options, not just this one. + if test -n "$x_libraries"; then + X_LIBS="$X_LIBS -L$x_libraries" + # For Solaris; some versions of Sun CC require a space after -R and + # others require no space. Words are not sufficient . . . . + case `(uname -sr) 2>/dev/null` in + "SunOS 5"*) + echo "$as_me:$LINENO: checking whether -R must be followed by a space" >&5 +echo $ECHO_N "checking whether -R must be followed by a space... $ECHO_C" >&6 + ac_xsave_LIBS=$LIBS; LIBS="$LIBS -R$x_libraries" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_R_nospace=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_R_nospace=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test $ac_R_nospace = yes; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + X_LIBS="$X_LIBS -R$x_libraries" + else + LIBS="$ac_xsave_LIBS -R $x_libraries" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_R_space=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_R_space=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + if test $ac_R_space = yes; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + X_LIBS="$X_LIBS -R $x_libraries" + else + echo "$as_me:$LINENO: result: neither works" >&5 +echo "${ECHO_T}neither works" >&6 + fi + fi + LIBS=$ac_xsave_LIBS + esac + fi + + # Check for system-dependent libraries X programs must link with. + # Do this before checking for the system-independent R6 libraries + # (-lICE), since we may need -lsocket or whatever for X linking. + + if test "$ISC" = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl_s -linet" + else + # Martyn Johnson says this is needed for Ultrix, if the X + # libraries were built with DECnet support. And Karl Berry says + # the Alpha needs dnet_stub (dnet does not exist). + ac_xsave_LIBS="$LIBS"; LIBS="$LIBS $X_LIBS -lX11" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char XOpenDisplay (); +int +main () +{ +XOpenDisplay (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +echo "$as_me:$LINENO: checking for dnet_ntoa in -ldnet" >&5 +echo $ECHO_N "checking for dnet_ntoa in -ldnet... $ECHO_C" >&6 +if test "${ac_cv_lib_dnet_dnet_ntoa+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldnet $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dnet_ntoa (); +int +main () +{ +dnet_ntoa (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dnet_dnet_ntoa=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dnet_dnet_ntoa=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dnet_dnet_ntoa" >&5 +echo "${ECHO_T}$ac_cv_lib_dnet_dnet_ntoa" >&6 +if test $ac_cv_lib_dnet_dnet_ntoa = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet" +fi + + if test $ac_cv_lib_dnet_dnet_ntoa = no; then + echo "$as_me:$LINENO: checking for dnet_ntoa in -ldnet_stub" >&5 +echo $ECHO_N "checking for dnet_ntoa in -ldnet_stub... $ECHO_C" >&6 +if test "${ac_cv_lib_dnet_stub_dnet_ntoa+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldnet_stub $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dnet_ntoa (); +int +main () +{ +dnet_ntoa (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dnet_stub_dnet_ntoa=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dnet_stub_dnet_ntoa=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dnet_stub_dnet_ntoa" >&5 +echo "${ECHO_T}$ac_cv_lib_dnet_stub_dnet_ntoa" >&6 +if test $ac_cv_lib_dnet_stub_dnet_ntoa = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -ldnet_stub" +fi + + fi +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$ac_xsave_LIBS" + + # msh@cis.ufl.edu says -lnsl (and -lsocket) are needed for his 386/AT, + # to get the SysV transport functions. + # Chad R. Larson says the Pyramis MIS-ES running DC/OSx (SVR4) + # needs -lnsl. + # The nsl library prevents programs from opening the X display + # on Irix 5.2, according to T.E. Dickey. + # The functions gethostbyname, getservbyname, and inet_addr are + # in -lbsd on LynxOS 3.0.1/i386, according to Lars Hecking. + echo "$as_me:$LINENO: checking for gethostbyname" >&5 +echo $ECHO_N "checking for gethostbyname... $ECHO_C" >&6 +if test "${ac_cv_func_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define gethostbyname to an innocuous variant, in case <limits.h> declares gethostbyname. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define gethostbyname innocuous_gethostbyname + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char gethostbyname (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef gethostbyname + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_gethostbyname) || defined (__stub___gethostbyname) +choke me +#else +char (*f) () = gethostbyname; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != gethostbyname; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_gethostbyname=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_func_gethostbyname" >&6 + + if test $ac_cv_func_gethostbyname = no; then + echo "$as_me:$LINENO: checking for gethostbyname in -lnsl" >&5 +echo $ECHO_N "checking for gethostbyname in -lnsl... $ECHO_C" >&6 +if test "${ac_cv_lib_nsl_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lnsl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_nsl_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_nsl_gethostbyname=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_nsl_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_nsl_gethostbyname" >&6 +if test $ac_cv_lib_nsl_gethostbyname = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl" +fi + + if test $ac_cv_lib_nsl_gethostbyname = no; then + echo "$as_me:$LINENO: checking for gethostbyname in -lbsd" >&5 +echo $ECHO_N "checking for gethostbyname in -lbsd... $ECHO_C" >&6 +if test "${ac_cv_lib_bsd_gethostbyname+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbsd $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char gethostbyname (); +int +main () +{ +gethostbyname (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_bsd_gethostbyname=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_bsd_gethostbyname=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_bsd_gethostbyname" >&5 +echo "${ECHO_T}$ac_cv_lib_bsd_gethostbyname" >&6 +if test $ac_cv_lib_bsd_gethostbyname = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lbsd" +fi + + fi + fi + + # lieder@skyler.mavd.honeywell.com says without -lsocket, + # socket/setsockopt and other routines are undefined under SCO ODT + # 2.0. But -lsocket is broken on IRIX 5.2 (and is not necessary + # on later versions), says Simon Leinen: it contains gethostby* + # variants that don't use the name server (or something). -lsocket + # must be given before -lnsl if both are needed. We assume that + # if connect needs -lnsl, so does gethostbyname. + echo "$as_me:$LINENO: checking for connect" >&5 +echo $ECHO_N "checking for connect... $ECHO_C" >&6 +if test "${ac_cv_func_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define connect to an innocuous variant, in case <limits.h> declares connect. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define connect innocuous_connect + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char connect (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef connect + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_connect) || defined (__stub___connect) +choke me +#else +char (*f) () = connect; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != connect; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_connect=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_connect=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_connect" >&5 +echo "${ECHO_T}$ac_cv_func_connect" >&6 + + if test $ac_cv_func_connect = no; then + echo "$as_me:$LINENO: checking for connect in -lsocket" >&5 +echo $ECHO_N "checking for connect in -lsocket... $ECHO_C" >&6 +if test "${ac_cv_lib_socket_connect+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsocket $X_EXTRA_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char connect (); +int +main () +{ +connect (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_socket_connect=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_socket_connect=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_socket_connect" >&5 +echo "${ECHO_T}$ac_cv_lib_socket_connect" >&6 +if test $ac_cv_lib_socket_connect = yes; then + X_EXTRA_LIBS="-lsocket $X_EXTRA_LIBS" +fi + + fi + + # Guillermo Gomez says -lposix is necessary on A/UX. + echo "$as_me:$LINENO: checking for remove" >&5 +echo $ECHO_N "checking for remove... $ECHO_C" >&6 +if test "${ac_cv_func_remove+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define remove to an innocuous variant, in case <limits.h> declares remove. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define remove innocuous_remove + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char remove (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef remove + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char remove (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_remove) || defined (__stub___remove) +choke me +#else +char (*f) () = remove; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != remove; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_remove=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_remove=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_remove" >&5 +echo "${ECHO_T}$ac_cv_func_remove" >&6 + + if test $ac_cv_func_remove = no; then + echo "$as_me:$LINENO: checking for remove in -lposix" >&5 +echo $ECHO_N "checking for remove in -lposix... $ECHO_C" >&6 +if test "${ac_cv_lib_posix_remove+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lposix $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char remove (); +int +main () +{ +remove (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_posix_remove=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_posix_remove=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_posix_remove" >&5 +echo "${ECHO_T}$ac_cv_lib_posix_remove" >&6 +if test $ac_cv_lib_posix_remove = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lposix" +fi + + fi + + # BSDI BSD/OS 2.1 needs -lipc for XOpenDisplay. + echo "$as_me:$LINENO: checking for shmat" >&5 +echo $ECHO_N "checking for shmat... $ECHO_C" >&6 +if test "${ac_cv_func_shmat+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define shmat to an innocuous variant, in case <limits.h> declares shmat. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define shmat innocuous_shmat + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char shmat (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef shmat + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shmat (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_shmat) || defined (__stub___shmat) +choke me +#else +char (*f) () = shmat; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != shmat; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_shmat=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_shmat=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_shmat" >&5 +echo "${ECHO_T}$ac_cv_func_shmat" >&6 + + if test $ac_cv_func_shmat = no; then + echo "$as_me:$LINENO: checking for shmat in -lipc" >&5 +echo $ECHO_N "checking for shmat in -lipc... $ECHO_C" >&6 +if test "${ac_cv_lib_ipc_shmat+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lipc $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shmat (); +int +main () +{ +shmat (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ipc_shmat=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_ipc_shmat=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_ipc_shmat" >&5 +echo "${ECHO_T}$ac_cv_lib_ipc_shmat" >&6 +if test $ac_cv_lib_ipc_shmat = yes; then + X_EXTRA_LIBS="$X_EXTRA_LIBS -lipc" +fi + + fi + fi + + # Check for libraries that X11R6 Xt/Xaw programs need. + ac_save_LDFLAGS=$LDFLAGS + test -n "$x_libraries" && LDFLAGS="$LDFLAGS -L$x_libraries" + # SM needs ICE to (dynamically) link under SunOS 4.x (so we have to + # check for ICE first), but we must link in the order -lSM -lICE or + # we get undefined symbols. So assume we have SM if we have ICE. + # These have to be linked with before -lX11, unlike the other + # libraries we check for below, so use a different variable. + # John Interrante, Karl Berry + echo "$as_me:$LINENO: checking for IceConnectionNumber in -lICE" >&5 +echo $ECHO_N "checking for IceConnectionNumber in -lICE... $ECHO_C" >&6 +if test "${ac_cv_lib_ICE_IceConnectionNumber+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lICE $X_EXTRA_LIBS $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char IceConnectionNumber (); +int +main () +{ +IceConnectionNumber (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ICE_IceConnectionNumber=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_ICE_IceConnectionNumber=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_ICE_IceConnectionNumber" >&5 +echo "${ECHO_T}$ac_cv_lib_ICE_IceConnectionNumber" >&6 +if test $ac_cv_lib_ICE_IceConnectionNumber = yes; then + X_PRE_LIBS="$X_PRE_LIBS -lSM -lICE" +fi + + LDFLAGS=$ac_save_LDFLAGS + +fi + + + +ac_help="$ac_help + --with-symbols add the -g flag to produce the debug symbols + --with-use-malloc add the __USE_MALLOC flag to avoid the STL allocators + --with-info define INFO at compile time to get basic log output + --with-valgrind clean up allocated buffers to avoid valgrind warnings + --with-version use this version for produced libraries + + --with-static-png enable static linking of PNG library + --with-static-jpeg enable static linking of JPEG library + --with-static-z enable static linking of Z library" + + + +echo "$as_me:$LINENO: checking for Cygwin32 environment" >&5 +echo $ECHO_N "checking for Cygwin32 environment... $ECHO_C" >&6 +if test "${nxconf_cv_cygwin32+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +return __CYGWIN32__; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + nxconf_cv_cygwin32=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +nxconf_cv_cygwin32=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f conftest* +fi +echo "$as_me:$LINENO: result: $nxconf_cv_cygwin32" >&5 +echo "${ECHO_T}$nxconf_cv_cygwin32" >&6 +CYGWIN32= +test "$nxconf_cv_cygwin32" = yes && CYGWIN32=yes + + + +echo "$as_me:$LINENO: checking for Amd64 environment" >&5 +echo $ECHO_N "checking for Amd64 environment... $ECHO_C" >&6 +if test "${nxconf_cv_amd64+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +return (__amd64__ || __x86_64__); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + nxconf_cv_amd64=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +nxconf_cv_amd64=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f conftest* +fi +echo "$as_me:$LINENO: result: $nxconf_cv_amd64" >&5 +echo "${ECHO_T}$nxconf_cv_amd64" >&6 +AMD64= +test "$nxconf_cv_amd64" = yes && AMD64=yes + + + +echo "$as_me:$LINENO: checking for Darwin environment" >&5 +echo $ECHO_N "checking for Darwin environment... $ECHO_C" >&6 +if test "${nxconf_cv_darwin+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +return __APPLE__; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + nxconf_cv_darwin=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +nxconf_cv_darwin=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f conftest* +fi +echo "$as_me:$LINENO: result: $nxconf_cv_darwin" >&5 +echo "${ECHO_T}$nxconf_cv_darwin" >&6 +DARWIN= +test "$nxconf_cv_darwin" = yes && DARWIN=yes + + + +echo "$as_me:$LINENO: checking for Solaris environment" >&5 +echo $ECHO_N "checking for Solaris environment... $ECHO_C" >&6 +if test "${nxconf_cv_sun+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +return __sun; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + nxconf_cv_sun=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +nxconf_cv_sun=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f conftest* +fi +echo "$as_me:$LINENO: result: $nxconf_cv_sun" >&5 +echo "${ECHO_T}$nxconf_cv_sun" >&6 +SUN= +test "$nxconf_cv_sun" = yes && SUN=yes + + + +echo "$as_me:$LINENO: checking for FreeBSD environment" >&5 +echo $ECHO_N "checking for FreeBSD environment... $ECHO_C" >&6 +if test "${nxconf_cv_freebsd+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +return __FreeBSD__; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + nxconf_cv_freebsd=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +nxconf_cv_freebsd=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f conftest* +fi +echo "$as_me:$LINENO: result: $nxconf_cv_freebsd" >&5 +echo "${ECHO_T}$nxconf_cv_freebsd" >&6 +FreeBSD= +test "$nxconf_cv_freebsd" = yes && FreeBSD=yes + + +if test "$CYGWIN32" != yes -a "$DARWIN" != yes; then + CXXFLAGS="$CXXFLAGS -fPIC" + CFLAGS="$CFLAGS -fPIC" +fi + + +if test "$SUN" = yes; then + LIBS="$LIBS -L/usr/sfw/lib -lsocket " + CXXFLAGS="$CXXFLAGS -I/usr/sfw/include" + CFLAGS="$CFLAGS -I/usr/sfw/include" +fi + + +if test "$FreeBSD" = yes; then + LIBS="$LIBS -L/usr/local/lib" + CXXFLAGS="$CXXFLAGS -I/usr/local/include" + CFLAGS="$CFLAGS -I/usr/local/include" +fi + + +if test "$DARWIN" = yes; then + LDFLAGS="$LDFLAGS -bundle" +elif test "$SUN" = yes; then + LDFLAGS="$LDFLAGS -G -h \$(LIBLOAD)" +else + LDFLAGS="$LDFLAGS -Wl,-soname,\$(LIBLOAD)" +fi + + + +echo "$as_me:$LINENO: checking for in_addr_t" >&5 +echo $ECHO_N "checking for in_addr_t... $ECHO_C" >&6 +if test "${nxconf_cv_inaddrt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <netinet/in.h> +int +main () +{ +in_addr_t t; t = 1; return t; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + nxconf_cv_inaddrt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +nxconf_cv_inaddrt=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +rm -f conftest* +fi +echo "$as_me:$LINENO: result: $nxconf_cv_inaddrt" >&5 +echo "${ECHO_T}$nxconf_cv_inaddrt" >&6 +INADDRT= +test "$nxconf_cv_inaddrt" = yes && INADDRT=yes + + +if test "$INADDRT" != yes ; then + echo -e "using unsigned int for type in_addr_t" + CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=unsigned" + CFLAGS="$CFLAGS -DIN_ADDR_T=unsigned" +else + CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=in_addr_t" + CFLAGS="$CFLAGS -DIN_ADDR_T=in_addr_t" +fi + + + + +if test "${with_version}" = yes; then + VERSION=${ac_option} +else + VERSION=`cat VERSION` +fi +echo -e "compiling version ${VERSION}" + +LIBVERSION=`echo ${VERSION} | cut -d '.' -f 1` + +CXXFLAGS="$CXXFLAGS -DVERSION=\\\"${VERSION}\\\"" +CFLAGS="$CFLAGS -DVERSION=\\\"${VERSION}\\\"" + + +if test "${with_static_png}" = yes; then + echo -e "enabling static linking of PNG library" + if test "$SUN" = yes && test -f "/usr/sfw/lib/libpng.a"; then + LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libpng.a" + else + if test -f "/usr/lib/libpng.a" ; then + LIBSTATIC="$LIBSTATIC /usr/lib/libpng.a" + else + if test -f "/usr/local/lib/libpng.a" ; then + echo -e "assuming libpng.a in /usr/local/lib" + LIBSTATIC="$LIBSTATIC /usr/local/lib/libpng.a" + else + echo -e "Warning: assuming libpng.a in the local path" + LIBSTATIC="$LIBSTATIC libpng.a" + fi + fi + fi +else + echo -e "enabling dynamic linking of PNG library" + LIBSHARED="$LIBSHARED -lpng" +fi + + +if test "${with_static_jpeg}" = yes; then + echo -e "enabling static linking of JPEG library" + if test "$SUN" = yes && test -f "/usr/sfw/lib/libjpeg.a"; then + LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libjpeg.a" + else + if test -f "/usr/lib/libjpeg.a" ; then + LIBSTATIC="$LIBSTATIC /usr/lib/libjpeg.a" + else + if test -f "/usr/local/lib/libjpeg.a" ; then + echo -e "assuming libjpeg.a in /usr/local/lib" + LIBSTATIC="$LIBSTATIC /usr/local/lib/libjpeg.a" + else + echo -e "Warning: assuming libjpeg.a in the local path" + LIBSTATIC="$LIBSTATIC libjpeg.a" + fi + fi + fi +else + echo -e "enabling dynamic linking of JPEG library" + LIBSHARED="$LIBSHARED -ljpeg" +fi + + +if test "${with_static_z}" = yes; then + echo -e "enabling static linking of Z library" + if test "$SUN" = yes && test -f "/usr/sfw/lib/libz.a"; then + LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libz.a" + else + if test -f "/usr/lib/libz.a" ; then + LIBSTATIC="$LIBSTATIC /usr/lib/libz.a" + else + if test -f "/usr/local/lib/libz.a" ; then + echo -e "assuming libz.a in /usr/local/lib" + LIBSTATIC="$LIBSTATIC /usr/local/lib/libz.a" + else + echo -e "Warning: assuming libz.a in the local path" + LIBSTATIC="$LIBSTATIC libz.a" + fi + fi + fi +else + echo -e "enabling dynamic linking of Z library" + LIBSHARED="$LIBSHARED -lz" +fi + + +if test "$DARWIN" = yes ; then + LIBS="$LIBS $LIBSTATIC $LIBSHARED" +elif test "$SUN" = yes ; then + LIBS="$LIBS $LIBSTATIC $LIBSHARED" +else + LIBS="$LIBS $LIBSTATIC -shared $LIBSHARED" +fi + + +if test "${with_symbols}" = yes; then + echo -e "enabling production of debug symbols" + CXXFLAGS="-g $CXXFLAGS" + CFLAGS="-g $CFLAGS" +else + echo -e "disabling production of debug symbols" +fi + + +if test "${with_use_malloc}" = yes; then + echo -e "disabling use of the STL allocators" + CXXFLAGS="$CXXFLAGS -D__USE_MALLOC" +else + echo -e "enabling use of the STL allocators" +fi + + +if test "${with_info}" = yes; then + echo -e "enabling info output in the log file" + CXXFLAGS="$CXXFLAGS -DINFO" + CFLAGS="$CFLAGS -DINFO" +else + echo -e "disabling info output in the log file" +fi + + +if test "${with_valgrind}" = yes; then + echo -e "enabling valgrind memory checker workarounds" + CXXFLAGS="$CXXFLAGS -DVALGRIND" + CFLAGS="$CFLAGS -DVALGRIND" +else + echo -e "disabling valgrind memory checker workarounds" +fi + + + + +if test -x "../nx-X11/config/makedepend/makedepend" ; then + MAKEDEPEND=../nx-X11/config/makedepend/makedepend +else + if test -x "/usr/X11R6/bin/makedepend" ; then + MAKEDEPEND=/usr/X11R6/bin/makedepend + else + if test -x "/usr/openwin/bin/makedepend" ; then + MAKEDEPEND=/usr/openwin/bin/makedepend + else + MAKEDEPEND=/usr/bin/makedepend + fi + fi +fi + + + + +if test "$CYGWIN32" = yes; then + ALL="\$(LIBCYGARCHIVE) \$(LIBCYGSHARED) \$(LIBARCHIVE)" + LIBS="-lstdc++ -lpng -ljpeg -lz" +else + ALL="\$(LIBFULL) \$(LIBLOAD) \$(LIBSHARED) \$(LIBARCHIVE)" +fi + + ac_config_files="$ac_config_files Makefile" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then we branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +cat >confdef2opt.sed <<\_ACEOF +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g +t quote +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g +t quote +d +: quote +s,[ `~#$^&*(){}\\|;'"<>?],\\&,g +s,\[,\\&,g +s,\],\\&,g +s,\$,$$,g +p +_ACEOF +# We use echo to avoid assuming a particular line-breaking character. +# The extra dot is to prevent the shell from consuming trailing +# line-breaks from the sub-command output. A line-break within +# single-quotes doesn't work because, if this script is created in a +# platform that uses two characters for line-breaks (e.g., DOS), tr +# would break. +ac_LF_and_DOT=`echo; echo .` +DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` +rm -f confdef2opt.sed + + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to <bug-autoconf@gnu.org>." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@armcxx@,$armcxx,;t t +s,@armcc@,$armcc,;t t +s,@CXX@,$CXX,;t t +s,@CXXFLAGS@,$CXXFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CXX@,$ac_ct_CXX,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@CXXCPP@,$CXXCPP,;t t +s,@X_CFLAGS@,$X_CFLAGS,;t t +s,@X_PRE_LIBS@,$X_PRE_LIBS,;t t +s,@X_LIBS@,$X_LIBS,;t t +s,@X_EXTRA_LIBS@,$X_EXTRA_LIBS,;t t +s,@LIBVERSION@,$LIBVERSION,;t t +s,@VERSION@,$VERSION,;t t +s,@MAKEDEPEND@,$MAKEDEPEND,;t t +s,@ALL@,$ALL,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/nxcomp/configure.in b/nxcomp/configure.in new file mode 100644 index 000000000..e9ab81da8 --- /dev/null +++ b/nxcomp/configure.in @@ -0,0 +1,393 @@ +dnl Process this file with autoconf to produce a configure script. + +dnl Prolog + +AC_INIT(NX.h) +AC_PREREQ(2.13) + +dnl Set our default compilation flags. + +CXXFLAGS="-O3 -fno-rtti -fno-exceptions" +CFLAGS="-O3" + +dnl Reset default linking directives. + +LIBSTATIC="" +LIBSHARED="" + +dnl Prefer headers and libraries from nx-X11, if present. + +if test -d "../nx-X11/include" ; then + CXXFLAGS="$CXXFLAGS -I../nx-X11/exports/include" + CFLAGS="$CFLAGS -I../nx-X11/exports/include" + LIBS="$LIBS -L../nx-X11/exports/lib" +fi + +dnl Check whether --with-ipaq was given. + +if test "${with_ipaq}" = yes; then + echo -e "enabling IPAQ configuration" + CXX="arm-linux-c++" + CC="arm-linux-gcc" + unset ac_cv_prog_armcxx + unset ac_cv_prog_armcc + unset ac_cv_prog_CXXCPP + AC_CHECK_PROG([armcxx],["$CXX"],[yes],[no],[$PATH]) + AC_CHECK_PROG([armcc],["$CC"],[yes],[no],[$PATH]) + if test $armcxx = "yes" && test $armcc = "yes" ; then + ac_cv_prog_CXX="$CXX" + ac_cv_prog_CC="$CC" + else + AC_MSG_ERROR(Installation or configuration problem. Cannot find compiler for arm-linux.) + fi +else + unset ac_cv_prog_CXX + unset ac_cv_prog_CC + unset ac_cv_prog_CXXCPP +fi + +dnl Check for programs. + +AC_PROG_CXX +AC_PROG_CC +AC_LANG_CPLUSPLUS + +dnl Check whether option -Wno-deprecated +dnl is needed by GCC compiler. + +AC_MSG_CHECKING([whether compiler needs -Wno-deprecated]) +gcc_version=`${CC} --version | grep 'gcc (GCC) [[3-4]].' | head -n 1` +case "${gcc_version}" in + gcc*) + AC_MSG_RESULT([yes]) + CXXFLAGS="$CXXFLAGS -Wno-deprecated" + ;; + + *) + AC_MSG_RESULT([no]) + ;; +esac + +AC_MSG_CHECKING([whether compiler accepts -Wmissing-declarations]) +gcc_version=`${CC} --version | grep 'gcc (GCC) [[3-4]].' | head -n 1` +case "${gcc_version}" in + gcc*) + AC_MSG_RESULT([no]) + ;; + + *) + AC_MSG_RESULT([yes]) + CXXFLAGS="$CXXFLAGS -Wmissing-declarations" + ;; +esac + +dnl Check for BSD compatible install. + +AC_PROG_INSTALL + +dnl Check for extra header files. + +AC_PATH_XTRA + +dnl Custom addition. + +ac_help="$ac_help + --with-symbols add the -g flag to produce the debug symbols + --with-use-malloc add the __USE_MALLOC flag to avoid the STL allocators + --with-info define INFO at compile time to get basic log output + --with-valgrind clean up allocated buffers to avoid valgrind warnings + --with-version use this version for produced libraries + + --with-static-png enable static linking of PNG library + --with-static-jpeg enable static linking of JPEG library + --with-static-z enable static linking of Z library" + +dnl Check to see if we're running under Cygwin32. + +AC_DEFUN(nxconf_CYGWIN32, +[AC_CACHE_CHECK(for Cygwin32 environment, nxconf_cv_cygwin32, +[AC_TRY_COMPILE(,[return __CYGWIN32__;], +nxconf_cv_cygwin32=yes, nxconf_cv_cygwin32=no) +rm -f conftest*]) +CYGWIN32= +test "$nxconf_cv_cygwin32" = yes && CYGWIN32=yes]) +nxconf_CYGWIN32 + +dnl Check whether we're building on a AMD64. + +AC_DEFUN(nxconf_AMD64, +[AC_CACHE_CHECK(for Amd64 environment, nxconf_cv_amd64, +[AC_TRY_COMPILE(,[return (__amd64__ || __x86_64__);], +nxconf_cv_amd64=yes, nxconf_cv_amd64=no) +rm -f conftest*]) +AMD64= +test "$nxconf_cv_amd64" = yes && AMD64=yes]) +nxconf_AMD64 + +dnl Check for Darwin environment. + +AC_DEFUN(nxconf_DARWIN, +[AC_CACHE_CHECK(for Darwin environment, nxconf_cv_darwin, +[AC_TRY_COMPILE(,[return __APPLE__;], +nxconf_cv_darwin=yes, nxconf_cv_darwin=no) +rm -f conftest*]) +DARWIN= +test "$nxconf_cv_darwin" = yes && DARWIN=yes]) +nxconf_DARWIN + +dnl Check to see if we're running under Solaris. + +AC_DEFUN(nxconf_SUN, +[AC_CACHE_CHECK(for Solaris environment, nxconf_cv_sun, +[AC_TRY_COMPILE(,[return __sun;], +nxconf_cv_sun=yes, nxconf_cv_sun=no) +rm -f conftest*]) +SUN= +test "$nxconf_cv_sun" = yes && SUN=yes]) +nxconf_SUN + +dnl Check to see if we're running under FreeBSD. + +AC_DEFUN(nxconf_FreeBSD, +[AC_CACHE_CHECK(for FreeBSD environment, nxconf_cv_freebsd, +[AC_TRY_COMPILE(,[return __FreeBSD__;], +nxconf_cv_freebsd=yes, nxconf_cv_freebsd=no) +rm -f conftest*]) +FreeBSD= +test "$nxconf_cv_freebsd" = yes && FreeBSD=yes]) +nxconf_FreeBSD + +dnl Build PIC libraries. + +if test "$CYGWIN32" != yes -a "$DARWIN" != yes; then + CXXFLAGS="$CXXFLAGS -fPIC" + CFLAGS="$CFLAGS -fPIC" +fi + +dnl Solaris requires the socket and gcc_s libs explicitly linked. +dnl Note also that headers from default /usr/openwin/include/X11 +dnl cause a warning due to pragma in Xmd.h. + +if test "$SUN" = yes; then + LIBS="$LIBS -L/usr/sfw/lib -lsocket " + CXXFLAGS="$CXXFLAGS -I/usr/sfw/include" + CFLAGS="$CFLAGS -I/usr/sfw/include" +fi + +dnl On FreeBSD search libraries and includes under /usr/local. + +if test "$FreeBSD" = yes; then + LIBS="$LIBS -L/usr/local/lib" + CXXFLAGS="$CXXFLAGS -I/usr/local/include" + CFLAGS="$CFLAGS -I/usr/local/include" +fi + +dnl Under Darwin we don't have support for -soname option and +dnl we need the -bundle flag. Under Solaris, instead, we need +dnl the options -G -h. + +if test "$DARWIN" = yes; then + LDFLAGS="$LDFLAGS -bundle" +elif test "$SUN" = yes; then + LDFLAGS="$LDFLAGS -G -h \$(LIBLOAD)" +else + LDFLAGS="$LDFLAGS -Wl,-soname,\$(LIBLOAD)" +fi + +dnl Check to see if in_addr_t is defined. +dnl Could use a specific configure test. + +AC_DEFUN(nxconf_INADDRT, +[AC_CACHE_CHECK(for in_addr_t, nxconf_cv_inaddrt, +[AC_TRY_COMPILE([#include <netinet/in.h>],[in_addr_t t; t = 1; return t;], +nxconf_cv_inaddrt=yes, nxconf_cv_inaddrt=no) +rm -f conftest*]) +INADDRT= +test "$nxconf_cv_inaddrt" = yes && INADDRT=yes]) +nxconf_INADDRT + +dnl If in_addr_t is not defined use unsigned int. + +if test "$INADDRT" != yes ; then + echo -e "using unsigned int for type in_addr_t" + CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=unsigned" + CFLAGS="$CFLAGS -DIN_ADDR_T=unsigned" +else + CXXFLAGS="$CXXFLAGS -DIN_ADDR_T=in_addr_t" + CFLAGS="$CFLAGS -DIN_ADDR_T=in_addr_t" +fi + +dnl Check whether --with-version was given. + +AC_SUBST(LIBVERSION) +AC_SUBST(VERSION) +if test "${with_version}" = yes; then + VERSION=${ac_option} +else + VERSION=`cat VERSION` +fi +echo -e "compiling version ${VERSION}" + +LIBVERSION=`echo ${VERSION} | cut -d '.' -f 1` + +CXXFLAGS="$CXXFLAGS -DVERSION=\\\"${VERSION}\\\"" +CFLAGS="$CFLAGS -DVERSION=\\\"${VERSION}\\\"" + +dnl Check whether --with-static-png was given and +dnl add -lpng or libpng.a to linking. + +if test "${with_static_png}" = yes; then + echo -e "enabling static linking of PNG library" + if test "$SUN" = yes && test -f "/usr/sfw/lib/libpng.a"; then + LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libpng.a" + else + if test -f "/usr/lib/libpng.a" ; then + LIBSTATIC="$LIBSTATIC /usr/lib/libpng.a" + else + if test -f "/usr/local/lib/libpng.a" ; then + echo -e "assuming libpng.a in /usr/local/lib" + LIBSTATIC="$LIBSTATIC /usr/local/lib/libpng.a" + else + echo -e "Warning: assuming libpng.a in the local path" + LIBSTATIC="$LIBSTATIC libpng.a" + fi + fi + fi +else + echo -e "enabling dynamic linking of PNG library" + LIBSHARED="$LIBSHARED -lpng" +fi + +dnl Check whether --with-static-jpeg was given and +dnl add -ljpeg or libjpeg.a to linking. + +if test "${with_static_jpeg}" = yes; then + echo -e "enabling static linking of JPEG library" + if test "$SUN" = yes && test -f "/usr/sfw/lib/libjpeg.a"; then + LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libjpeg.a" + else + if test -f "/usr/lib/libjpeg.a" ; then + LIBSTATIC="$LIBSTATIC /usr/lib/libjpeg.a" + else + if test -f "/usr/local/lib/libjpeg.a" ; then + echo -e "assuming libjpeg.a in /usr/local/lib" + LIBSTATIC="$LIBSTATIC /usr/local/lib/libjpeg.a" + else + echo -e "Warning: assuming libjpeg.a in the local path" + LIBSTATIC="$LIBSTATIC libjpeg.a" + fi + fi + fi +else + echo -e "enabling dynamic linking of JPEG library" + LIBSHARED="$LIBSHARED -ljpeg" +fi + +dnl Check whether --with-static-z was given and +dnl add -lz or libz.a to linking. + +if test "${with_static_z}" = yes; then + echo -e "enabling static linking of Z library" + if test "$SUN" = yes && test -f "/usr/sfw/lib/libz.a"; then + LIBSTATIC="$LIBSTATIC /usr/sfw/lib/libz.a" + else + if test -f "/usr/lib/libz.a" ; then + LIBSTATIC="$LIBSTATIC /usr/lib/libz.a" + else + if test -f "/usr/local/lib/libz.a" ; then + echo -e "assuming libz.a in /usr/local/lib" + LIBSTATIC="$LIBSTATIC /usr/local/lib/libz.a" + else + echo -e "Warning: assuming libz.a in the local path" + LIBSTATIC="$LIBSTATIC libz.a" + fi + fi + fi +else + echo -e "enabling dynamic linking of Z library" + LIBSHARED="$LIBSHARED -lz" +fi + +dnl Finally compose the LIB variable. + +if test "$DARWIN" = yes ; then + LIBS="$LIBS $LIBSTATIC $LIBSHARED" +elif test "$SUN" = yes ; then + LIBS="$LIBS $LIBSTATIC $LIBSHARED" +else + LIBS="$LIBS $LIBSTATIC -shared $LIBSHARED" +fi + +dnl Check whether --with-symbols or --without-symbols was +dnl given and set the required optimization level. + +if test "${with_symbols}" = yes; then + echo -e "enabling production of debug symbols" + CXXFLAGS="-g $CXXFLAGS" + CFLAGS="-g $CFLAGS" +else + echo -e "disabling production of debug symbols" +fi + +dnl Check whether --with-use-malloc or --without-use-malloc +dnl was given. + +if test "${with_use_malloc}" = yes; then + echo -e "disabling use of the STL allocators" + CXXFLAGS="$CXXFLAGS -D__USE_MALLOC" +else + echo -e "enabling use of the STL allocators" +fi + +dnl Check whether --with-info or --without-info was given. + +if test "${with_info}" = yes; then + echo -e "enabling info output in the log file" + CXXFLAGS="$CXXFLAGS -DINFO" + CFLAGS="$CFLAGS -DINFO" +else + echo -e "disabling info output in the log file" +fi + +dnl Check whether --with-valgrind or --without-valgrind was given. + +if test "${with_valgrind}" = yes; then + echo -e "enabling valgrind memory checker workarounds" + CXXFLAGS="$CXXFLAGS -DVALGRIND" + CFLAGS="$CFLAGS -DVALGRIND" +else + echo -e "disabling valgrind memory checker workarounds" +fi + +dnl Find makedepend somewhere. + +AC_SUBST(MAKEDEPEND) + +if test -x "../nx-X11/config/makedepend/makedepend" ; then + MAKEDEPEND=../nx-X11/config/makedepend/makedepend +else + if test -x "/usr/X11R6/bin/makedepend" ; then + MAKEDEPEND=/usr/X11R6/bin/makedepend + else + if test -x "/usr/openwin/bin/makedepend" ; then + MAKEDEPEND=/usr/openwin/bin/makedepend + else + MAKEDEPEND=/usr/bin/makedepend + fi + fi +fi + +dnl Determine what to build based on the platform. +dnl Override the LIBS settings on Cygwin32 so that +dnl we always link with the exact set of libraries. + +AC_SUBST(ALL) + +if test "$CYGWIN32" = yes; then + ALL="\$(LIBCYGARCHIVE) \$(LIBCYGSHARED) \$(LIBARCHIVE)" + LIBS="-lstdc++ -lpng -ljpeg -lz" +else + ALL="\$(LIBFULL) \$(LIBLOAD) \$(LIBSHARED) \$(LIBARCHIVE)" +fi + +AC_OUTPUT(Makefile) diff --git a/nxcomp/install-sh b/nxcomp/install-sh new file mode 100755 index 000000000..58719246f --- /dev/null +++ b/nxcomp/install-sh @@ -0,0 +1,238 @@ +#! /bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/nxcomp/mkinstalldirs b/nxcomp/mkinstalldirs new file mode 100755 index 000000000..936cf3407 --- /dev/null +++ b/nxcomp/mkinstalldirs @@ -0,0 +1,34 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1993-05-16 +# Last modified: 1995-03-05 +# Public domain + +errstatus=0 + +for file in ${1+"$@"} ; do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d in ${1+"$@"} ; do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" 1>&2 + mkdir "$pathcomp" > /dev/null 2>&1 || lasterr=$? + fi + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus |