aboutsummaryrefslogtreecommitdiff
path: root/nxcomp/src
diff options
context:
space:
mode:
authorMihai Moldovan <ionic@ionic.de>2017-07-26 10:38:46 +0200
committerMihai Moldovan <ionic@ionic.de>2017-07-26 10:38:46 +0200
commit7897834ce578dc394649ed09acb9f95fc04915ff (patch)
treea0bcbfa2c4ae1111ad3d49390c7abd0ae3a9a2ed /nxcomp/src
parent9193d11eeeea933e293acd5e0f03fa4e9887186b (diff)
parent4afc641fdd8c03bd708f50e0c3691b5de7ea1dba (diff)
downloadnx-libs-7897834ce578dc394649ed09acb9f95fc04915ff.tar.gz
nx-libs-7897834ce578dc394649ed09acb9f95fc04915ff.tar.bz2
nx-libs-7897834ce578dc394649ed09acb9f95fc04915ff.zip
Merge branch 'sunweaver-pr/nxcomp-autoreconf' into 3.6.x
Attributes GH PR #480: https://github.com/ArcticaProject/nx-libs/pull/480
Diffstat (limited to 'nxcomp/src')
-rw-r--r--nxcomp/src/ActionCache.cpp51
-rw-r--r--nxcomp/src/ActionCache.h49
-rw-r--r--nxcomp/src/Agent.cpp84
-rw-r--r--nxcomp/src/Agent.h263
-rw-r--r--nxcomp/src/Alpha.cpp138
-rw-r--r--nxcomp/src/Alpha.h35
-rw-r--r--nxcomp/src/Auth.cpp671
-rw-r--r--nxcomp/src/Auth.h127
-rw-r--r--nxcomp/src/Bitmap.cpp118
-rw-r--r--nxcomp/src/Bitmap.h36
-rw-r--r--nxcomp/src/BlockCache.cpp81
-rw-r--r--nxcomp/src/BlockCache.h67
-rw-r--r--nxcomp/src/BlockCacheSet.cpp147
-rw-r--r--nxcomp/src/BlockCacheSet.h49
-rw-r--r--nxcomp/src/ChangeGC.cpp184
-rw-r--r--nxcomp/src/ChangeGC.h185
-rw-r--r--nxcomp/src/ChangeProperty.cpp191
-rw-r--r--nxcomp/src/ChangeProperty.h189
-rw-r--r--nxcomp/src/Channel.cpp2039
-rw-r--r--nxcomp/src/Channel.h664
-rw-r--r--nxcomp/src/ChannelCache.cpp68
-rw-r--r--nxcomp/src/ChannelCache.h61
-rw-r--r--nxcomp/src/ChannelEndPoint.cpp349
-rw-r--r--nxcomp/src/ChannelEndPoint.h71
-rw-r--r--nxcomp/src/ChannelStore.h54
-rw-r--r--nxcomp/src/CharCache.cpp73
-rw-r--r--nxcomp/src/CharCache.h91
-rw-r--r--nxcomp/src/Children.cpp1059
-rw-r--r--nxcomp/src/ClearArea.cpp125
-rw-r--r--nxcomp/src/ClearArea.h182
-rw-r--r--nxcomp/src/ClientCache.cpp392
-rw-r--r--nxcomp/src/ClientCache.h417
-rw-r--r--nxcomp/src/ClientChannel.cpp7881
-rw-r--r--nxcomp/src/ClientChannel.h434
-rw-r--r--nxcomp/src/ClientProxy.cpp553
-rw-r--r--nxcomp/src/ClientProxy.h113
-rw-r--r--nxcomp/src/ClientReadBuffer.cpp178
-rw-r--r--nxcomp/src/ClientReadBuffer.h65
-rw-r--r--nxcomp/src/ClientStore.cpp226
-rw-r--r--nxcomp/src/ClientStore.h143
-rw-r--r--nxcomp/src/Colormap.cpp106
-rw-r--r--nxcomp/src/Colormap.h32
-rw-r--r--nxcomp/src/ConfigureWindow.cpp142
-rw-r--r--nxcomp/src/ConfigureWindow.h178
-rw-r--r--nxcomp/src/Control.cpp822
-rw-r--r--nxcomp/src/Control.h764
-rw-r--r--nxcomp/src/CopyArea.cpp199
-rw-r--r--nxcomp/src/CopyArea.h192
-rw-r--r--nxcomp/src/CreateGC.cpp194
-rw-r--r--nxcomp/src/CreateGC.h186
-rw-r--r--nxcomp/src/CreatePixmap.cpp280
-rw-r--r--nxcomp/src/CreatePixmap.h162
-rw-r--r--nxcomp/src/DecodeBuffer.cpp639
-rw-r--r--nxcomp/src/DecodeBuffer.h138
-rw-r--r--nxcomp/src/EncodeBuffer.cpp623
-rw-r--r--nxcomp/src/EncodeBuffer.h183
-rw-r--r--nxcomp/src/FillPoly.cpp239
-rw-r--r--nxcomp/src/FillPoly.h200
-rw-r--r--nxcomp/src/Fork.cpp110
-rw-r--r--nxcomp/src/Fork.h31
-rw-r--r--nxcomp/src/FreeCache.h42
-rw-r--r--nxcomp/src/GenericChannel.cpp495
-rw-r--r--nxcomp/src/GenericChannel.h440
-rw-r--r--nxcomp/src/GenericReadBuffer.cpp82
-rw-r--r--nxcomp/src/GenericReadBuffer.h61
-rw-r--r--nxcomp/src/GenericReply.cpp302
-rw-r--r--nxcomp/src/GenericReply.h161
-rw-r--r--nxcomp/src/GenericRequest.cpp334
-rw-r--r--nxcomp/src/GenericRequest.h160
-rw-r--r--nxcomp/src/GetImage.cpp177
-rw-r--r--nxcomp/src/GetImage.h190
-rw-r--r--nxcomp/src/GetImageReply.cpp194
-rw-r--r--nxcomp/src/GetImageReply.h150
-rw-r--r--nxcomp/src/GetProperty.cpp119
-rw-r--r--nxcomp/src/GetProperty.h183
-rw-r--r--nxcomp/src/GetPropertyReply.cpp304
-rw-r--r--nxcomp/src/GetPropertyReply.h160
-rw-r--r--nxcomp/src/ImageText16.cpp231
-rw-r--r--nxcomp/src/ImageText16.h190
-rw-r--r--nxcomp/src/ImageText8.cpp231
-rw-r--r--nxcomp/src/ImageText8.h190
-rw-r--r--nxcomp/src/IntCache.cpp230
-rw-r--r--nxcomp/src/IntCache.h119
-rw-r--r--nxcomp/src/InternAtom.cpp131
-rw-r--r--nxcomp/src/InternAtom.h178
-rw-r--r--nxcomp/src/Jpeg.cpp890
-rw-r--r--nxcomp/src/Jpeg.h36
-rw-r--r--nxcomp/src/Keeper.cpp612
-rw-r--r--nxcomp/src/Keeper.h199
-rw-r--r--nxcomp/src/List.cpp112
-rw-r--r--nxcomp/src/List.h95
-rw-r--r--nxcomp/src/ListFontsReply.cpp213
-rw-r--r--nxcomp/src/ListFontsReply.h146
-rw-r--r--nxcomp/src/Loop.cpp16685
-rw-r--r--nxcomp/src/MD5.c403
-rw-r--r--nxcomp/src/Makefile.am146
-rw-r--r--nxcomp/src/Message.cpp2343
-rw-r--r--nxcomp/src/Message.h1089
-rw-r--r--nxcomp/src/Misc.cpp1934
-rw-r--r--nxcomp/src/Misc.h279
-rw-r--r--nxcomp/src/NXmitshm.h56
-rw-r--r--nxcomp/src/NXrender.h78
-rw-r--r--nxcomp/src/OpcodeCache.h53
-rw-r--r--nxcomp/src/OpcodeStore.cpp88
-rw-r--r--nxcomp/src/OpcodeStore.h91
-rw-r--r--nxcomp/src/Pack.c180
-rw-r--r--nxcomp/src/Pgn.cpp809
-rw-r--r--nxcomp/src/Pgn.h42
-rw-r--r--nxcomp/src/Pipe.cpp433
-rw-r--r--nxcomp/src/Pipe.h35
-rw-r--r--nxcomp/src/PolyArc.cpp162
-rw-r--r--nxcomp/src/PolyArc.h185
-rw-r--r--nxcomp/src/PolyFillArc.cpp162
-rw-r--r--nxcomp/src/PolyFillArc.h185
-rw-r--r--nxcomp/src/PolyFillRectangle.cpp160
-rw-r--r--nxcomp/src/PolyFillRectangle.h185
-rw-r--r--nxcomp/src/PolyLine.cpp168
-rw-r--r--nxcomp/src/PolyLine.h186
-rw-r--r--nxcomp/src/PolyPoint.cpp168
-rw-r--r--nxcomp/src/PolyPoint.h186
-rw-r--r--nxcomp/src/PolySegment.cpp162
-rw-r--r--nxcomp/src/PolySegment.h185
-rw-r--r--nxcomp/src/PolyText16.cpp312
-rw-r--r--nxcomp/src/PolyText16.h188
-rw-r--r--nxcomp/src/PolyText8.cpp310
-rw-r--r--nxcomp/src/PolyText8.h188
-rw-r--r--nxcomp/src/Proxy.cpp6529
-rw-r--r--nxcomp/src/Proxy.h1276
-rw-r--r--nxcomp/src/ProxyReadBuffer.cpp211
-rw-r--r--nxcomp/src/ProxyReadBuffer.h57
-rw-r--r--nxcomp/src/PutImage.cpp411
-rw-r--r--nxcomp/src/PutImage.h172
-rw-r--r--nxcomp/src/PutPackedImage.cpp604
-rw-r--r--nxcomp/src/PutPackedImage.h218
-rw-r--r--nxcomp/src/QueryFontReply.cpp154
-rw-r--r--nxcomp/src/QueryFontReply.h136
-rw-r--r--nxcomp/src/ReadBuffer.cpp639
-rw-r--r--nxcomp/src/ReadBuffer.h128
-rw-r--r--nxcomp/src/RenderAddGlyphs.cpp233
-rw-r--r--nxcomp/src/RenderAddGlyphs.h88
-rw-r--r--nxcomp/src/RenderChangePicture.cpp238
-rw-r--r--nxcomp/src/RenderChangePicture.h88
-rw-r--r--nxcomp/src/RenderComposite.cpp400
-rw-r--r--nxcomp/src/RenderComposite.h88
-rw-r--r--nxcomp/src/RenderCompositeGlyphs.cpp629
-rw-r--r--nxcomp/src/RenderCompositeGlyphs.h100
-rw-r--r--nxcomp/src/RenderCreateGlyphSet.cpp185
-rw-r--r--nxcomp/src/RenderCreateGlyphSet.h88
-rw-r--r--nxcomp/src/RenderCreatePicture.cpp278
-rw-r--r--nxcomp/src/RenderCreatePicture.h88
-rw-r--r--nxcomp/src/RenderExtension.cpp427
-rw-r--r--nxcomp/src/RenderExtension.h504
-rw-r--r--nxcomp/src/RenderFillRectangles.cpp237
-rw-r--r--nxcomp/src/RenderFillRectangles.h88
-rw-r--r--nxcomp/src/RenderFreeGlyphSet.cpp166
-rw-r--r--nxcomp/src/RenderFreeGlyphSet.h88
-rw-r--r--nxcomp/src/RenderFreePicture.cpp166
-rw-r--r--nxcomp/src/RenderFreePicture.h88
-rw-r--r--nxcomp/src/RenderGenericRequest.cpp270
-rw-r--r--nxcomp/src/RenderGenericRequest.h89
-rw-r--r--nxcomp/src/RenderMinorExtensionHeaders.h42
-rw-r--r--nxcomp/src/RenderMinorExtensionMethods.h81
-rw-r--r--nxcomp/src/RenderMinorExtensionTags.h194
-rw-r--r--nxcomp/src/RenderPictureClip.cpp303
-rw-r--r--nxcomp/src/RenderPictureClip.h88
-rw-r--r--nxcomp/src/RenderPictureFilter.cpp278
-rw-r--r--nxcomp/src/RenderPictureFilter.h88
-rw-r--r--nxcomp/src/RenderPictureTransform.cpp214
-rw-r--r--nxcomp/src/RenderPictureTransform.h88
-rw-r--r--nxcomp/src/RenderTrapezoids.cpp372
-rw-r--r--nxcomp/src/RenderTrapezoids.h88
-rw-r--r--nxcomp/src/RenderTriangles.cpp362
-rw-r--r--nxcomp/src/RenderTriangles.h88
-rw-r--r--nxcomp/src/Rgb.cpp106
-rw-r--r--nxcomp/src/Rgb.h36
-rw-r--r--nxcomp/src/Rle.cpp106
-rw-r--r--nxcomp/src/Rle.h36
-rw-r--r--nxcomp/src/SendEvent.cpp304
-rw-r--r--nxcomp/src/SendEvent.h195
-rw-r--r--nxcomp/src/SequenceQueue.cpp174
-rw-r--r--nxcomp/src/SequenceQueue.h91
-rw-r--r--nxcomp/src/ServerCache.cpp195
-rw-r--r--nxcomp/src/ServerCache.h303
-rw-r--r--nxcomp/src/ServerChannel.cpp7946
-rw-r--r--nxcomp/src/ServerChannel.h529
-rw-r--r--nxcomp/src/ServerProxy.cpp621
-rw-r--r--nxcomp/src/ServerProxy.h154
-rw-r--r--nxcomp/src/ServerReadBuffer.cpp247
-rw-r--r--nxcomp/src/ServerReadBuffer.h73
-rw-r--r--nxcomp/src/ServerStore.cpp183
-rw-r--r--nxcomp/src/ServerStore.h83
-rw-r--r--nxcomp/src/SetClipRectangles.cpp154
-rw-r--r--nxcomp/src/SetClipRectangles.h187
-rw-r--r--nxcomp/src/SetUnpackAlpha.cpp266
-rw-r--r--nxcomp/src/SetUnpackAlpha.h162
-rw-r--r--nxcomp/src/SetUnpackColormap.cpp266
-rw-r--r--nxcomp/src/SetUnpackColormap.h162
-rw-r--r--nxcomp/src/SetUnpackGeometry.cpp305
-rw-r--r--nxcomp/src/SetUnpackGeometry.h167
-rw-r--r--nxcomp/src/ShapeExtension.cpp305
-rw-r--r--nxcomp/src/ShapeExtension.h164
-rw-r--r--nxcomp/src/Socket.cpp757
-rw-r--r--nxcomp/src/Socket.h106
-rw-r--r--nxcomp/src/Split.cpp1839
-rw-r--r--nxcomp/src/Split.h543
-rw-r--r--nxcomp/src/StaticCompressor.cpp432
-rw-r--r--nxcomp/src/StaticCompressor.h80
-rw-r--r--nxcomp/src/Statistics.cpp2007
-rw-r--r--nxcomp/src/Statistics.h745
-rw-r--r--nxcomp/src/Timestamp.cpp77
-rw-r--r--nxcomp/src/Timestamp.h307
-rw-r--r--nxcomp/src/TranslateCoords.cpp115
-rw-r--r--nxcomp/src/TranslateCoords.h185
-rw-r--r--nxcomp/src/Transport.cpp3068
-rw-r--r--nxcomp/src/Transport.h577
-rw-r--r--nxcomp/src/Types.h271
-rw-r--r--nxcomp/src/Unpack.cpp1514
-rw-r--r--nxcomp/src/Unpack.h149
-rw-r--r--nxcomp/src/Vars.c54
-rw-r--r--nxcomp/src/Version.c101
-rw-r--r--nxcomp/src/WriteBuffer.cpp500
-rw-r--r--nxcomp/src/WriteBuffer.h134
-rw-r--r--nxcomp/src/XidCache.cpp51
-rw-r--r--nxcomp/src/XidCache.h49
-rw-r--r--nxcomp/src/Z.cpp146
-rw-r--r--nxcomp/src/Z.h37
226 files changed, 103327 insertions, 0 deletions
diff --git a/nxcomp/src/ActionCache.cpp b/nxcomp/src/ActionCache.cpp
new file mode 100644
index 000000000..783b1724e
--- /dev/null
+++ b/nxcomp/src/ActionCache.cpp
@@ -0,0 +1,51 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Control.h"
+
+#include "ActionCache.h"
+
+ActionCache::ActionCache()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ base_[i] = new IntCache(8);
+ }
+
+ slot_ = 0;
+ last_ = 0;
+}
+
+ActionCache::~ActionCache()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ delete base_[i];
+ }
+}
diff --git a/nxcomp/src/ActionCache.h b/nxcomp/src/ActionCache.h
new file mode 100644
index 000000000..2aedd4a07
--- /dev/null
+++ b/nxcomp/src/ActionCache.h
@@ -0,0 +1,49 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ActionCache_H
+#define ActionCache_H
+
+#include "IntCache.h"
+
+class ActionCache
+{
+ friend class EncodeBuffer;
+ friend class DecodeBuffer;
+
+ public:
+
+ ActionCache();
+ ~ActionCache();
+
+ private:
+
+ IntCache *base_[256];
+
+ unsigned int slot_;
+ unsigned short last_;
+};
+
+#endif /* ActionCache_H */
diff --git a/nxcomp/src/Agent.cpp b/nxcomp/src/Agent.cpp
new file mode 100644
index 000000000..e55620601
--- /dev/null
+++ b/nxcomp/src/Agent.cpp
@@ -0,0 +1,84 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Misc.h"
+#include "Agent.h"
+#include "Proxy.h"
+
+extern Proxy *proxy;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+Agent::Agent(int fd[2])
+{
+ remoteFd_ = fd[0];
+ localFd_ = fd[1];
+
+ transport_ = new AgentTransport(localFd_);
+
+ if (transport_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Agent: PANIC! Can't create the memory-to-memory transport "
+ << "for FD#" << localFd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create the memory-to-memory transport "
+ << "for FD#" << localFd_ << ".\n";
+
+ HandleCleanup();
+ }
+
+ FD_ZERO(&saveRead_);
+ FD_ZERO(&saveWrite_);
+
+ canRead_ = 0;
+
+ #ifdef DEBUG
+ *logofs << "Agent: Created agent object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+Agent::~Agent()
+{
+ delete transport_;
+
+ #ifdef DEBUG
+ *logofs << "Agent: Deleted agent object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/Agent.h b/nxcomp/src/Agent.h
new file mode 100644
index 000000000..3e1a50ae5
--- /dev/null
+++ b/nxcomp/src/Agent.h
@@ -0,0 +1,263 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Agent_H
+#define Agent_H
+
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+
+#include "Misc.h"
+#include "Transport.h"
+#include "Proxy.h"
+
+extern Proxy *proxy;
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+class Agent
+{
+ public:
+
+ //
+ // Must be created by passing the fake descriptor that
+ // will be used for simulating socket communication
+ // betwen the agent and the proxy. I/O will take place
+ // by copying data to the agent's read and write buf-
+ // fers.
+ //
+
+ Agent(int fd[2]);
+
+ ~Agent();
+
+ AgentTransport *getTransport() const
+ {
+ return transport_;
+ }
+
+ void saveReadMask(fd_set *readSet)
+ {
+ saveRead_ = *readSet;
+ }
+
+ void saveWriteMask(fd_set *writeSet)
+ {
+ saveWrite_ = *writeSet;
+ }
+
+ void clearReadMask(fd_set *readSet)
+ {
+ FD_CLR(remoteFd_, readSet);
+ FD_CLR(localFd_, readSet);
+ }
+
+ void clearWriteMask(fd_set *writeSet)
+ {
+ FD_CLR(remoteFd_, writeSet);
+ FD_CLR(localFd_, writeSet);
+ }
+
+ void setLocalRead(fd_set *readSet, int *result)
+ {
+ (*result)++;
+
+ FD_SET(localFd_, readSet);
+ }
+
+ void setRemoteRead(fd_set *readSet, int *result)
+ {
+ (*result)++;
+
+ FD_SET(remoteFd_, readSet);
+ }
+
+ void setRemoteWrite(fd_set *writeSet, int *result)
+ {
+ (*result)++;
+
+ FD_SET(remoteFd_, writeSet);
+ }
+
+ fd_set *getSavedReadMask()
+ {
+ return &saveRead_;
+ }
+
+ fd_set *getSavedWriteMask()
+ {
+ return &saveWrite_;
+ }
+
+ int getRemoteFd() const
+ {
+ return remoteFd_;
+ }
+
+ int getLocalFd() const
+ {
+ return localFd_;
+ }
+
+ int getProxyFd() const
+ {
+ return proxy -> getFd();
+ }
+
+ int isValid() const
+ {
+ return (transport_ != NULL);
+ }
+
+ int localReadable()
+ {
+ return (transport_ -> readable() != 0);
+ }
+
+ //
+ // Check if we can process more data from
+ // the agent descriptor and cache the result
+ // to avoid multiple calls. This must be
+ // always called before querying the other
+ // functions.
+ //
+
+ void saveChannelState()
+ {
+ canRead_ = (proxy != NULL ? proxy -> canRead(localFd_) : 0);
+ }
+
+ int remoteCanRead(const fd_set * const readSet)
+ {
+ // OS X 10.5 requires the second argument to be non-const, so copy readSet.
+ // It's safe though, as FD_ISSET does not operate on it.
+ fd_set readWorkSet = *readSet;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Agent: remoteCanRead() is " <<
+ (FD_ISSET(remoteFd_, &readWorkSet) && transport_ -> dequeuable() != 0)
+ << " with FD_ISSET() " << (int) FD_ISSET(remoteFd_, &readWorkSet)
+ << " and dequeuable " << transport_ -> dequeuable()
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (FD_ISSET(remoteFd_, &readWorkSet) &&
+ transport_ -> dequeuable() != 0);
+ }
+
+ int remoteCanWrite(const fd_set * const writeSet)
+ {
+ // OS X 10.5 requires the second argument to be non-const, so copy writeSet.
+ // It's safe though, as FD_ISSET does not operate on it.
+ fd_set writeWorkSet = *writeSet;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Agent: remoteCanWrite() is " <<
+ (FD_ISSET(remoteFd_, &writeWorkSet) && transport_ ->
+ queuable() != 0 && canRead_ == 1) << " with FD_ISSET() "
+ << (int) FD_ISSET(remoteFd_, &writeWorkSet) << " queueable "
+ << transport_ -> queuable() << " channel can read "
+ << canRead_ << ".\n" << logofs_flush;
+ #endif
+
+ return (FD_ISSET(remoteFd_, &writeWorkSet) &&
+ transport_ -> queuable() != 0 &&
+ canRead_ == 1);
+ }
+
+ int localCanRead()
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Agent: localCanRead() is " <<
+ (transport_ -> readable() != 0 && canRead_ == 1)
+ << " with readable " << transport_ -> readable()
+ << " channel can read " << canRead_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return (transport_ -> readable() != 0 &&
+ canRead_ == 1);
+ }
+
+ int proxyCanRead()
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Agent: proxyCanRead() is " << proxy -> canRead()
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (proxy -> canRead());
+ }
+
+ int proxyCanRead(const fd_set * const readSet)
+ {
+ // OS X 10.5 requires the second argument to be non-const, so copy readSet.
+ // It's safe though, as FD_ISSET does not operate on it.
+ fd_set readWorkSet = *readSet;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Agent: proxyCanRead() is "
+ << ((int) FD_ISSET(proxy -> getFd(), &readWorkSet))
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (FD_ISSET(proxy -> getFd(), &readWorkSet));
+ }
+
+ int enqueueData(const char *data, const int size) const
+ {
+ return transport_ -> enqueue(data, size);
+ }
+
+ int dequeueData(char *data, int size) const
+ {
+ return transport_ -> dequeue(data, size);
+ }
+
+ int dequeuableData() const
+ {
+ return transport_ -> dequeuable();
+ }
+
+ private:
+
+ int remoteFd_;
+ int localFd_;
+
+ fd_set saveRead_;
+ fd_set saveWrite_;
+
+ int canRead_;
+
+ AgentTransport *transport_;
+};
+
+#endif /* Agent_H */
diff --git a/nxcomp/src/Alpha.cpp b/nxcomp/src/Alpha.cpp
new file mode 100644
index 000000000..6157e21e5
--- /dev/null
+++ b/nxcomp/src/Alpha.cpp
@@ -0,0 +1,138 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Misc.h"
+#include "Unpack.h"
+#include "Alpha.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int UnpackAlpha(unsigned char method, unsigned char *src_data, int src_size,
+ unsigned char *dst_data, int dst_size)
+{
+ if (*src_data == 0)
+ {
+ if (dst_size != src_size - 1)
+ {
+ #ifdef TEST
+ *logofs << "UnpackAlpha: PANIC! Invalid destination size "
+ << dst_size << " with source " << src_size
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackAlpha: Expanding " << src_size - 1
+ << " bytes of plain alpha data.\n" << logofs_flush;
+ #endif
+
+ memcpy(dst_data, src_data + 1, src_size - 1);
+
+ return 1;
+ }
+
+ unsigned int check_size = dst_size;
+
+ int result = ZDecompress(&unpackStream, dst_data, &check_size,
+ src_data + 1, src_size - 1);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackAlpha: PANIC! Failure decompressing alpha data. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decompressing alpha data. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+ }
+ else if (check_size != (unsigned int) dst_size)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackAlpha: PANIC! Size mismatch in alpha data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size mismatch in alpha data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackAlpha: Decompressed " << src_size - 1
+ << " bytes to " << dst_size << " bytes of alpha data.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int UnpackAlpha(T_alpha *alpha, unsigned char *dst_data,
+ int dst_size, int big_endian)
+{
+ unsigned int count = dst_size >> 2;
+
+ unsigned int i;
+
+ int shift;
+
+ if (count != alpha -> entries)
+ {
+ #ifdef WARNING
+ *logofs << "UnpackAlpha: WARNING! Not applying the alpha with "
+ << count << " elements needed and " << alpha -> entries
+ << " available.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ shift = (big_endian == 1 ? 0 : 3);
+
+ for (i = 0; i < count; i++)
+ {
+ *(dst_data + shift) = *(alpha -> data + i);
+
+ dst_data += 4;
+ }
+
+ return 1;
+}
diff --git a/nxcomp/src/Alpha.h b/nxcomp/src/Alpha.h
new file mode 100644
index 000000000..ea5068812
--- /dev/null
+++ b/nxcomp/src/Alpha.h
@@ -0,0 +1,35 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Alpha_H
+#define Alpha_H
+
+int UnpackAlpha(unsigned char method, unsigned char *src_data, int src_size,
+ unsigned char *dst_data, int dst_size);
+
+int UnpackAlpha(T_alpha *alpha, unsigned char *dst_data,
+ int dst_size, int big_endian);
+
+#endif /* Aplha_H */
diff --git a/nxcomp/src/Auth.cpp b/nxcomp/src/Auth.cpp
new file mode 100644
index 000000000..d42c0556d
--- /dev/null
+++ b/nxcomp/src/Auth.cpp
@@ -0,0 +1,671 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Auth.h"
+
+#include "Misc.h"
+#include "Control.h"
+#include "Timestamp.h"
+#include "Pipe.h"
+
+#define DEFAULT_STRING_LIMIT 512
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Store the provided cookie as our 'fake' cookie, then
+// read the 'real' cookie from the current X authority
+// file.
+//
+
+Auth::Auth(char *display, char *cookie)
+{
+ display_ = NULL;
+
+ file_ = NULL;
+
+ last_ = nullTimestamp();
+
+ fakeCookie_ = NULL;
+ realCookie_ = NULL;
+
+ fakeData_ = NULL;
+ realData_ = NULL;
+
+ dataSize_ = 0;
+
+ generatedCookie_ = 0;
+
+ if (display == NULL || *display == '\0' || cookie == NULL ||
+ *cookie == '\0' || strlen(cookie) != 32)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Can't create the X authorization data "
+ << "with cookie '" << cookie << "' and display '"
+ << display << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create the X authorization data "
+ << "with cookie '" << cookie << "' and display '"
+ << display << "'.\n";
+
+ return;
+ }
+
+ #ifdef TEST
+ *logofs << "Auth: Creating X authorization data with cookie '"
+ << cookie << "' and display '" << display << "'.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Get a local copy of all parameters.
+ //
+
+ display_ = new char[strlen(display) + 1];
+ file_ = new char[DEFAULT_STRING_LIMIT];
+
+ fakeCookie_ = new char[strlen(cookie) + 1];
+ realCookie_ = new char[DEFAULT_STRING_LIMIT];
+
+ if (display_ == NULL || file_ == NULL ||
+ fakeCookie_ == NULL || realCookie_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Cannot allocate memory for the X "
+ << "authorization data.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot allocate memory for the X "
+ << "authorization data.\n";
+
+ return;
+ }
+
+ strcpy(display_, display);
+
+ *file_ = '\0';
+
+ strcpy(fakeCookie_, cookie);
+
+ *realCookie_ = '\0';
+
+ //
+ // Get the real cookie from the authorization file.
+ //
+
+ updateCookie();
+}
+
+Auth::~Auth()
+{
+ delete [] display_;
+ delete [] file_;
+
+ delete [] fakeCookie_;
+ delete [] realCookie_;
+
+ delete [] fakeData_;
+ delete [] realData_;
+}
+
+//
+// At the present moment the cookie is read only once,
+// at the time the instance is initialized. If the auth
+// file changes along the life of the session, the old
+// cookie will be used. This works with X servers beca-
+// use of an undocumented "feature". See nx-X11.
+//
+
+int Auth::updateCookie()
+{
+ if (isTimestamp(last_) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Auth: Reading the X authorization file "
+ << "with last update at " << strMsTimestamp(last_)
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (getCookie() == 1 && validateCookie() == 1)
+ {
+ //
+ // It should rather be the modification time
+ // the auth file, so we can read it again if
+ // the file is changed.
+ //
+
+ #ifdef TEST
+ *logofs << "Auth: Setting last X authorization file "
+ << "update at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ last_ = getTimestamp();
+
+ return 1;
+ }
+
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Cannot read the cookie from the X "
+ << "authorization file.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot read the cookie from the X "
+ << "authorization file.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Auth: WARNING! Skipping check on the X "
+ << "authorization file.\n" << logofs_flush;
+ #endif
+
+ return 0;
+}
+
+int Auth::getCookie()
+{
+ //
+ // Check the name of the auth file that we are going to use.
+ // It can be either the value of the XAUTHORITY environment
+ // or the default .Xauthority file in the user's home.
+ //
+
+ char *environment;
+
+ environment = getenv("XAUTHORITY");
+
+ if (environment != NULL && *environment != '\0')
+ {
+ strncpy(file_, environment, DEFAULT_STRING_LIMIT - 1);
+ }
+ else
+ {
+ snprintf(file_, DEFAULT_STRING_LIMIT - 1, "%s/.Xauthority",
+ control -> HomePath);
+ }
+
+ *(file_ + DEFAULT_STRING_LIMIT - 1) = '\0';
+
+ #ifdef TEST
+ *logofs << "Auth: Using X authorization file '" << file_
+ << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Use the nxauth command on Windows and the Mac, xauth
+ // on all the other platforms. On Windows we assume that
+ // the nxauth command is located under bin in the client
+ // installation directory. On Mac OS X we assume that the
+ // command is located directly in the client installation
+ // directory, to make bundle shipping easier. On all the
+ // other platforms we use the default xauth command that
+ // is in our path.
+ //
+
+ char command[DEFAULT_STRING_LIMIT];
+
+ #if defined(__CYGWIN32__)
+
+ snprintf(command, DEFAULT_STRING_LIMIT - 1,
+ "%s/bin/nxauth", control -> SystemPath);
+
+ *(command + DEFAULT_STRING_LIMIT - 1) = '\0';
+
+ #elif defined(__APPLE__)
+
+ snprintf(command, DEFAULT_STRING_LIMIT - 1,
+ "%s/nxauth", control -> SystemPath);
+
+ *(command + DEFAULT_STRING_LIMIT - 1) = '\0';
+
+ #else
+
+ strcpy(command, "xauth");
+
+ #endif
+
+ #ifdef TEST
+ *logofs << "Auth: Using X auth command '" << command
+ << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // The SSH code forces using the unix:n port when passing localhost:n.
+ // This is probably because localhost:n can fail to return a valid
+ // entry on machines where the hostname for localhost doesn't match
+ // exactly the 'localhost' string. For example, on a freshly installed
+ // Fedora Core 3 I get a 'localhost.localdomain/unix:0' entry. Query-
+ // ing 'xauth list localhost:0' results in an empty result, while the
+ // query 'xauth list unix:0' works as expected. Note anyway that if
+ // the cookie for the TCP connection on 'localhost' is set to a dif-
+ // ferent cookie than the one for the Unix connections, both SSH and
+ // NX will match the wrong cookie and session will fail.
+ //
+
+ char line[DEFAULT_STRING_LIMIT];
+
+ if (strncmp(display_, "localhost:", 10) == 0)
+ {
+ snprintf(line, DEFAULT_STRING_LIMIT, "unix:%s", display_ + 10);
+ }
+ else
+ {
+ snprintf(line, DEFAULT_STRING_LIMIT, "%.200s", display_);
+ }
+
+ const char *parameters[256];
+
+ parameters[0] = command;
+ parameters[1] = command;
+ parameters[2] = "-f";
+ parameters[3] = file_;
+ parameters[4] = "list";
+ parameters[5] = line;
+ parameters[6] = NULL;
+
+ #ifdef TEST
+ *logofs << "Auth: Executing command ";
+
+ for (int i = 0; i < 256 && parameters[i] != NULL; i++)
+ {
+ *logofs << "[" << parameters[i] << "]";
+ }
+
+ *logofs << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Use the popen() function to read the result
+ // of the command. We would better use our own
+ // implementation.
+ //
+
+ FILE *data = Popen((char *const *) parameters, "r");
+
+ int result = -1;
+
+ if (data == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Failed to execute the X auth command.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to execute the X auth command.\n";
+
+ goto AuthGetCookieResult;
+ }
+
+ if (fgets(line, DEFAULT_STRING_LIMIT, data) == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Auth: WARNING! Failed to read data from the X "
+ << "auth command.\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ cerr << "Warning" << ": Failed to read data from the X "
+ << "auth command.\n";
+ #endif
+
+ #ifdef PANIC
+ *logofs << "Auth: WARNING! Generating a fake cookie for "
+ << "X authentication.\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ cerr << "Warning" << ": Generating a fake cookie for "
+ << "X authentication.\n";
+ #endif
+
+ generateCookie(realCookie_);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Auth: Checking cookie in string '" << line
+ << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Skip the hostname in the authority entry
+ // just in case it includes some white spaces.
+ //
+
+ char *cookie = NULL;
+
+ cookie = index(line, ':');
+
+ if (cookie == NULL)
+ {
+ cookie = line;
+ }
+
+ if (sscanf(cookie, "%*s %*s %511s", realCookie_) != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Failed to identify the cookie "
+ << "in string '" << line << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to identify the cookie "
+ << "in string '" << line << "'.\n";
+
+ goto AuthGetCookieResult;
+ }
+
+ #ifdef TEST
+ *logofs << "Auth: Got cookie '" << realCookie_
+ << "' from file '" << file_ << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ result = 1;
+
+AuthGetCookieResult:
+
+ if (data != NULL)
+ {
+ Pclose(data);
+ }
+
+ return result;
+}
+
+int Auth::validateCookie()
+{
+ unsigned int length = strlen(realCookie_);
+
+ if (length > DEFAULT_STRING_LIMIT / 2 - 1 ||
+ strlen(fakeCookie_) != length)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Size mismatch between cookies '"
+ << realCookie_ << "' and '" << fakeCookie_ << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size mismatch between cookies '"
+ << realCookie_ << "' and '" << fakeCookie_ << "'.\n";
+
+ goto AuthValidateCookieError;
+ }
+
+ //
+ // The length of the resulting data will be
+ // half the size of the Hex cookie.
+ //
+
+ length = length / 2;
+
+ fakeData_ = new char[length];
+ realData_ = new char[length];
+
+ if (fakeData_ == NULL || realData_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Cannot allocate memory for the binary X "
+ << "authorization data.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot allocate memory for the binary X "
+ << "authorization data.\n";
+
+ goto AuthValidateCookieError;
+ }
+
+ //
+ // Translate the real cookie from Hex data
+ // to its binary representation.
+ //
+
+ unsigned int value;
+
+ for (unsigned int i = 0; i < length; i++)
+ {
+ if (sscanf(realCookie_ + 2 * i, "%2x", &value) != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Bad X authorization data in real "
+ << "cookie '" << realCookie_ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad X authorization data in real cookie '"
+ << realCookie_ << "'.\n";
+
+ goto AuthValidateCookieError;
+ }
+
+ realData_[i] = value;
+
+ if (sscanf(fakeCookie_ + 2 * i, "%2x", &value) != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Bad X authorization data in fake "
+ << "cookie '" << fakeCookie_ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad X authorization data in fake cookie '"
+ << fakeCookie_ << "'.\n";
+
+ goto AuthValidateCookieError;
+ }
+
+ fakeData_[i] = value;
+ }
+
+ dataSize_ = length;
+
+ #ifdef TEST
+ *logofs << "Auth: Validated real cookie '"
+ << realCookie_ << "' and fake cookie '" << fakeCookie_
+ << "' with data with size " << dataSize_ << ".\n"
+ << logofs_flush;
+
+ *logofs << "Auth: Ready to accept incoming connections.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+
+AuthValidateCookieError:
+
+ delete [] fakeData_;
+ delete [] realData_;
+
+ fakeData_ = NULL;
+ realData_ = NULL;
+
+ dataSize_ = 0;
+
+ return -1;
+}
+
+int Auth::checkCookie(unsigned char *buffer)
+{
+ if (isValid() != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Auth: PANIC! Attempt to check the X cookie with "
+ << "invalid authorization data.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Attempt to check the X cookie with "
+ << "invalid authorization data.\n";
+
+ return -1;
+ }
+
+ const char *protoName = "MIT-MAGIC-COOKIE-1";
+ int protoSize = strlen(protoName);
+
+ int matchedProtoSize;
+ int matchedDataSize;
+
+ if (buffer[0] == 0x42)
+ {
+ //
+ // Byte order is MSB first.
+ //
+
+ matchedProtoSize = 256 * buffer[6] + buffer[7];
+ matchedDataSize = 256 * buffer[8] + buffer[9];
+ }
+ else if (buffer[0] == 0x6c)
+ {
+ //
+ // Byte order is LSB first.
+ //
+
+ matchedProtoSize = buffer[6] + 256 * buffer[7];
+ matchedDataSize = buffer[8] + 256 * buffer[9];
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Auth: WARNING! Bad X connection data in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Bad X connection data in the buffer.\n";
+
+ return -1;
+ }
+
+ //
+ // Check if both the authentication protocol
+ // and the fake cookie match our data.
+ //
+
+ int protoOffset = 12;
+
+ #ifdef TEST
+ *logofs << "Auth: Received a protocol size of "
+ << matchedProtoSize << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ if (matchedProtoSize != protoSize ||
+ memcmp(buffer + protoOffset, protoName, protoSize) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Auth: WARNING! Protocol mismatch or no X "
+ << "authentication data.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Protocol mismatch or no X "
+ << "authentication data.\n";
+
+ return -1;
+ }
+
+ int dataOffset = protoOffset + ((matchedProtoSize + 3) & ~3);
+
+ #ifdef TEST
+ *logofs << "Auth: Received a data size of "
+ << matchedDataSize << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ if (matchedDataSize != dataSize_ ||
+ memcmp(buffer + dataOffset, fakeData_, dataSize_) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Auth: WARNING! Cookie mismatch in the X "
+ << "authentication data.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Cookie mismatch in the X "
+ << "authentication data.\n";
+
+ return -1;
+ }
+
+ //
+ // Everything is OK. Replace the fake data.
+ //
+
+ #ifdef TEST
+ *logofs << "Auth: Replacing fake X authentication data "
+ << "with the real data.\n" << logofs_flush;
+ #endif
+
+ memcpy(buffer + dataOffset, realData_, dataSize_);
+
+ return 1;
+}
+
+void Auth::generateCookie(char *cookie)
+{
+ //
+ // Code is from the SSH implementation, except that
+ // we use a much weaker random number generator.
+ // This is not critical, anyway, as this is just a
+ // fake cookie. The X server doesn't have a cookie
+ // for the display, so it will ignore the value we
+ // feed to it.
+ //
+
+ T_timestamp timer = getTimestamp();
+
+ srand((unsigned int) timer.tv_usec);
+
+ unsigned int data = rand();
+
+ for (int i = 0; i < 16; i++)
+ {
+ if (i % 4 == 0)
+ {
+ data = rand();
+ }
+
+ snprintf(cookie + 2 * i, 3, "%02x", data & 0xff);
+
+ data >>= 8;
+ }
+
+ generatedCookie_ = 1;
+
+ #ifdef TEST
+ *logofs << "Auth: Generated X cookie string '"
+ << cookie << "'.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/Auth.h b/nxcomp/src/Auth.h
new file mode 100644
index 000000000..d51d9a26f
--- /dev/null
+++ b/nxcomp/src/Auth.h
@@ -0,0 +1,127 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Auth_H
+#define Auth_H
+
+#include "Timestamp.h"
+
+//
+// Handle the forwarding of authorization credentials
+// to the X server by replacing the fake cookie with
+// the real cookie as it is read from the auth file.
+// At the moment only the MIT-MAGIC-COOKIE-1 cookies
+// are recognized. The implementation is based on the
+// corresponding code found in the SSH client.
+//
+
+class Auth
+{
+ public:
+
+ //
+ // Must be created by passing the fake cookie that
+ // will be forwarded by the remote end and with the
+ // real X display that is going to be used for the
+ // session.
+ //
+
+ Auth(char *display, char *cookie);
+
+ ~Auth();
+
+ int isValid()
+ {
+ return (isTimestamp(last_) == 1 && fakeCookie_ != NULL &&
+ *fakeCookie_ != '\0' && realCookie_ != NULL &&
+ *realCookie_ != '\0' && fakeData_ != NULL &&
+ realData_ != NULL && dataSize_ != 0);
+ }
+
+ int isFake() const
+ {
+ return generatedCookie_;
+ }
+
+ //
+ // Method called in the channel class to find if the
+ // provided cookie matches the fake one. If the data
+ // matches, the fake cookie is replaced with the real
+ // one.
+ //
+
+ int checkCookie(unsigned char *buffer);
+
+ protected:
+
+ //
+ // Update the real cookie for the display. If called
+ // a further time, check if the auth file is changed
+ // and get the new cookie.
+ //
+
+ int updateCookie();
+
+ //
+ // Find out which authorization file is to be used
+ // and query the cookie for the current display.
+ //
+
+ int getCookie();
+
+ //
+ // Extract the binary data from the cookies so that
+ // data can be directly compared at the time it is
+ // taken from the X request.
+ //
+
+ int validateCookie();
+
+ //
+ // Generate a fake random cookie and copy it to the
+ // provided string.
+ //
+
+ void generateCookie(char *cookie);
+
+ private:
+
+ char *display_;
+ char *file_;
+
+ T_timestamp last_;
+
+ char *fakeCookie_;
+ char *realCookie_;
+
+ char *fakeData_;
+ char *realData_;
+
+ int dataSize_;
+
+ int generatedCookie_;
+};
+
+#endif /* Auth_H */
diff --git a/nxcomp/src/Bitmap.cpp b/nxcomp/src/Bitmap.cpp
new file mode 100644
index 000000000..c89df53e3
--- /dev/null
+++ b/nxcomp/src/Bitmap.cpp
@@ -0,0 +1,118 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Misc.h"
+#include "Bitmap.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int UnpackBitmap(T_geometry *geometry, unsigned char method, unsigned char *src_data,
+ int src_size, int dst_bpp, int dst_width, int dst_height,
+ unsigned char *dst_data, int dst_size)
+{
+ if (dst_bpp != 32)
+ {
+ #ifdef TEST
+ *logofs << "UnpackBitmap: Nothing to do with "
+ << "image of " << dst_bpp << " bits per plane "
+ << "and size " << src_size << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (src_size != dst_size)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackBitmap: PANIC! Size mismatch with "
+ << src_size << " bytes in the source and "
+ << dst_size << " in the destination.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ memcpy(dst_data, src_data, src_size);
+
+ return 1;
+ }
+ else if (src_size != dst_width * dst_height * 3 ||
+ dst_size != dst_width * dst_height * 4)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackBitmap: PANIC! Size mismatch with "
+ << src_size << " bytes in the source and "
+ << dst_size << " in the destination.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ /*
+ * Insert the 4th byte in the bitmap.
+ */
+
+ unsigned char *next_src = src_data;
+ unsigned char *next_dst = dst_data;
+
+ if (geometry -> image_byte_order == LSBFirst)
+ {
+ while (next_src < src_data + src_size)
+ {
+ *next_dst++ = *next_src++;
+ *next_dst++ = *next_src++;
+ *next_dst++ = *next_src++;
+
+ next_dst++;
+ }
+ }
+ else
+ {
+ while (next_src < src_data + src_size)
+ {
+ next_dst++;
+
+ *next_dst++ = *next_src++;
+ *next_dst++ = *next_src++;
+ *next_dst++ = *next_src++;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackBitmap: Unpacked " << src_size
+ << " bytes to a buffer of " << dst_size
+ << " with " << dst_bpp << " bits per plane.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/Bitmap.h b/nxcomp/src/Bitmap.h
new file mode 100644
index 000000000..8143e3125
--- /dev/null
+++ b/nxcomp/src/Bitmap.h
@@ -0,0 +1,36 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Bitmap_H
+#define Bitmap_H
+
+#include "Unpack.h"
+
+int UnpackBitmap(T_geometry *geometry, unsigned char method,
+ unsigned char *src_data, int src_size, int dst_bpp,
+ int dst_width, int dst_height, unsigned char *dst_data,
+ int dst_size);
+
+#endif /* Bitmap_H */
diff --git a/nxcomp/src/BlockCache.cpp b/nxcomp/src/BlockCache.cpp
new file mode 100644
index 000000000..161145e3d
--- /dev/null
+++ b/nxcomp/src/BlockCache.cpp
@@ -0,0 +1,81 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include "BlockCache.h"
+
+
+int BlockCache::compare(unsigned int size, const unsigned char *data,
+ int overwrite)
+{
+ int match = 0;
+ if (size == size_)
+ {
+ match = 1;
+ for (unsigned int i = 0; i < size_; i++)
+ if (data[i] != buffer_[i])
+ {
+ match = 0;
+ break;
+ }
+ }
+ if (!match && overwrite)
+ set(size, data);
+ return match;
+}
+
+
+void BlockCache::set(unsigned int size, const unsigned char *data)
+{
+ if (size_ < size)
+ {
+ delete[]buffer_;
+ buffer_ = new unsigned char[size];
+ }
+ size_ = size;
+ memcpy(buffer_, data, size);
+ checksum_ = checksum(size, data);
+}
+
+
+unsigned int BlockCache::checksum(unsigned int size, const unsigned char *data)
+{
+ unsigned int sum = 0;
+ unsigned int shift = 0;
+ const unsigned char *next = data;
+ for (unsigned int i = 0; i < size; i++)
+ {
+ unsigned int value = (unsigned int) *next++;
+ sum += (value << shift);
+ shift++;
+ if (shift == 8)
+ shift = 0;
+ }
+ return sum;
+}
diff --git a/nxcomp/src/BlockCache.h b/nxcomp/src/BlockCache.h
new file mode 100644
index 000000000..48e586966
--- /dev/null
+++ b/nxcomp/src/BlockCache.h
@@ -0,0 +1,67 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef BlockCache_H
+#define BlockCache_H
+
+
+// Cache to hold an arbitrary-length block of bytes
+
+class BlockCache
+{
+ public:
+ BlockCache():buffer_(0), size_(0), checksum_(0)
+ {
+ }
+ ~BlockCache()
+ {
+ delete[]buffer_;
+ }
+ int compare(unsigned int size, const unsigned char *data,
+ int overwrite = 1);
+ void set(unsigned int size, const unsigned char *data);
+
+ unsigned int getLength() const
+ {
+ return size_;
+ }
+ unsigned int getChecksum() const
+ {
+ return checksum_;
+ }
+ const unsigned char *getData() const
+ {
+ return buffer_;
+ }
+
+ static unsigned int checksum(unsigned int size, const unsigned char *data);
+
+private:
+ unsigned char *buffer_;
+ unsigned int size_;
+ unsigned int checksum_;
+};
+
+#endif /* BlockCache_H */
diff --git a/nxcomp/src/BlockCacheSet.cpp b/nxcomp/src/BlockCacheSet.cpp
new file mode 100644
index 000000000..1dd6361dc
--- /dev/null
+++ b/nxcomp/src/BlockCacheSet.cpp
@@ -0,0 +1,147 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Misc.h"
+#include "BlockCacheSet.h"
+
+
+BlockCacheSet::BlockCacheSet(unsigned int numCaches):
+ caches_(new BlockCache *[numCaches]), size_(numCaches),
+ length_(0)
+{
+ for (unsigned int i = 0; i < numCaches; i++)
+ caches_[i] = new BlockCache();
+}
+
+
+BlockCacheSet::~BlockCacheSet()
+{
+ //
+ // TODO: There is still a strange segfault occurring
+ // at random time under Cygwin, when proxy is being
+ // shutdown. Problem appeared just after upgrading
+ // to the latest version of the Cygwin DLL. A stack
+ // trace, obtained at the last minute, reveals that
+ // failure happens in this destructor.
+ //
+
+ #ifndef __CYGWIN32__
+
+ for (unsigned int i = 0; i < size_; i++)
+ delete caches_[i];
+ delete[]caches_;
+
+ #endif /* ifdef __CYGWIN32__ */
+}
+
+
+int
+BlockCacheSet::lookup(unsigned int dataLength, const unsigned char *data,
+ unsigned int &index)
+{
+ unsigned int checksum = BlockCache::checksum(dataLength, data);
+ for (unsigned int i = 0; i < length_; i++)
+ if ((caches_[i]->getChecksum() == checksum) &&
+ (caches_[i]->compare(dataLength, data, 0)))
+ {
+ // match
+ index = i;
+ if (i)
+ {
+ BlockCache *save = caches_[i];
+ unsigned int target = (i >> 1);
+ do
+ {
+ caches_[i] = caches_[i - 1];
+ i--;
+ }
+ while (i > target);
+ caches_[target] = save;
+ }
+ return 1;
+ }
+ // no match
+ unsigned int insertionPoint = (length_ >> 1);
+ unsigned int start;
+ if (length_ >= size_)
+ start = size_ - 1;
+ else
+ {
+ start = length_;
+ length_++;
+ }
+ BlockCache *save = caches_[start];
+ for (unsigned int k = start; k > insertionPoint; k--)
+ caches_[k] = caches_[k - 1];
+ caches_[insertionPoint] = save;
+ save->set(dataLength, data);
+ return 0;
+}
+
+
+void
+BlockCacheSet::get(unsigned index, unsigned int &size,
+ const unsigned char *&data)
+{
+ size = caches_[index]->getLength();
+ data = caches_[index]->getData();
+ if (index)
+ {
+ BlockCache *save = caches_[index];
+ unsigned int target = (index >> 1);
+ do
+ {
+ caches_[index] = caches_[index - 1];
+ index--;
+ }
+ while (index > target);
+ caches_[target] = save;
+ }
+}
+
+
+
+void
+BlockCacheSet::set(unsigned int dataLength, const unsigned char *data)
+{
+ unsigned int insertionPoint = (length_ >> 1);
+ unsigned int start;
+ if (length_ >= size_)
+ start = size_ - 1;
+ else
+ {
+ start = length_;
+ length_++;
+ }
+ BlockCache *save = caches_[start];
+ for (unsigned int k = start; k > insertionPoint; k--)
+ caches_[k] = caches_[k - 1];
+ caches_[insertionPoint] = save;
+ save->set(dataLength, data);
+}
diff --git a/nxcomp/src/BlockCacheSet.h b/nxcomp/src/BlockCacheSet.h
new file mode 100644
index 000000000..97273b0e0
--- /dev/null
+++ b/nxcomp/src/BlockCacheSet.h
@@ -0,0 +1,49 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef BlockCacheSet_H
+#define BlockCacheSet_H
+
+#include "BlockCache.h"
+
+
+class BlockCacheSet
+{
+ public:
+ BlockCacheSet(unsigned int numCaches);
+ ~BlockCacheSet();
+
+ int lookup(unsigned int size, const unsigned char *data,
+ unsigned int &index);
+ void get(unsigned int index, unsigned int &size, const unsigned char *&data);
+ void set(unsigned int size, const unsigned char *data);
+
+ private:
+ BlockCache ** caches_;
+ unsigned int size_;
+ unsigned int length_;
+};
+
+#endif /* BlockCacheSet_H */
diff --git a/nxcomp/src/ChangeGC.cpp b/nxcomp/src/ChangeGC.cpp
new file mode 100644
index 000000000..e92178f27
--- /dev/null
+++ b/nxcomp/src/ChangeGC.cpp
@@ -0,0 +1,184 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ChangeGC.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ChangeGCStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ChangeGCMessage *changeGC = (ChangeGCMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ changeGC -> gcontext = GetULONG(buffer + 4, bigEndian);
+ changeGC -> value_mask = GetULONG(buffer + 8, bigEndian);
+
+ //
+ // Clear the unused bytes carried in the
+ // payload to increase the effectiveness
+ // of the caching algorithm.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Removing unused bytes from the "
+ << "data payload.\n" << logofs_flush;
+ #endif
+
+ changeGC -> value_mask &= (1 << 23) - 1;
+
+ unsigned int mask = 0x1;
+ unsigned char *source = (unsigned char *) buffer + CHANGEGC_DATA_OFFSET;
+ unsigned long value = 0;
+
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (changeGC -> value_mask & mask)
+ {
+ value = GetULONG(source, bigEndian);
+
+ value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i]));
+
+ PutULONG(value, source, bigEndian);
+
+ source += 4;
+ }
+
+ mask <<= 1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ChangeGCStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ChangeGCMessage *changeGC = (ChangeGCMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(changeGC -> gcontext, buffer + 4, bigEndian);
+ PutULONG(changeGC -> value_mask, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ChangeGCStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ChangeGCMessage *changeGC = (ChangeGCMessage *) message;
+
+ *logofs << name() << ": Identity gcontext " << changeGC -> gcontext
+ << ", mask " << changeGC -> value_mask << ", size "
+ << changeGC -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void ChangeGCStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+/*
+ md5_append(md5_state_, buffer + 4, 8);
+*/
+ md5_append(md5_state_, buffer + 8, 4);
+}
+
+void ChangeGCStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ ChangeGCMessage *changeGC = (ChangeGCMessage *) message;
+ ChangeGCMessage *cachedChangeGC = (ChangeGCMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << changeGC -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(changeGC -> gcontext, clientCache -> gcCache);
+
+ cachedChangeGC -> gcontext = changeGC -> gcontext;
+}
+
+void ChangeGCStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ ChangeGCMessage *changeGC = (ChangeGCMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ changeGC -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << changeGC -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/ChangeGC.h b/nxcomp/src/ChangeGC.h
new file mode 100644
index 000000000..9cac90e66
--- /dev/null
+++ b/nxcomp/src/ChangeGC.h
@@ -0,0 +1,185 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ChangeGC_H
+#define ChangeGC_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CHANGEGC_ENABLE_CACHE 1
+#define CHANGEGC_ENABLE_DATA 0
+#define CHANGEGC_ENABLE_SPLIT 0
+#define CHANGEGC_ENABLE_COMPRESS 0
+
+#define CHANGEGC_DATA_LIMIT 144
+#define CHANGEGC_DATA_OFFSET 12
+
+#define CHANGEGC_CACHE_SLOTS 3000
+#define CHANGEGC_CACHE_THRESHOLD 3
+#define CHANGEGC_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ChangeGCMessage : public Message
+{
+ friend class ChangeGCStore;
+
+ public:
+
+ ChangeGCMessage()
+ {
+ }
+
+ ~ChangeGCMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int gcontext;
+ unsigned int value_mask;
+};
+
+class ChangeGCStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ChangeGCStore() : MessageStore()
+ {
+ enableCache = CHANGEGC_ENABLE_CACHE;
+ enableData = CHANGEGC_ENABLE_DATA;
+ enableSplit = CHANGEGC_ENABLE_SPLIT;
+ enableCompress = CHANGEGC_ENABLE_COMPRESS;
+
+ dataLimit = CHANGEGC_DATA_LIMIT;
+ dataOffset = CHANGEGC_DATA_OFFSET;
+
+ cacheSlots = CHANGEGC_CACHE_SLOTS;
+ cacheThreshold = CHANGEGC_CACHE_THRESHOLD;
+ cacheLowerThreshold = CHANGEGC_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ChangeGCStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ChangeGC";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ChangeGC;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ChangeGCMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ChangeGCMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ChangeGCMessage((const ChangeGCMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ChangeGCMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+};
+
+#endif /* ChangeGC_H */
diff --git a/nxcomp/src/ChangeProperty.cpp b/nxcomp/src/ChangeProperty.cpp
new file mode 100644
index 000000000..74814c2ea
--- /dev/null
+++ b/nxcomp/src/ChangeProperty.cpp
@@ -0,0 +1,191 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ChangeProperty.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ChangePropertyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message;
+
+ changeProperty -> mode = *(buffer + 1);
+ changeProperty -> format = *(buffer + 16);
+
+ changeProperty -> window = GetULONG(buffer + 4, bigEndian);
+ changeProperty -> property = GetULONG(buffer + 8, bigEndian);
+ changeProperty -> type = GetULONG(buffer + 12, bigEndian);
+ changeProperty -> length = GetULONG(buffer + 20, bigEndian);
+
+ //
+ // Cleanup the padding bytes.
+ //
+
+ unsigned int uiFormat;
+ unsigned int uiLengthInBytes;
+
+ if ((int) size > CHANGEPROPERTY_DATA_OFFSET)
+ {
+ uiFormat = *(buffer + 16);
+
+ uiLengthInBytes = changeProperty -> length;
+
+ #ifdef DEBUG
+ *logofs << name() << ": length " << uiLengthInBytes
+ << ", format " << uiFormat << ", size "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ if (uiFormat == 16)
+ {
+ uiLengthInBytes <<= 1;
+ }
+ else if (uiFormat == 32)
+ {
+ uiLengthInBytes <<= 2;
+ }
+
+ unsigned char *end = ((unsigned char *) buffer) + size;
+ unsigned char *pad = ((unsigned char *) buffer) + CHANGEPROPERTY_DATA_OFFSET + uiLengthInBytes;
+
+ CleanData((unsigned char *) pad, end - pad);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ChangePropertyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message;
+
+ *(buffer + 1) = changeProperty -> mode;
+ *(buffer + 16) = changeProperty -> format;
+
+ PutULONG(changeProperty -> window, buffer + 4, bigEndian);
+ PutULONG(changeProperty -> property, buffer + 8, bigEndian);
+ PutULONG(changeProperty -> type, buffer + 12, bigEndian);
+ PutULONG(changeProperty -> length, buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ChangePropertyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message;
+
+ *logofs << name() << ": Identity mode " << (unsigned int) changeProperty -> mode << ", format "
+ << (unsigned int) changeProperty -> format << ", window " << changeProperty -> window
+ << ", property " << changeProperty -> property << ", type " << changeProperty -> type
+ << ", length " << changeProperty -> length << ", size " << changeProperty -> size_
+ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void ChangePropertyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 16, 1);
+
+ md5_append(md5_state_, buffer + 8, 4);
+ md5_append(md5_state_, buffer + 12, 4);
+ md5_append(md5_state_, buffer + 20, 4);
+}
+
+void ChangePropertyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message;
+ ChangePropertyMessage *cachedChangeProperty = (ChangePropertyMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << changeProperty -> window
+ << " as window field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(changeProperty -> window, clientCache -> windowCache);
+
+ cachedChangeProperty -> window = changeProperty -> window;
+}
+
+void ChangePropertyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ ChangePropertyMessage *changeProperty = (ChangePropertyMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> windowCache);
+
+ changeProperty -> window = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << changeProperty -> window
+ << " as window field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/src/ChangeProperty.h b/nxcomp/src/ChangeProperty.h
new file mode 100644
index 000000000..c06ce10fc
--- /dev/null
+++ b/nxcomp/src/ChangeProperty.h
@@ -0,0 +1,189 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ChangeProperty_H
+#define ChangeProperty_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CHANGEPROPERTY_ENABLE_CACHE 1
+#define CHANGEPROPERTY_ENABLE_DATA 0
+#define CHANGEPROPERTY_ENABLE_SPLIT 0
+#define CHANGEPROPERTY_ENABLE_COMPRESS 0
+
+#define CHANGEPROPERTY_DATA_LIMIT 28688
+#define CHANGEPROPERTY_DATA_OFFSET 24
+
+#define CHANGEPROPERTY_CACHE_SLOTS 2000
+#define CHANGEPROPERTY_CACHE_THRESHOLD 2
+#define CHANGEPROPERTY_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ChangePropertyMessage : public Message
+{
+ friend class ChangePropertyStore;
+
+ public:
+
+ ChangePropertyMessage()
+ {
+ }
+
+ ~ChangePropertyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char mode;
+ unsigned char format;
+ unsigned int window;
+ unsigned int property;
+ unsigned int type;
+ unsigned int length;
+};
+
+class ChangePropertyStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ChangePropertyStore() : MessageStore()
+ {
+ enableCache = CHANGEPROPERTY_ENABLE_CACHE;
+ enableData = CHANGEPROPERTY_ENABLE_DATA;
+ enableSplit = CHANGEPROPERTY_ENABLE_SPLIT;
+ enableCompress = CHANGEPROPERTY_ENABLE_COMPRESS;
+
+ dataLimit = CHANGEPROPERTY_DATA_LIMIT;
+ dataOffset = CHANGEPROPERTY_DATA_OFFSET;
+
+ cacheSlots = CHANGEPROPERTY_CACHE_SLOTS;
+ cacheThreshold = CHANGEPROPERTY_CACHE_THRESHOLD;
+ cacheLowerThreshold = CHANGEPROPERTY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ChangePropertyStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ChangeProperty";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ChangeProperty;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ChangePropertyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ChangePropertyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ChangePropertyMessage((const ChangePropertyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ChangePropertyMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ChangeProperty_H */
diff --git a/nxcomp/src/Channel.cpp b/nxcomp/src/Channel.cpp
new file mode 100644
index 000000000..24a422e37
--- /dev/null
+++ b/nxcomp/src/Channel.cpp
@@ -0,0 +1,2039 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Channel.h"
+
+#include "List.h"
+#include "Proxy.h"
+#include "Statistics.h"
+
+#include "StaticCompressor.h"
+
+#include "NXalert.h"
+
+extern Proxy *proxy;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Log the operations related to splits.
+//
+
+#undef SPLIT
+
+#undef COUNT
+
+#define COUNT_MAJOR_OPCODE 154
+
+#undef MONITOR
+
+#define MONITOR_MAJOR_OPCODE 154
+#define MONITOR_MINOR_OPCODE 23
+
+#undef CLEAR
+
+#define CLEAR_MAJOR_OPCODE 154
+#define CLEAR_MINOR_OPCODE 23
+
+//
+// Define this to know how many messages
+// are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// Set to the descriptor of the first X
+// channel successfully connected.
+//
+
+int Channel::firstClient_ = -1;
+
+//
+// Port used for font server connections.
+//
+
+int Channel::fontPort_ = -1;
+
+//
+// This is used for reference count.
+//
+
+#ifdef REFERENCES
+
+int Channel::references_ = 0;
+
+#endif
+
+Channel::Channel(Transport *transport, StaticCompressor *compressor)
+
+ : transport_(transport), compressor_(compressor)
+{
+ fd_ = transport_ -> fd();
+
+ finish_ = 0;
+ closing_ = 0;
+ drop_ = 0;
+ congestion_ = 0;
+ priority_ = 0;
+
+ alert_ = 0;
+
+ firstRequest_ = 1;
+ firstReply_ = 1;
+
+ enableCache_ = 1;
+ enableSplit_ = 1;
+ enableSave_ = 1;
+ enableLoad_ = 1;
+
+ //
+ // Must be set by proxy.
+ //
+
+ opcodeStore_ = NULL;
+
+ clientStore_ = NULL;
+ serverStore_ = NULL;
+
+ clientCache_ = NULL;
+ serverCache_ = NULL;
+
+ #ifdef REFERENCES
+ *logofs << "Channel: Created new Channel at "
+ << this << " out of " << ++references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+Channel::~Channel()
+{
+ if (firstClient_ == fd_)
+ {
+ firstClient_ = -1;
+ }
+
+ #ifdef REFERENCES
+ *logofs << "Channel: Deleted Channel at "
+ << this << " out of " << --references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+int Channel::handleEncode(EncodeBuffer &encodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #ifdef MONITOR
+
+ static float totalMessages = 0;
+ static float totalBits = 0;
+
+ int bits;
+ int diff;
+
+ bits = encodeBuffer.getBits();
+
+ #endif
+
+ //
+ // Check if message can be differentially
+ // encoded using a similar message in the
+ // message store.
+ //
+
+ #ifdef COUNT
+
+ if (*(buffer) == COUNT_MAJOR_OPCODE)
+ {
+ if (*(buffer) < 128)
+ {
+ *logofs << "handleEncode: Handling OPCODE#" << (unsigned int) *(buffer)
+ << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleEncode: Handling OPCODE#" << (unsigned int) *(buffer)
+ << " MINOR#" << (unsigned int) *(buffer + 1) << ".\n"
+ << logofs_flush;
+ }
+ }
+
+ #endif
+
+ #ifdef CLEAR
+
+ if (*(buffer) == CLEAR_MAJOR_OPCODE &&
+ (CLEAR_MINOR_OPCODE == -1 || *(buffer + 1) == CLEAR_MINOR_OPCODE))
+ {
+ *((unsigned char *) buffer) = X_NoOperation;
+
+ *((unsigned char *) buffer + 1) = '\0';
+
+ CleanData((unsigned char *) buffer + 4, size - 4);
+ }
+
+ #endif
+
+ if (handleEncodeCached(encodeBuffer, channelCache,
+ store, buffer, size) == 1)
+ {
+ #ifdef MONITOR
+
+ diff = encodeBuffer.getBits() - bits;
+
+ if (*(buffer) == MONITOR_MAJOR_OPCODE &&
+ (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE))
+ {
+ totalMessages++;
+
+ totalBits += diff;
+
+ *logofs << "handleEncode: Handled cached OPCODE#" << (unsigned int) *(buffer)
+ << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size
+ << " bytes in, " << diff << " bits (" << ((float) diff) / 8
+ << " bytes) out. Average " << totalBits / totalMessages
+ << "/1.\n" << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Let the channel update the split store
+ // and notify the agent in the case of a
+ // cache hit.
+ //
+
+ if (store -> enableSplit)
+ {
+ handleSplit(encodeBuffer, store, store -> lastAction,
+ store -> lastHit, opcode, buffer, size);
+ }
+
+ return 1;
+ }
+
+ //
+ // A similar message could not be found in
+ // cache or message must be discarded. Must
+ // transmit the message using the field by
+ // field differential encoding.
+ //
+
+ handleEncodeIdentity(encodeBuffer, channelCache,
+ store, buffer, size, bigEndian_);
+
+ //
+ // Check if message has a distinct data part.
+ //
+
+ if (store -> enableData)
+ {
+ //
+ // If message split was requested by agent then send data
+ // out-of-band, dividing it in small chunks. Until message
+ // is completely transferred, keep in the split store a
+ // dummy version of the message, with data replaced with a
+ // pattern.
+ //
+ // While data is being transferred, agent should have put
+ // the resource (for example its client) asleep. It can
+ // happen, though, that a different client would reference
+ // the same message. We cannot issue a cache hit for images
+ // being split (such images are put in store in 'incomplete'
+ // state), so we need to handle this case.
+ //
+
+ if (store -> enableSplit == 1)
+ {
+ //
+ // Let the channel decide what to do with the
+ // message. If the split can't take place be-
+ // cause the split store is full, the channel
+ // will tell the remote side that the data is
+ // going to follow.
+ //
+
+ if (handleSplit(encodeBuffer, store, store -> lastAction,
+ (store -> lastAction == IS_ADDED ? store -> lastAdded : 0),
+ opcode, buffer, size) == 1)
+ {
+ #ifdef MONITOR
+
+ diff = encodeBuffer.getBits() - bits;
+
+ if (*(buffer) == MONITOR_MAJOR_OPCODE &&
+ (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE))
+ {
+ totalMessages++;
+
+ totalBits += diff;
+
+ *logofs << "handleEncode: Handled split OPCODE#" << (unsigned int) *(buffer)
+ << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size
+ << " bytes in, " << diff << " bits (" << ((float) diff) / 8
+ << " bytes) out. Average " << totalBits / totalMessages
+ << "/1.\n" << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+ }
+
+ //
+ // The split did not take place and we are going
+ // to transfer the data part. Check if the static
+ // compression of the data section is enabled.
+ // This is the case of all messages not having a
+ // special differential encoding or messages that
+ // we want to store in cache in compressed form.
+ //
+
+ unsigned int offset = store -> identitySize(buffer, size);
+
+ if (store -> enableCompress)
+ {
+ unsigned char *data = NULL;
+ unsigned int dataSize = 0;
+
+ int compressed = handleCompress(encodeBuffer, opcode, offset,
+ buffer, size, data, dataSize);
+ if (compressed < 0)
+ {
+ return -1;
+ }
+ else if (compressed > 0)
+ {
+ //
+ // Update the size of the message according
+ // to the result of the data compression.
+ //
+
+ handleUpdate(store, size - offset, dataSize);
+ }
+ }
+ else
+ {
+ handleCopy(encodeBuffer, opcode, offset, buffer, size);
+ }
+ }
+
+ #ifdef MONITOR
+
+ diff = encodeBuffer.getBits() - bits;
+
+ if (*(buffer) == MONITOR_MAJOR_OPCODE &&
+ (MONITOR_MINOR_OPCODE == -1 || *(buffer + 1) == MONITOR_MINOR_OPCODE))
+ {
+ totalMessages++;
+
+ totalBits += diff;
+
+ *logofs << "handleEncode: Handled OPCODE#" << (unsigned int) *(buffer)
+ << " MINOR#" << (unsigned int) *(buffer + 1) << ". " << size
+ << " bytes in, " << diff << " bits (" << ((float) diff) / 8
+ << " bytes) out. Average " << totalBits / totalMessages
+ << "/1.\n" << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+}
+
+int Channel::handleDecode(DecodeBuffer &decodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // Check first if the message is in the
+ // message store.
+ //
+
+ unsigned int split = 0;
+
+ if (handleDecodeCached(decodeBuffer, channelCache,
+ store, buffer, size) == 1)
+ {
+ //
+ // Let the channel update the split store
+ // in the case of a message being cached.
+ //
+
+ if (store -> enableSplit == 1)
+ {
+ // Since ProtoStep7 (#issue 108)
+ #ifdef DEBUG
+ *logofs << "handleDecode: " << store -> name()
+ << ": Checking if the message was split.\n"
+ << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeBoolValue(split);
+
+ if (split == 1)
+ {
+ handleSplit(decodeBuffer, store, store -> lastAction,
+ store -> lastHit, opcode, buffer, size);
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+ }
+ }
+
+ return 1;
+ }
+
+ //
+ // Decode the full identity.
+ //
+
+ handleDecodeIdentity(decodeBuffer, channelCache, store, buffer,
+ size, bigEndian_, &writeBuffer_);
+
+ //
+ // Check if the message has a distinct
+ // data part.
+ //
+
+ if (store -> enableData)
+ {
+ //
+ // Check if message has been split.
+ //
+
+ if (store -> enableSplit)
+ {
+ #ifdef DEBUG
+ *logofs << "handleDecode: " << store -> name()
+ << ": Checking if the message was split.\n"
+ << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeBoolValue(split);
+
+ if (split == 1)
+ {
+ //
+ // If the message was added to the store,
+ // create the entry without the data part.
+ //
+
+ handleSaveSplit(store, buffer, size);
+
+ handleSplit(decodeBuffer, store, store -> lastAction,
+ (store -> lastAction == IS_ADDED ? store -> lastAdded : 0),
+ opcode, buffer, size);
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+
+ return 0;
+ }
+ }
+
+ //
+ // Decode the data part.
+ //
+
+ unsigned int offset = store -> identitySize(buffer, size);
+
+ if (store -> enableCompress)
+ {
+ const unsigned char *data = NULL;
+ unsigned int dataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, opcode, offset,
+ buffer, size, data, dataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ else if (decompressed > 0)
+ {
+ //
+ // The message has been transferred
+ // in compressed format.
+ //
+
+ handleSave(store, buffer, size, data, dataSize);
+
+ if (store -> enableSplit)
+ {
+ if (split == 1)
+ {
+ handleSplit(decodeBuffer, store, store -> lastAction,
+ (store -> lastAction == IS_ADDED ? store -> lastAdded : 0),
+ opcode, buffer, size);
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+ }
+ }
+
+ return 0;
+ }
+ }
+ else
+ {
+ //
+ // Static compression of the data part
+ // was not enabled for this message.
+ //
+
+ handleCopy(decodeBuffer, opcode, offset, buffer, size);
+ }
+ }
+
+ //
+ // The message doesn't have a data part
+ // or the data was not compressed.
+ //
+
+ handleSave(store, buffer, size);
+
+ if (store -> enableSplit)
+ {
+ if (split == 1)
+ {
+ handleSplit(decodeBuffer, store, store -> lastAction,
+ (store -> lastAction == IS_ADDED ? store -> lastAdded : 0),
+ opcode, buffer, size);
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+ }
+ }
+
+ return 0;
+}
+
+int Channel::handleEncodeCached(EncodeBuffer &encodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, const unsigned char *buffer,
+ const unsigned int size)
+{
+ if (control -> LocalDeltaCompression == 0 ||
+ enableCache_ == 0 || store -> enableCache == 0)
+ {
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeActionValue(is_discarded,
+ store -> lastActionCache);
+
+ store -> lastAction = is_discarded;
+
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Going to handle a new message of this class.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if the estimated size of cache is greater
+ // than the requested limit. If it is the case make
+ // some room by deleting one or more messages.
+ //
+
+ int position;
+
+ while (mustCleanStore(store) == 1 && canCleanStore(store) == 1)
+ {
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Trying to reduce size of message store.\n"
+ << logofs_flush;
+ #endif
+
+ position = store -> clean(use_checksum);
+
+ if (position == nothing)
+ {
+ #ifdef TEST
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": WARNING! No message found to be "
+ << "actually removed.\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Message at position " << position
+ << " will be removed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Encode the position of message to
+ // be discarded.
+ //
+
+ store -> lastRemoved = position;
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeActionValue(is_removed, store -> lastRemoved,
+ store -> lastActionCache);
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name() << ": Going to "
+ << "clean up message at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+
+ store -> remove(position, use_checksum, discard_data);
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name() << ": There are "
+ << store -> getSize() << " messages in the store out of "
+ << store -> cacheSlots << " slots.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of store is " << store -> getLocalStorageSize()
+ << " bytes locally and " << store -> getRemoteStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of total cache is " << store -> getLocalTotalStorageSize()
+ << " bytes locally and " << store -> getRemoteTotalStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+ #endif
+ }
+
+ #ifdef DEBUG
+
+ if (mustCleanStore(store) == 1 && canCleanStore(store) == 0)
+ {
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Store would need a clean but operation will be delayed.\n"
+ << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name() << ": There are "
+ << store -> getSize() << " messages in the store out of "
+ << store -> cacheSlots << " slots.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of store is " << store -> getLocalStorageSize()
+ << " bytes locally and " << store -> getRemoteStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of total cache is " << store -> getLocalTotalStorageSize()
+ << " bytes locally and " << store -> getRemoteTotalStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // If 'on the wire' size of message exceeds the
+ // allowed limit then avoid to store it in the
+ // cache.
+ //
+
+ if (store -> validateMessage(buffer, size) == 0)
+ {
+ #ifdef TEST
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Message with size " << size << " ignored.\n"
+ << logofs_flush;
+ #endif
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeActionValue(is_discarded,
+ store -> lastActionCache);
+
+ store -> lastAction = is_discarded;
+
+ return 0;
+ }
+
+ //
+ // Fill the message object with the
+ // received data.
+ //
+
+ Message *message = store -> getTemporary();
+
+ if (message == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": PANIC! Can't allocate memory for "
+ << "a new message.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "a new message in context [D].\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // As we are at encoding side, it is enough to store the
+ // checksum for the object while data can be erased. Both
+ // the identity and the data will never be sent through
+ // the wire again as long as they are stored in the cache
+ // at the decoding side. The split parameter is always
+ // set to 0 as the data will not be stored in any case.
+ //
+
+ store -> parse(message, 0, buffer, size, use_checksum,
+ discard_data, bigEndian_);
+
+ #ifdef DUMP
+
+ store -> dump(message);
+
+ #endif
+
+ //
+ // Search the object in the message
+ // store. If found get the position.
+ //
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Searching object of size " << size
+ << " in the cache.\n" << logofs_flush;
+ #endif
+
+ int added;
+ int locked;
+
+ position = store -> findOrAdd(message, use_checksum,
+ discard_data, added, locked);
+
+ if (position == nothing)
+ {
+ #ifdef WARNING
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": WARNING! Can't store object in the cache.\n"
+ << logofs_flush;
+ #endif
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeActionValue(is_discarded,
+ store -> lastActionCache);
+
+ store -> lastAction = is_discarded;
+
+ return 0;
+ }
+ else if (locked == 1)
+ {
+ //
+ // We can't issue a cache hit. Encoding identity
+ // differences while message it's being split
+ // would later result in agent to commit a wrong
+ // version of message.
+ //
+
+ #ifdef WARNING
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": WARNING! Message of size " << store -> plainSize(position)
+ << " at position " << position << " is locked.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Message of size " << store -> plainSize(position)
+ << " at position " << position << " is locked.\n";
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeActionValue(is_discarded,
+ store -> lastActionCache);
+
+ store -> lastAction = is_discarded;
+
+ return 0;
+ }
+ else if (added == 1)
+ {
+ store -> resetTemporary();
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name() << ": Message of size "
+ << store -> plainSize(position) << " has been stored at position "
+ << position << ".\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name() << ": There are "
+ << store -> getSize() << " messages in the store out of "
+ << store -> cacheSlots << " slots.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of store is " << store -> getLocalStorageSize()
+ << " bytes locally and " << store -> getRemoteStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Size of total cache is " << store -> getLocalTotalStorageSize()
+ << " bytes locally and " << store -> getRemoteTotalStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+ #endif
+
+ //
+ // Inform the decoding side that message
+ // must be inserted in cache and encode
+ // the position where the insertion took
+ // place.
+ //
+
+ store -> lastAction = IS_ADDED;
+
+ store -> lastAdded = position;
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeActionValue(IS_ADDED, store -> lastAdded,
+ store -> lastActionCache);
+
+ return 0;
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name()
+ << ": Cache hit. Found object at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Must abort the connection if the
+ // the position is invalid.
+ //
+
+ Message *cachedMessage = store -> get(position);
+
+ //
+ // Increase the rating of the cached
+ // message.
+ //
+
+ store -> touch(cachedMessage);
+
+ #ifdef DEBUG
+ *logofs << "handleEncodeCached: " << store -> name() << ": Hits for "
+ << "object at position " << position << " are now "
+ << store -> getTouches(position) << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Send to the decoding side position
+ // where object can be found in cache.
+ //
+
+ store -> lastAction = IS_HIT;
+
+ store -> lastHit = position;
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeActionValue(IS_HIT, store -> lastHit,
+ store -> lastActionCache);
+
+ //
+ // Send the field by field differences in
+ // respect to the original message stored
+ // in cache.
+ //
+
+ store -> updateIdentity(encodeBuffer, message, cachedMessage, channelCache);
+
+ return 1;
+ }
+}
+
+void Channel::handleUpdateAdded(MessageStore *store, unsigned int dataSize,
+ unsigned int compressedDataSize)
+{
+ #ifdef TEST
+
+ if (store -> lastAction != IS_ADDED)
+ {
+ #ifdef PANIC
+ *logofs << "handleUpdateAdded: " << store -> name()
+ << ": PANIC! Function called for action '"
+ << store -> lastAction << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Update function called for "
+ << "store '" << store -> name() << "' with "
+ << "action '" << store -> lastAction
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "handleUpdateAdded: " << store -> name() << ": Updating "
+ << "object at position " << store -> lastAdded << " of size "
+ << store -> plainSize(store -> lastAdded) << " (" << dataSize
+ << "/" << compressedDataSize << ").\n" << logofs_flush;
+ #endif
+
+ store -> updateData(store -> lastAdded, dataSize, compressedDataSize);
+
+ #ifdef DEBUG
+ *logofs << "handleUpdateAdded: " << store -> name() << ": There are "
+ << store -> getSize() << " messages in the store out of "
+ << store -> cacheSlots << " slots.\n" << logofs_flush;
+
+ *logofs << "handleUpdateAdded: " << store -> name()
+ << ": Size of store is " << store -> getLocalStorageSize()
+ << " bytes locally and " << store -> getRemoteStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+
+ *logofs << "handleUpdateAdded: " << store -> name()
+ << ": Size of total cache is " << store -> getLocalTotalStorageSize()
+ << " bytes locally and " << store -> getRemoteTotalStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+ #endif
+}
+
+int Channel::handleDecodeCached(DecodeBuffer &decodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // Create a new message object and
+ // fill it with received data.
+ //
+
+ #ifdef DEBUG
+ *logofs << "handleDecodeCached: " << store -> name()
+ << ": Going to handle a new message of this class.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Decode bits telling how to handle
+ // this message.
+ //
+
+ unsigned char action;
+ unsigned short int position;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeActionValue(action, position,
+ store -> lastActionCache);
+
+ //
+ // Clean operations must always come
+ // before any operation on message.
+ //
+
+ while (action == is_removed)
+ {
+ // Since ProtoStep7 (#issue 108)
+ store -> lastRemoved = position;
+
+ #ifdef DEBUG
+
+ if (store -> get(store -> lastRemoved))
+ {
+ *logofs << "handleDecodeCached: " << store -> name() << ": Cleaning up "
+ << "object at position " << store -> lastRemoved
+ << " of size " << store -> plainSize(store -> lastRemoved)
+ << " (" << store -> plainSize(store -> lastRemoved) << "/"
+ << store -> compressedSize(store -> lastRemoved) << ").\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // If the message can't be found we
+ // will abort the connection.
+ //
+
+ store -> remove(store -> lastRemoved, discard_checksum, use_data);
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeActionValue(action, position,
+ store -> lastActionCache);
+ }
+
+ //
+ // If it's a cache hit, the position
+ // where object can be found follows.
+ //
+
+ if ((T_store_action) action == IS_HIT)
+ {
+ // Since ProtoStep7 (#issue 108)
+ store -> lastHit = position;
+
+ //
+ // Get data from the cache at given position.
+ //
+
+ #ifdef DEBUG
+
+ if (store -> get(store -> lastHit))
+ {
+ *logofs << "handleDecodeCached: " << store -> name() << ": Retrieving "
+ << "object at position " << store -> lastHit
+ << " of size " << store -> plainSize(store -> lastHit)
+ << " (" << store -> plainSize(store -> lastHit) << "/"
+ << store -> compressedSize(store -> lastHit) << ").\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Must abort the connection if the
+ // the position is invalid.
+ //
+
+ Message *message = store -> get(store -> lastHit);
+
+ //
+ // Make room for the outgoing message.
+ //
+
+ size = store -> plainSize(store -> lastHit);
+
+ buffer = writeBuffer_.addMessage(size);
+
+ #ifdef DEBUG
+ *logofs << "handleDecodeCached: " << store -> name()
+ << ": Prepared an outgoing buffer of "
+ << size << " bytes.\n" << logofs_flush;
+ #endif
+
+ //
+ // Decode the variant part. Pass client
+ // or server cache to the message store.
+ //
+
+ store -> updateIdentity(decodeBuffer, message, channelCache);
+
+ //
+ // Write each field in the outgoing buffer.
+ //
+
+ store -> unparse(message, buffer, size, bigEndian_);
+
+ #ifdef DUMP
+
+ store -> dump(message);
+
+ #endif
+
+ store -> lastAction = IS_HIT;
+
+ return 1;
+ }
+ else if ((T_store_action) action == IS_ADDED)
+ {
+ // Since ProtoStep7 (#issue 108)
+ store -> lastAdded = position;
+
+ #ifdef DEBUG
+ *logofs << "handleDecodeCached: " << store -> name()
+ << ": Message will be later stored at position "
+ << store -> lastAdded << ".\n" << logofs_flush;
+ #endif
+
+ store -> lastAction = IS_ADDED;
+
+ return 0;
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "handleDecodeCached: " << store -> name()
+ << ": Message will be later discarded.\n"
+ << logofs_flush;
+ #endif
+
+ store -> lastAction = is_discarded;
+
+ return 0;
+ }
+}
+
+void Channel::handleSaveAdded(MessageStore *store, int split, unsigned char *buffer,
+ unsigned int size, const unsigned char *compressedData,
+ const unsigned int compressedDataSize)
+{
+ #ifdef TEST
+
+ if (store -> lastAction != IS_ADDED)
+ {
+ #ifdef PANIC
+ *logofs << "handleSaveAdded: " << store -> name()
+ << ": PANIC! Function called for action '"
+ << store -> lastAction << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Save function called for "
+ << "store '" << store -> name() << "' with "
+ << "action '" << store -> lastAction
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ Message *message = store -> getTemporary();
+
+ if (message == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleSaveAdded: " << store -> name()
+ << ": PANIC! Can't access temporary storage "
+ << "for message at position " << store -> lastAdded
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't access temporary storage "
+ << "for message at position " << store -> lastAdded
+ << ".\n";
+
+ HandleCleanup();
+ }
+
+ if (compressedData == NULL)
+ {
+ //
+ // If the data part has been split
+ // avoid to copy it into the message.
+ //
+
+ store -> parse(message, split, buffer, size, discard_checksum,
+ use_data, bigEndian_);
+ }
+ else
+ {
+ store -> parse(message, buffer, size, compressedData,
+ compressedDataSize, discard_checksum,
+ use_data, bigEndian_);
+ }
+
+ if (store -> add(message, store -> lastAdded,
+ discard_checksum, use_data) == nothing)
+ {
+ #ifdef PANIC
+ *logofs << "handleSaveAdded: " << store -> name()
+ << ": PANIC! Can't store message in the cache "
+ << "at position " << store -> lastAdded << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't store message of type "
+ << store -> name() << "in the cache at position "
+ << store -> lastAdded << ".\n";
+
+ HandleCleanup();
+ }
+ else
+ {
+ store -> resetTemporary();
+
+ #ifdef DEBUG
+ *logofs << "handleSaveAdded: " << store -> name() << ": Stored "
+ << (compressedData == NULL ? "plain" : "compressed")
+ << " object at position " << store -> lastAdded
+ << " of size " << store -> plainSize(store -> lastAdded)
+ << " (" << store -> plainSize(store -> lastAdded) << "/"
+ << store -> compressedSize(store -> lastAdded) << ").\n"
+ << logofs_flush;
+ #endif
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleSaveAdded: " << store -> name()
+ << ": Size of store is " << store -> getLocalStorageSize()
+ << " bytes locally and " << store -> getRemoteStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+
+ *logofs << "handleSaveAdded: " << store -> name()
+ << ": Size of total cache is " << store -> getLocalTotalStorageSize()
+ << " bytes locally and " << store -> getRemoteTotalStorageSize()
+ << " bytes remotely.\n" << logofs_flush;
+ #endif
+}
+
+int Channel::handleWait(int timeout)
+{
+ #ifdef TEST
+ *logofs << "handleWait: Going to wait for more data "
+ << "on FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ T_timestamp startTs = getNewTimestamp();
+
+ T_timestamp nowTs = startTs;
+
+ int readable;
+ int remaining;
+
+ for (;;)
+ {
+ remaining = timeout - diffTimestamp(startTs, nowTs);
+
+ if (transport_ -> blocked() == 1)
+ {
+ #ifdef WARNING
+ *logofs << "handleWait: WARNING! Having to drain with "
+ << "channel " << "for FD#" << fd_ << " blocked.\n"
+ << logofs_flush;
+ #endif
+
+ handleDrain(0, remaining);
+
+ continue;
+ }
+
+ if (remaining <= 0)
+ {
+ #ifdef TEST
+ *logofs << "handleWait: Timeout raised while waiting "
+ << "for more data for FD#" << fd_ << " at "
+ << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef TEST
+ *logofs << "handleWait: Waiting " << remaining << " Ms "
+ << "for a new message on FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ readable = transport_ -> wait(remaining);
+
+ if (readable > 0)
+ {
+ #ifdef TEST
+ *logofs << "handleWait: WARNING! Encoding more data "
+ << "for FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncRead(fd_) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+ }
+ else if (readable == -1)
+ {
+ return -1;
+ }
+
+ nowTs = getNewTimestamp();
+ }
+}
+
+int Channel::handleDrain(int limit, int timeout)
+{
+ #ifdef TEST
+ *logofs << "handleDrain: Going to drain FD#" << fd_
+ << " with a limit of " << limit << " bytes "
+ << "at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ T_timestamp startTs = getNewTimestamp();
+
+ T_timestamp nowTs = startTs;
+
+ int drained;
+ int remaining;
+
+ int result;
+
+ for (;;)
+ {
+ remaining = timeout - diffTimestamp(startTs, nowTs);
+
+ if (remaining <= 0)
+ {
+ #ifdef TEST
+ *logofs << "handleDrain: Timeout raised while draining "
+ << "FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ result = 0;
+
+ goto ChannelDrainEnd;
+ }
+
+ #ifdef TEST
+ *logofs << "handleDrain: Trying to write to FD#"
+ << fd_ << " with " << remaining << " Ms "
+ << "remaining.\n" << logofs_flush;
+ #endif
+
+ drained = transport_ -> drain(limit, remaining);
+
+ if (drained == 1)
+ {
+ #ifdef TEST
+ *logofs << "handleDrain: Transport for FD#" << fd_
+ << " drained to " << transport_ -> length()
+ << " bytes at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ result = 1;
+
+ goto ChannelDrainEnd;
+ }
+ else if (drained == 0 && transport_ -> readable() > 0)
+ {
+ #ifdef TEST
+ *logofs << "handleDrain: WARNING! Encoding more data "
+ << "for FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncRead(fd_) < 0)
+ {
+ goto ChannelDrainError;
+ }
+ }
+ else if (drained == -1)
+ {
+ goto ChannelDrainError;
+ }
+
+ nowTs = getNewTimestamp();
+
+ if (diffTimestamp(startTs, nowTs) >= control -> ChannelTimeout)
+ {
+ int seconds = (remaining + control -> LatencyTimeout * 10) / 1000;
+
+ #ifdef WARNING
+ *logofs << "handleDrain: WARNING! Could not drain FD#"
+ << fd_ << " within " << seconds << " seconds.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't write to connection on FD#"
+ << fd_ << " since " << seconds << " seconds.\n";
+
+ if (alert_ == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ alert_ = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT;
+ }
+ else
+ {
+ alert_ = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT;
+ }
+
+ HandleAlert(alert_, 1);
+ }
+ }
+ }
+
+ChannelDrainEnd:
+
+ //
+ // Maybe we drained the channel and are
+ // now out of the congestion state.
+ //
+
+ handleCongestion();
+
+ return result;
+
+ChannelDrainError:
+
+ finish_ = 1;
+
+ return -1;
+}
+
+int Channel::handleCongestion()
+{
+ //
+ // Send a begin congestion control code
+ // if the local end of the channel does
+ // not consume its data.
+ //
+
+ if (isCongested() == 1)
+ {
+ if (congestion_ == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleCongestion: Sending congestion for FD#"
+ << fd_ << " with length " << transport_ -> length()
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ congestion_ = 1;
+
+ //
+ // Use the callback to send the control
+ // code immediately.
+ //
+
+ if (proxy -> handleAsyncCongestion(fd_) < 0)
+ {
+ finish_ = 1;
+
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ //
+ // If the channel was in congestion state
+ // send an end congestion control code.
+ //
+
+ if (congestion_ == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleCongestion: Sending decongestion for FD#"
+ << fd_ << " with length " << transport_ -> length()
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+
+ if (proxy -> handleAsyncDecongestion(fd_) < 0)
+ {
+ finish_ = 1;
+
+ return -1;
+ }
+ }
+
+ //
+ // Remove the "channel unresponsive"
+ // dialog.
+ //
+
+ if (alert_ != 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleCongestion: Displacing the dialog "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ HandleAlert(DISPLACE_MESSAGE_ALERT, 1);
+ }
+ }
+
+ return 1;
+}
+
+int Channel::handleFlush(T_flush type, int bufferLength, int scratchLength)
+{
+ if (finish_ == 1)
+ {
+ #ifdef TEST
+ *logofs << "handleFlush: Not flushing data for "
+ << "finishing channel for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ writeBuffer_.fullReset();
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "handleFlush: Flushing " << bufferLength
+ << " + " << scratchLength << " bytes "
+ << "to FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if the channel has data available.
+ // Recent Linux kernels are very picky.
+ // They require that we read often or they
+ // assume that the process is non-interact-
+ // ive.
+ //
+
+ int result = 0;
+
+ if (handleAsyncEvents() < 0)
+ {
+ goto ChannelFlushError;
+ }
+
+ //
+ // Write the data in the main buffer first,
+ // followed by the data in the scratch buffer.
+ //
+
+ if (bufferLength > 0)
+ {
+ result = transport_ -> write(write_immediate,
+ writeBuffer_.getData(), bufferLength);
+ }
+
+ if (result >= 0 && scratchLength > 0)
+ {
+ result = transport_ -> write(write_immediate,
+ writeBuffer_.getScratchData(), scratchLength);
+ }
+
+ if (type == flush_if_any)
+ {
+ writeBuffer_.fullReset();
+ }
+ else
+ {
+ writeBuffer_.partialReset();
+ }
+
+ //
+ // If we failed to write to the X connection then
+ // set the finish flag. The caller should continue
+ // to handle all the remaining messages or it will
+ // corrupt the decode buffer. At the real end, an
+ // error will be propagated to the upper layers
+ // which will perform any needed cleanup.
+ //
+
+ if (result < 0)
+ {
+ goto ChannelFlushError;
+ }
+
+ //
+ // Reset transport buffers.
+ //
+
+ transport_ -> partialReset();
+
+ //
+ // Check if the X server has generated
+ // any event in response to our data.
+ //
+
+ if (handleAsyncEvents() < 0)
+ {
+ goto ChannelFlushError;
+ }
+
+ //
+ // Check if the channel has entered in
+ // congestion state and, in this case,
+ // send an immediate congestion control
+ // code to the remote.
+ //
+
+ handleCongestion();
+
+ //
+ // We could optionally drain the output
+ // buffer if this is X11 channel.
+ //
+ // if (isCongested() == 1 && isReliable() == 1)
+ // {
+ // if (handleDrain(0, control -> ChannelTimeout) < 0)
+ // {
+ // goto ChannelFlushError;
+ // }
+ // }
+ //
+
+ return 1;
+
+ChannelFlushError:
+
+ finish_ = 1;
+
+ return -1;
+}
+
+int Channel::handleFlush()
+{
+ #ifdef TEST
+ *logofs << "handleFlush: Flushing "
+ << transport_ -> length() << " bytes to FD#"
+ << fd_ << " with descriptor writable.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if there is anything to read
+ // before anf after having written to
+ // the socket.
+ //
+
+ if (handleAsyncEvents() < 0)
+ {
+ goto ChannelFlushError;
+ }
+
+ if (transport_ -> flush() < 0)
+ {
+ #ifdef TEST
+ *logofs << "handleFlush: Failure detected "
+ << "flushing data to FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ goto ChannelFlushError;
+ }
+
+ if (handleAsyncEvents() < 0)
+ {
+ goto ChannelFlushError;
+ }
+
+ //
+ // Reset channel's transport buffers.
+ //
+
+ transport_ -> partialReset();
+
+ //
+ // Check if the channel went out of the
+ // congestion state.
+ //
+
+ handleCongestion();
+
+ return 1;
+
+ChannelFlushError:
+
+ finish_ = 1;
+
+ return -1;
+}
+
+void Channel::handleResetAlert()
+{
+ if (alert_ != 0)
+ {
+ #ifdef TEST
+ *logofs << "handleResetAlert: The channel alert '"
+ << alert_ << "' was displaced.\n"
+ << logofs_flush;
+ #endif
+
+ alert_ = 0;
+ }
+}
+
+int Channel::handleCompress(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned int offset, const unsigned char *buffer,
+ const unsigned int size, unsigned char *&compressedData,
+ unsigned int &compressedDataSize)
+{
+ if (size <= offset)
+ {
+ #ifdef DEBUG
+ *logofs << "handleCompress: Not compressing data for FD#" << fd_
+ << " as offset is " << offset << " with data size "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleCompress: Compressing data for FD#" << fd_
+ << " with data size " << size << " and offset "
+ << offset << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // It is responsibility of the compressor to
+ // mark the buffer as such if the compression
+ // couldn't take place.
+ //
+
+ if (compressor_ -> compressBuffer(buffer + offset, size - offset, compressedData,
+ compressedDataSize, encodeBuffer) <= 0)
+ {
+ #ifdef DEBUG
+ *logofs << "handleCompress: Sent " << size - offset
+ << " bytes of plain data for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "handleCompress: Sent " << compressedDataSize
+ << " bytes of compressed data for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+}
+
+int Channel::handleDecompress(DecodeBuffer &decodeBuffer, const unsigned char opcode,
+ const unsigned int offset, unsigned char *buffer,
+ const unsigned int size, const unsigned char *&compressedData,
+ unsigned int &compressedDataSize)
+{
+ if (size <= offset)
+ {
+ return 0;
+ }
+
+ int result = compressor_ -> decompressBuffer(buffer + offset, size - offset,
+ compressedData, compressedDataSize,
+ decodeBuffer);
+ if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleDecompress: PANIC! Failed to decompress "
+ << size - offset << " bytes of data for FD#" << fd_
+ << " with OPCODE#" << (unsigned int) opcode << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Data decompression failed for OPCODE#"
+ << (unsigned int) opcode << ".\n";
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "handleDecompress: Received " << size - offset
+ << " bytes of plain data for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "handleDecompress: Received " << compressedDataSize
+ << " bytes of compressed data for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+}
+
+int Channel::handleCleanAndNullRequest(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ #ifdef TEST
+ *logofs << "handleCleanAndNullRequest: Removing the previous data "
+ << "and sending an X_NoOperation " << "for FD#" << fd_
+ << " due to OPCODE#" << (unsigned int) opcode << " ("
+ << DumpOpcode(opcode) << ").\n" << logofs_flush;
+ #endif
+
+ writeBuffer_.removeMessage(size - 4);
+
+ size = 4;
+ opcode = X_NoOperation;
+
+ return 1;
+}
+
+int Channel::handleNullRequest(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ #ifdef TEST
+ *logofs << "handleNullRequest: Sending an X_NoOperation for FD#"
+ << fd_ << " due to OPCODE#" << (unsigned int) opcode
+ << " (" << DumpOpcode(opcode) << ").\n"
+ << logofs_flush;
+ #endif
+
+ size = 4;
+ buffer = writeBuffer_.addMessage(size);
+ opcode = X_NoOperation;
+
+ return 1;
+}
+
+void Channel::handleSplitStoreError(int resource)
+{
+ if (resource < 0 || resource >= CONNECTIONS_LIMIT)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitStoreError: PANIC! Resource "
+ << resource << " is out of range with limit "
+ << "set to " << CONNECTIONS_LIMIT << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Resource " << resource
+ << " is out of range with limit set to "
+ << CONNECTIONS_LIMIT << ".\n";
+
+ HandleCleanup();
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitStoreError: PANIC! Cannot "
+ << "allocate the split store for resource "
+ << resource << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot allocate the "
+ << "split store for resource " << resource
+ << ".\n";
+
+ HandleCleanup();
+ }
+}
+
+void Channel::handleSplitStoreAlloc(List *list, int resource)
+{
+ if (resource < 0 || resource >= CONNECTIONS_LIMIT)
+ {
+ handleSplitStoreError(resource);
+ }
+
+ if (clientStore_ -> getSplitStore(resource) == NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitStoreAlloc: Allocating a new "
+ << "split store for resource " << resource
+ << ".\n" << logofs_flush;
+ #endif
+
+ SplitStore *splitStore = clientStore_ -> createSplitStore(resource);
+
+ if (splitStore == NULL)
+ {
+ handleSplitStoreError(resource);
+ }
+
+ list -> add(resource);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ //
+ // Old proxy versions only use a single
+ // split store.
+ //
+
+ if (resource != 0)
+ {
+ *logofs << "handleSplitStoreAlloc: WARNING! A split "
+ << "store for resource " << resource
+ << " already exists.\n" << logofs_flush;
+ }
+ }
+ #endif
+}
+
+void Channel::handleSplitStoreRemove(List *list, int resource)
+{
+ if (resource < 0 || resource >= CONNECTIONS_LIMIT)
+ {
+ handleSplitStoreError(resource);
+ }
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore != NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitStoreRemove: Deleting the "
+ << "split store for resource " << resource
+ << ".\n" << logofs_flush;
+ #endif
+
+ clientStore_ -> destroySplitStore(resource);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitStoreRemove: Deleting resource "
+ << resource << " from the list " << ".\n"
+ << logofs_flush;
+ #endif
+
+ list -> remove(resource);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleSplitStoreRemove: WARNING! A split "
+ << "store for resource " << resource
+ << " does not exist.\n" << logofs_flush;
+ }
+ #endif
+}
+
+Split *Channel::handleSplitCommitRemove(int request, int resource, int position)
+{
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitCommitRemove: SPLIT! Checking split "
+ << "commit with resource " << resource << " request "
+ << request << " and position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Remove the split from the split queue.
+ //
+
+ CommitStore *commitStore = clientStore_ -> getCommitStore();
+
+ Split *split = commitStore -> pop();
+
+ if (split == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitCommitRemove: PANIC! Can't "
+ << "find the split in the commit queue.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't find the "
+ << "split in the commit queue.\n";
+
+ HandleCleanup();
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitCommitRemove: SPLIT! Element from "
+ << "the queue has resource " << split -> getResource()
+ << " request " << split -> getRequest() << " and "
+ << "position " << split -> getPosition()
+ << ".\n" << logofs_flush;
+ #endif
+
+ // Since ProtoStep7 (#issue 108)
+ if (resource != split -> getResource() ||
+ request != split -> getRequest() ||
+ position != split -> getPosition())
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitCommitRemove: PANIC! The data in "
+ << "the split doesn't match the commit request.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The data in the split doesn't "
+ << "match the commit request.\n";
+
+ return NULL;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ commitStore -> dump();
+
+ #endif
+
+ return split;
+}
+
+int Channel::setReferences()
+{
+ #ifdef TEST
+ *logofs << "Channel: Initializing the static "
+ << "members for the base class.\n"
+ << logofs_flush;
+ #endif
+
+ firstClient_ = -1;
+
+ fontPort_ = -1;
+
+ #ifdef REFERENCES
+
+ references_ = 0;
+
+ #endif
+
+ return 1;
+}
+
+int Channel::setOpcodes(OpcodeStore *opcodeStore)
+{
+ opcodeStore_ = opcodeStore;
+
+ #ifdef TEST
+ *logofs << "setOpcodes: Propagated opcodes store to channel "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int Channel::setStores(ClientStore *clientStore, ServerStore *serverStore)
+{
+ clientStore_ = clientStore;
+ serverStore_ = serverStore;
+
+ #ifdef TEST
+ *logofs << "setStores: Propagated message stores to channel "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int Channel::setCaches(ClientCache *clientCache, ServerCache *serverCache)
+{
+ clientCache_ = clientCache;
+ serverCache_ = serverCache;
+
+ #ifdef TEST
+ *logofs << "setCaches: Propagated encode caches to channel "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/Channel.h b/nxcomp/src/Channel.h
new file mode 100644
index 000000000..7e432416a
--- /dev/null
+++ b/nxcomp/src/Channel.h
@@ -0,0 +1,664 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Channel_H
+#define Channel_H
+
+#include "Transport.h"
+
+#include "WriteBuffer.h"
+
+#include "OpcodeStore.h"
+
+#include "ClientStore.h"
+#include "ServerStore.h"
+
+#include "ClientCache.h"
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Forward declaration of referenced classes.
+//
+
+class List;
+
+class StaticCompressor;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to log a line when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Type of traffic carried by channel.
+//
+
+typedef enum
+{
+ channel_none = -1,
+ channel_x11,
+ channel_cups,
+ channel_smb,
+ channel_media,
+ channel_http,
+ channel_font,
+ channel_slave,
+ channel_last_tag
+
+} T_channel_type;
+
+//
+// Type of notification event to be sent
+// by proxy to the X channel.
+//
+
+typedef enum
+{
+ notify_no_split,
+ notify_start_split,
+ notify_commit_split,
+ notify_end_split,
+ notify_empty_split,
+
+} T_notification_type;
+
+class Channel
+{
+ public:
+
+ //
+ // Maximum number of X connections supported.
+ //
+
+ static const int CONNECTIONS_LIMIT = 256;
+
+ Channel(Transport *transport, StaticCompressor *compressor);
+
+ virtual ~Channel();
+
+ //
+ // Read any X message available on the X
+ // connection and encode it to the encode
+ // buffer.
+ //
+
+ virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length) = 0;
+
+ //
+ // Decode any X message encoded in the
+ // proxy message and write it to the X
+ // connection.
+ //
+
+ virtual int handleWrite(const unsigned char *message, unsigned int length) = 0;
+
+ //
+ // Other methods to be implemented in
+ // client, server and generic channel
+ // classes.
+ //
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
+ T_store_action action, int position, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size) = 0;
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store,
+ T_store_action action, int position, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size) = 0;
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer) = 0;
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer) = 0;
+
+ virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split) = 0;
+
+ virtual int handleSplitEvent(DecodeBuffer &decodeBuffer) = 0;
+
+ virtual int handleMotion(EncodeBuffer &encodeBuffer) = 0;
+
+ virtual int handleCompletion(EncodeBuffer &encodeBuffer) = 0;
+
+ virtual int handleConfiguration() = 0;
+
+ virtual int handleFinish() = 0;
+
+ //
+ // Interleave reads of the available
+ // events while writing data to the
+ // channel socket.
+ //
+
+ virtual int handleAsyncEvents() = 0;
+
+ //
+ // Handle the channel tear down.
+ //
+
+ int handleClosing()
+ {
+ closing_ = 1;
+
+ return 1;
+ }
+
+ int handleDrop()
+ {
+ drop_ = 1;
+
+ return 1;
+ }
+
+ //
+ // Try to read more data from the socket. In
+ // the meanwhile flush any enqueued data if
+ // the channel is blocked. Return as soon as
+ // more data has been read or the timeout has
+ // been exceeded.
+ //
+
+ int handleWait(int timeout);
+
+ //
+ // Drain the output buffer while handling the
+ // data that may become readable.
+ //
+
+ int handleDrain(int timeout, int limit);
+
+ //
+ // Flush any remaining data in the transport
+ // buffer.
+ //
+
+ int handleFlush();
+
+ //
+ // Called when the loop has replaced or
+ // closed a previous alert.
+ //
+
+ void handleResetAlert();
+
+ //
+ // Initialize all the static members.
+ //
+
+ static int setReferences();
+
+ //
+ // Set pointer to object mapping opcodes
+ // of NX specific messages.
+ //
+
+ int setOpcodes(OpcodeStore *opcodeStore);
+
+ //
+ // Update pointers to message stores in
+ // channels.
+ //
+
+ int setStores(ClientStore *clientStore, ServerStore *serverStore);
+
+ //
+ // The same for channels caches.
+ //
+
+ int setCaches(ClientCache *clientCache, ServerCache *serverCache);
+
+ //
+ // Set the port used for tunneling of the
+ // font server connections.
+ //
+
+ void setPorts(int fontPort)
+ {
+ fontPort_ = fontPort;
+ }
+
+ //
+ // Check if there are pending split
+ // to send to the remote side.
+ //
+
+ virtual int needSplit() const = 0;
+
+ //
+ // Check if there are motion events
+ // to flush.
+ //
+
+ virtual int needMotion() const = 0;
+
+ //
+ // Return the type of traffic carried
+ // by this channel.
+ //
+
+ virtual T_channel_type getType() const = 0;
+
+ //
+ // Check if the channel has been marked
+ // as closing down.
+ //
+
+ int getFinish() const
+ {
+ return finish_;
+ }
+
+ int getClosing()
+ {
+ return closing_;
+ }
+
+ int getDrop()
+ {
+ return drop_;
+ }
+
+ int getCongestion()
+ {
+ return congestion_;
+ }
+
+ protected:
+
+ int handleFlush(T_flush type)
+ {
+ //
+ // We could write the data immediately if there
+ // is already something queued to the low level
+ // TCP buffers.
+ //
+ // if (... || transport_ -> queued() > 0)
+ // {
+ // ...
+ // }
+ //
+
+ if (writeBuffer_.getScratchLength() > 0 ||
+ (type == flush_if_any && writeBuffer_.getLength() > 0) ||
+ writeBuffer_.getLength() >= (unsigned int)
+ control -> TransportFlushBufferSize)
+ {
+ return handleFlush(type, writeBuffer_.getLength(),
+ writeBuffer_.getScratchLength());
+ }
+
+ return 0;
+ }
+
+ //
+ // Actually flush the data to the
+ // channel descriptor.
+ //
+
+ int handleFlush(T_flush type, int bufferLength, int scratchLength);
+
+ //
+ // Handle the congestion changes.
+ //
+
+ int handleCongestion();
+
+ //
+ // Encode and decode X messages.
+ //
+
+ int handleEncode(EncodeBuffer &encodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ int handleDecode(DecodeBuffer &decodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Encode the message based on its
+ // message store.
+ //
+
+ int handleEncodeCached(EncodeBuffer &encodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, const unsigned char *buffer,
+ const unsigned int size);
+
+ int handleDecodeCached(DecodeBuffer &decodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleEncodeIdentity(EncodeBuffer &encodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, const unsigned char *buffer,
+ const unsigned int size, int bigEndian)
+ {
+ return (store -> encodeIdentity(encodeBuffer, buffer, size,
+ bigEndian, channelCache));
+ }
+
+ int handleDecodeIdentity(DecodeBuffer &decodeBuffer, ChannelCache *channelCache,
+ MessageStore *store, unsigned char *&buffer,
+ unsigned int &size, int bigEndian,
+ WriteBuffer *writeBuffer)
+ {
+ return (store -> decodeIdentity(decodeBuffer, buffer, size, bigEndian,
+ writeBuffer, channelCache));
+ }
+
+ //
+ // Other utility functions used by
+ // the encoding and decoding methods.
+ //
+
+ void handleCopy(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned int offset, const unsigned char *buffer,
+ const unsigned int size)
+ {
+ if (size > offset)
+ {
+ encodeBuffer.encodeMemory(buffer + offset, size - offset);
+ }
+ }
+
+ void handleCopy(DecodeBuffer &decodeBuffer, const unsigned char opcode,
+ const unsigned int offset, unsigned char *buffer,
+ const unsigned int size)
+ {
+ if (size > offset)
+ {
+ memcpy(buffer + offset, decodeBuffer.decodeMemory(size - offset), size - offset);
+ }
+ }
+
+ void handleUpdate(MessageStore *store, const unsigned int dataSize,
+ const unsigned int compressedDataSize)
+ {
+ if (store -> lastAction == IS_ADDED)
+ {
+ handleUpdateAdded(store, dataSize, compressedDataSize);
+ }
+ }
+
+ void handleSave(MessageStore *store, unsigned char *buffer, unsigned int size,
+ const unsigned char *compressedData = NULL,
+ const unsigned int compressedDataSize = 0)
+ {
+ if (store -> lastAction == IS_ADDED)
+ {
+ handleSaveAdded(store, 0, buffer, size, compressedData, compressedDataSize);
+ }
+ }
+
+ void handleSaveSplit(MessageStore *store, unsigned char *buffer,
+ unsigned int size)
+ {
+ if (store -> lastAction == IS_ADDED)
+ {
+ return handleSaveAdded(store, 1, buffer, size, 0, 0);
+ }
+ }
+
+ void handleUpdateAdded(MessageStore *store, const unsigned int dataSize,
+ const unsigned int compressedDataSize);
+
+ void handleSaveAdded(MessageStore *store, int split, unsigned char *buffer,
+ unsigned int size, const unsigned char *compressedData,
+ const unsigned int compressedDataSize);
+
+ //
+ // Compress the data part of a message
+ // using ZLIB or another compressor
+ // and send it over the network.
+ //
+
+ int handleCompress(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned int offset, const unsigned char *buffer,
+ const unsigned int size, unsigned char *&compressedData,
+ unsigned int &compressedDataSize);
+
+ int handleDecompress(DecodeBuffer &decodeBuffer, const unsigned char opcode,
+ const unsigned int offset, unsigned char *buffer,
+ const unsigned int size, const unsigned char *&compressedData,
+ unsigned int &compressedDataSize);
+
+ //
+ // Send an X_NoOperation to the X server.
+ // The second version also removes any
+ // previous data in the write buffer.
+ //
+
+ int handleNullRequest(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleCleanAndNullRequest(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ //
+ // X11 channels are considered to be in
+ // congestion state when there was a
+ // blocking write and, since then, the
+ // local end didn't consume all the data.
+ //
+
+ virtual int isCongested()
+ {
+ return (transport_ -> getType() !=
+ transport_agent && transport_ -> length() >
+ control -> TransportFlushBufferSize);
+ }
+
+ virtual int isReliable()
+ {
+ return 1;
+ }
+
+ //
+ // Determine how to handle allocation
+ // of new messages in the message
+ // stores.
+ //
+
+ int mustCleanStore(MessageStore *store)
+ {
+ return (store -> getRemoteTotalStorageSize() > control ->
+ RemoteTotalStorageSize || store -> getLocalTotalStorageSize() >
+ control -> LocalTotalStorageSize || (store -> getRemoteStorageSize() >
+ (control -> RemoteTotalStorageSize / 100 * store ->
+ cacheThreshold)) || (store -> getLocalStorageSize() >
+ (control -> LocalTotalStorageSize / 100 * store ->
+ cacheThreshold)));
+ }
+
+ int canCleanStore(MessageStore *store)
+ {
+ return ((store -> getSize() > 0 && (store -> getRemoteStorageSize() >
+ (control -> RemoteTotalStorageSize / 100 * store ->
+ cacheLowerThreshold))) || (store -> getLocalStorageSize() >
+ (control -> LocalTotalStorageSize / 100 * store ->
+ cacheLowerThreshold)));
+ }
+
+ protected:
+
+ //
+ // Set up the split stores.
+ //
+
+ void handleSplitStoreError(int resource) __attribute__((noreturn));
+
+ void handleSplitStoreAlloc(List *list, int resource);
+ void handleSplitStoreRemove(List *list, int resource);
+
+ Split *handleSplitCommitRemove(int request, int resource, int position);
+
+ void validateSize(const char *name, int input, int output,
+ int offset, int size)
+ {
+ if (size < offset || size > control -> MaximumMessageSize ||
+ size != (int) RoundUp4(input) + offset ||
+ output > control -> MaximumMessageSize)
+ {
+ *logofs << "Channel: PANIC! Invalid size " << size
+ << " for " << name << " output with data "
+ << input << "/" << output << "/" << offset
+ << "/" << size << ".\n" << logofs_flush;
+
+ cerr << "Error" << ": Invalid size " << size
+ << " for " << name << " output.\n";
+
+ HandleAbort();
+ }
+ }
+
+ //
+ // Is the X client big endian?
+ //
+
+ int bigEndian() const
+ {
+ return bigEndian_;
+ }
+
+ int bigEndian_;
+
+ //
+ // Other X server's features
+ // saved at session startup.
+ //
+
+ unsigned int imageByteOrder_;
+ unsigned int bitmapBitOrder_;
+ unsigned int scanlineUnit_;
+ unsigned int scanlinePad_;
+
+ int firstRequest_;
+ int firstReply_;
+
+ //
+ // Use this class for IO operations.
+ //
+
+ Transport *transport_;
+
+ //
+ // The static compressor is created by the
+ // proxy and shared among channels.
+ //
+
+ StaticCompressor *compressor_;
+
+ //
+ // Map NX operations to opcodes. Propagated
+ // by proxy to all channels on the same X
+ // server.
+ //
+
+ OpcodeStore *opcodeStore_;
+
+ //
+ // Also stores are shared between channels.
+ //
+
+ ClientStore *clientStore_;
+ ServerStore *serverStore_;
+
+ //
+ // Caches are specific for each channel.
+ //
+
+ ClientCache *clientCache_;
+ ServerCache *serverCache_;
+
+ //
+ // Data going to X connection.
+ //
+
+ WriteBuffer writeBuffer_;
+
+ //
+ // Other data members.
+ //
+
+ int fd_;
+
+ int finish_;
+ int closing_;
+ int drop_;
+ int congestion_;
+ int priority_;
+
+ int alert_;
+
+ //
+ // It will be set to the descriptor of the
+ // first X channel that is successfully con-
+ // nected and will print an info message on
+ // standard error.
+ //
+
+ static int firstClient_;
+
+ //
+ // Port used for font server connections.
+ //
+
+ static int fontPort_;
+
+ //
+ // Track which cache operations have been
+ // enabled by the agent.
+ //
+
+ int enableCache_;
+ int enableSplit_;
+ int enableSave_;
+ int enableLoad_;
+
+ //
+ // Keep track of object creation and
+ // deletion.
+ //
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+#endif /* Channel_H */
diff --git a/nxcomp/src/ChannelCache.cpp b/nxcomp/src/ChannelCache.cpp
new file mode 100644
index 000000000..f30f18bc1
--- /dev/null
+++ b/nxcomp/src/ChannelCache.cpp
@@ -0,0 +1,68 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ChannelCache.h"
+
+const unsigned int CONFIGUREWINDOW_FIELD_WIDTH[7] =
+{
+ 16, // x
+ 16, // y
+ 16, // width
+ 16, // height
+ 16, // border width
+ 29, // sibling window
+ 3 // stack mode
+};
+
+const unsigned int CREATEGC_FIELD_WIDTH[23] =
+{
+ 4, // function
+ 32, // plane mask
+ 32, // foreground
+ 32, // background
+ 16, // line width
+ 2, // line style
+ 2, // cap style
+ 2, // join style
+ 2, // fill style
+ 1, // fill rule
+ 29, // tile
+ 29, // stipple
+ 16, // tile/stipple x origin
+ 16, // tile/stipple y origin
+ 29, // font
+ 1, // subwindow mode
+ 1, // graphics exposures
+ 16, // clip x origin
+ 16, // clip y origin
+ 29, // clip mask
+ 16, // card offset
+ 8, // dashes
+ 1 // arc mode
+};
diff --git a/nxcomp/src/ChannelCache.h b/nxcomp/src/ChannelCache.h
new file mode 100644
index 000000000..6a29c3847
--- /dev/null
+++ b/nxcomp/src/ChannelCache.h
@@ -0,0 +1,61 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ChannelCache_H
+#define ChannelCache_H
+
+//
+// Sizes of optional fields for ConfigureWindow
+// request.
+//
+
+extern const unsigned int CONFIGUREWINDOW_FIELD_WIDTH[7];
+
+//
+// Sizes of optional fields for CreateGC request.
+//
+
+extern const unsigned int CREATEGC_FIELD_WIDTH[23];
+
+//
+// This is just needed to provide a pointer
+// to the base cache class in encoding and
+// decoding procedures of message stores.
+//
+
+class ChannelCache
+{
+ public:
+
+ ChannelCache()
+ {
+ }
+
+ ~ChannelCache()
+ {
+ }
+};
+
+#endif /* ChannelCache_H */
diff --git a/nxcomp/src/ChannelEndPoint.cpp b/nxcomp/src/ChannelEndPoint.cpp
new file mode 100644
index 000000000..921615bae
--- /dev/null
+++ b/nxcomp/src/ChannelEndPoint.cpp
@@ -0,0 +1,349 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "ChannelEndPoint.h"
+
+#include "NXalert.h"
+
+ChannelEndPoint::ChannelEndPoint(const char *spec)
+ : defaultTCPPort_(0), defaultTCPInterface_(0),
+ defaultUnixPath_(NULL), spec_(NULL) {
+ setSpec(spec);
+}
+
+ChannelEndPoint::~ChannelEndPoint()
+{
+ char *unixPath = NULL;
+
+ if (getUnixPath(&unixPath))
+ {
+ struct stat st;
+ lstat(unixPath, &st);
+ if(S_ISSOCK(st.st_mode))
+ unlink(unixPath);
+ }
+}
+
+void
+ChannelEndPoint::setSpec(const char *spec) {
+ if (spec_) free(spec_);
+
+ if (spec && strlen(spec))
+ {
+ spec_ = strdup(spec);
+ isUnix_ = getUnixPath();
+ isTCP_ = getTCPHostAndPort();
+ }
+ else
+ {
+ spec_ = NULL;
+ isUnix_ = false;
+ isTCP_ = false;
+ }
+}
+
+void
+ChannelEndPoint::setSpec(long port) {
+ if (port >= 0) {
+ char tmp[20];
+ sprintf(tmp, "%ld", port);
+ setSpec(tmp);
+ }
+ else {
+ disable();
+ }
+}
+
+void
+ChannelEndPoint::setSpec(const char *hostName, long port) {
+ int length;
+
+ if (spec_) free(spec_);
+ isUnix_ = false;
+ isTCP_ = false;
+
+ if (hostName && strlen(hostName) && port >= 1)
+ {
+ length = snprintf(NULL, 0, "tcp:%s:%ld", hostName, port);
+ spec_ = (char *)calloc(length + 1, sizeof(char));
+ snprintf(spec_, length+1, "tcp:%s:%ld", hostName, port);
+ isTCP_ = true;
+ }
+ else setSpec((char*)NULL);
+}
+
+bool
+ChannelEndPoint::getSpec(char **socketUri) const {
+
+ if (socketUri) *socketUri = NULL;
+
+ char *unixPath = NULL;
+ char *hostName = NULL;
+ long port = -1;
+
+ char *newSocketUri = NULL;
+ int length = -1;
+
+ if (getUnixPath(&unixPath))
+ {
+ length = snprintf(NULL, 0, "unix:%s", unixPath);
+ }
+ else if (getTCPHostAndPort(&hostName, &port))
+ {
+ length = snprintf(NULL, 0, "tcp:%s:%ld", hostName, port);
+ }
+
+ if (length > 0)
+ {
+ newSocketUri = (char *)calloc(length + 1, sizeof(char));
+ if (isUnixSocket())
+ snprintf(newSocketUri, length+1, "unix:%s", unixPath);
+ else
+ snprintf(newSocketUri, length+1, "tcp:%s:%ld", hostName, port);
+
+ if (socketUri)
+ *socketUri = strdup(newSocketUri);
+ }
+
+ free(newSocketUri);
+ free(unixPath);
+ free(hostName);
+
+ if (NULL != *socketUri)
+ return true;
+
+ return false;
+}
+
+void
+ChannelEndPoint::setDefaultTCPPort(long port) {
+ defaultTCPPort_ = port;
+}
+
+void
+ChannelEndPoint::setDefaultTCPInterface(int publicInterface) {
+ defaultTCPInterface_ = publicInterface;
+}
+
+void
+ChannelEndPoint::setDefaultUnixPath(char *path) {
+ if (defaultUnixPath_) free(defaultUnixPath_);
+
+ if (path && strlen(path))
+ defaultUnixPath_ = strdup(path);
+ else
+ defaultUnixPath_ = NULL;
+}
+
+void
+ChannelEndPoint::disable() {
+ setSpec("0");
+}
+
+bool
+ChannelEndPoint::getPort(long *port) const {
+ if (port) *port = 0;
+ long p = -1;
+ if (spec_) {
+ char *end;
+ p = strtol(spec_, &end, 10);
+ if ((end == spec_) || (*end != '\0'))
+ return false;
+ }
+
+ if (port) *port = p;
+ return true;
+}
+
+bool
+ChannelEndPoint::getUnixPath(char **unixPath) const {
+
+ if (unixPath) *unixPath = 0;
+
+ long p;
+ char *path = NULL;
+
+ if (getPort(&p)) {
+ if (p != 1) return false;
+ }
+ else if (spec_ && (strncmp("unix:", spec_, 5) == 0)) {
+ path = spec_ + 5;
+ }
+ else
+ return false;
+
+ if (!path || (*path == '\0')) {
+ path = defaultUnixPath_;
+ if (!path)
+ return false;
+ }
+
+ if (unixPath)
+ *unixPath = strdup(path);
+
+ return true;
+}
+
+bool
+ChannelEndPoint::isUnixSocket() const {
+ return isUnix_;
+}
+
+// FIXME!!!
+static const char *
+getComputerName() {
+ //
+ // Strangely enough, under some Windows OSes SMB
+ // service doesn't bind to localhost. Fall back
+ // to localhost if can't find computer name in
+ // the environment. In future we should try to
+ // bind to localhost and then try the other IPs.
+ //
+
+ const char *hostname = NULL;
+
+ #ifdef __CYGWIN32__
+
+ hostname = getenv("COMPUTERNAME");
+
+ #endif
+
+ if (hostname == NULL)
+ {
+ hostname = "localhost";
+ }
+
+ return hostname;
+}
+
+bool
+ChannelEndPoint::getTCPHostAndPort(char **host, long *port) const {
+ long p;
+ char *h = NULL;
+ ssize_t h_len;
+
+ if (host) *host = NULL;
+ if (port) *port = 0;
+
+ if (getPort(&p)) {
+ h_len = 0;
+ }
+ else if (spec_ && (strncmp("tcp:", spec_, 4) == 0)) {
+ h = spec_ + 4;
+ char *colon = strrchr(h, ':');
+ if (colon) {
+ char *end;
+ h_len = colon++ - h;
+ p = strtol(colon, &end, 10);
+ if ((end == colon) || (*end != '\0'))
+ return false;
+ }
+ else {
+ h_len = strlen(h);
+ p = 1;
+ }
+ }
+ else
+ return false;
+
+ if (p == 1) p = defaultTCPPort_;
+ if (p < 1) return false;
+
+ if (port)
+ *port = p;
+
+ if (host)
+ *host = ( h_len
+ ? strndup(h, h_len)
+ : strdup(defaultTCPInterface_ ? getComputerName() : "localhost"));
+
+ return true;
+}
+
+bool
+ChannelEndPoint::isTCPSocket() const {
+ return isTCP_;
+}
+
+long ChannelEndPoint::getTCPPort() const {
+ long port;
+ if (getTCPHostAndPort(NULL, &port)) return port;
+ return -1;
+}
+
+bool
+ChannelEndPoint::enabled() const {
+ return (isUnixSocket() || isTCPSocket());
+}
+
+bool
+ChannelEndPoint::validateSpec() {
+ isTCP_ = getTCPHostAndPort();
+ isUnix_ = getUnixPath();
+ return ( getPort() || isUnix_ || isTCP_ );
+}
+
+ChannelEndPoint &ChannelEndPoint::operator=(const ChannelEndPoint &other) {
+ char *old;
+ defaultTCPPort_ = other.defaultTCPPort_;
+ defaultTCPInterface_ = other.defaultTCPInterface_;
+ old = defaultUnixPath_;
+ defaultUnixPath_ = (other.defaultUnixPath_ ? strdup(other.defaultUnixPath_) : NULL);
+ free(old);
+ old = spec_;
+ spec_ = (other.spec_ ? strdup(other.spec_) : NULL);
+ free(old);
+ isUnix_ = getUnixPath();
+ isTCP_ = getTCPHostAndPort();
+ return *this;
+}
+
+std::ostream& operator<<(std::ostream& os, const ChannelEndPoint& endPoint) {
+ if (endPoint.enabled()) {
+ char* endPointSpec = NULL;
+ if (endPoint.getSpec(&endPointSpec))
+ {
+ os << endPointSpec;
+ free(endPointSpec);
+ }
+ else
+ os << "(invalid)";
+ }
+ else
+ {
+ os << "(disabled)";
+ }
+ return os;
+}
diff --git a/nxcomp/src/ChannelEndPoint.h b/nxcomp/src/ChannelEndPoint.h
new file mode 100644
index 000000000..4c0c728f3
--- /dev/null
+++ b/nxcomp/src/ChannelEndPoint.h
@@ -0,0 +1,71 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ChannelEndPoint_H
+#define ChannelEndPoint_H
+
+#include <iostream>
+#include <sys/un.h>
+
+class ChannelEndPoint
+{
+ private:
+ long defaultTCPPort_;
+ int defaultTCPInterface_; // 0=localhost, otherwise IP of public interface.
+ char *defaultUnixPath_;
+ char *spec_;
+ bool isUnix_;
+ bool isTCP_;
+
+ bool getPort(long *port = NULL) const;
+
+ public:
+ ChannelEndPoint(const char *spec = NULL);
+ ~ChannelEndPoint();
+ ChannelEndPoint &operator=(const ChannelEndPoint &other);
+
+ bool enabled() const;
+ bool disabled() { return !enabled(); }
+ void disable();
+ void setSpec(const char *spec);
+ void setSpec(long port);
+ void setSpec(const char *hostName, long port);
+ bool getSpec(char **socketUri) const;
+ void setDefaultTCPPort(long port);
+ void setDefaultTCPInterface(int publicInterface);
+ void setDefaultUnixPath(char *path);
+
+ bool getUnixPath(char **path = NULL) const;
+ bool isUnixSocket() const;
+ bool getTCPHostAndPort(char **hostname = NULL, long *port = NULL) const;
+ long getTCPPort() const;
+ bool isTCPSocket() const;
+
+ bool validateSpec();
+};
+
+std::ostream& operator<<(std::ostream& os, const ChannelEndPoint& endPoint);
+
+#endif
diff --git a/nxcomp/src/ChannelStore.h b/nxcomp/src/ChannelStore.h
new file mode 100644
index 000000000..53bb60f73
--- /dev/null
+++ b/nxcomp/src/ChannelStore.h
@@ -0,0 +1,54 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ChannelStore_H
+#define ChannelStore_H
+
+//
+// One message store for each opcode.
+//
+
+#define CHANNEL_STORE_OPCODE_LIMIT 256
+
+//
+// One split store for each resource.
+//
+
+#define CHANNEL_STORE_RESOURCE_LIMIT 256
+
+class ChannelStore
+{
+ public:
+
+ ChannelStore()
+ {
+ }
+
+ virtual ~ChannelStore()
+ {
+ }
+};
+
+#endif /* ChannelStore_H */
diff --git a/nxcomp/src/CharCache.cpp b/nxcomp/src/CharCache.cpp
new file mode 100644
index 000000000..ed0e5a02a
--- /dev/null
+++ b/nxcomp/src/CharCache.cpp
@@ -0,0 +1,73 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "CharCache.h"
+
+int CharCache::lookup(unsigned char value, unsigned int &index)
+{
+ for (unsigned int i = 0; i < length_; i++)
+ if (value == buffer_[i])
+ {
+ index = i;
+ if (i)
+ {
+ unsigned int target = (i >> 1);
+ do
+ {
+ buffer_[i] = buffer_[i - 1];
+ i--;
+ }
+ while (i > target);
+ buffer_[target] = value;
+ }
+ return 1;
+ }
+ insert(value);
+ return 0;
+}
+
+void CharCache::insert(unsigned char value)
+{
+ unsigned int insertionPoint = 0;
+ if (2 >= length_)
+ insertionPoint = length_;
+ else
+ insertionPoint = 2;
+ unsigned int start;
+ if (length_ >= 7)
+ start = 7 - 1;
+ else
+ {
+ start = length_;
+ length_++;
+ }
+ for (unsigned int k = start; k > insertionPoint; k--)
+ buffer_[k] = buffer_[k - 1];
+ buffer_[insertionPoint] = value;
+}
diff --git a/nxcomp/src/CharCache.h b/nxcomp/src/CharCache.h
new file mode 100644
index 000000000..b8891d2df
--- /dev/null
+++ b/nxcomp/src/CharCache.h
@@ -0,0 +1,91 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef CharCache_H
+#define CharCache_H
+
+//
+// CharCache is a counterpart of IntCache that is
+// optimized for use in compressing text composed
+// of 8-bit characters.
+//
+
+class CharCache
+{
+ public:
+
+ CharCache() : length_(0)
+ {
+ }
+
+ ~CharCache()
+ {
+ }
+
+ unsigned int getSize() const
+ {
+ return (unsigned int) length_;
+ }
+
+ int lookup(unsigned char value, unsigned int &index);
+
+ //
+ // This can be inlined as it is only
+ // called by decodeCachedValue().
+ //
+
+ unsigned int get(unsigned int index)
+ {
+ unsigned char result = buffer_[index];
+
+ if (index != 0)
+ {
+ unsigned int i = index;
+ unsigned int target = (i >> 1);
+
+ do
+ {
+ buffer_[i] = buffer_[i - 1];
+
+ i--;
+ }
+ while (i > target);
+
+ buffer_[target] = result;
+ }
+
+ return (unsigned int) result;
+ }
+
+ void insert(unsigned char value);
+
+ private:
+
+ unsigned char length_;
+
+ unsigned char buffer_[7];
+};
+
+#endif /* CharCache_H */
diff --git a/nxcomp/src/Children.cpp b/nxcomp/src/Children.cpp
new file mode 100644
index 000000000..e77d1defd
--- /dev/null
+++ b/nxcomp/src/Children.cpp
@@ -0,0 +1,1059 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "NX.h"
+
+#include "Misc.h"
+
+#include "Types.h"
+#include "Timestamp.h"
+
+#include "Control.h"
+#include "Statistics.h"
+#include "Proxy.h"
+
+#include "Keeper.h"
+#include "Fork.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#define DISPLAY_LENGTH_LIMIT 256
+#define DEFAULT_STRING_LIMIT 512
+
+//
+// These are from the main loop.
+//
+
+extern Keeper *keeper;
+
+extern int (*signalHandler)(int);
+
+extern int useUnixSocket;
+
+extern int lastDialog;
+extern int lastWatchdog;
+extern int lastKeeper;
+
+extern void CleanupListeners();
+extern void CleanupSockets();
+extern void CleanupAgent();
+extern void CleanupGlobal();
+
+extern void InstallSignals();
+
+extern char *GetClientPath();
+
+extern int CheckParent(const char *name, const char *type,
+ int parent);
+
+#ifdef __sun
+extern char **environ;
+#endif
+
+//
+// Close all the unused descriptors and
+// install any signal handler that might
+// have been disabled in the main process.
+//
+
+static void SystemCleanup(const char *name);
+
+//
+// Release all objects allocated in the
+// heap.
+
+static void MemoryCleanup(const char *name);
+
+//
+// Remove 'name' from the environment.
+//
+
+static int UnsetEnv(const char *name);
+
+static int NXTransKeeperHandler(int signal);
+static void NXTransKeeperCheck();
+
+
+//
+// Start a nxclient process in dialog mode.
+//
+
+int NXTransDialog(const char *caption, const char *message,
+ const char *window, const char *type, int local,
+ const char* display)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ int pid;
+
+ #ifdef TEST
+ *logofs << "NXTransDialog: Going to fork with NX pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ pid = Fork();
+
+ if (pid != 0)
+ {
+ if (pid < 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransDialog: WARNING! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransDialog: Created NX dialog process "
+ << "with pid '" << pid << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return pid;
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransDialog: Executing child with pid '"
+ << getpid() << "' and parent '" << getppid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ SystemCleanup("NXTransDialog");
+
+ //
+ // Copy the client command before
+ // freeing up the control class.
+ //
+
+ char command[DEFAULT_STRING_LIMIT];
+
+ if (control != NULL)
+ {
+ strcpy(command, control -> ClientPath);
+ }
+ else
+ {
+ char *path = GetClientPath();
+
+ strcpy(command, path);
+
+ delete [] path;
+ }
+
+ //
+ // Get rid of the unused resources.
+ //
+
+ MemoryCleanup("NXTransDialog");
+
+ #ifdef TEST
+ *logofs << "NXTransDialog: Running external NX dialog with caption '"
+ << caption << "' message '" << message << "' type '"
+ << type << "' local '" << local << "' display '"
+ << display << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int pulldown = (strcmp(type, "pulldown") == 0);
+
+ char parent[DEFAULT_STRING_LIMIT];
+
+ snprintf(parent, DEFAULT_STRING_LIMIT, "%d", getppid());
+
+ parent[DEFAULT_STRING_LIMIT - 1] = '\0';
+
+ UnsetEnv("LD_LIBRARY_PATH");
+
+ for (int i = 0; i < 2; i++)
+ {
+ if (local != 0)
+ {
+ if (pulldown)
+ {
+ execlp(command, command, "--dialog", type, "--caption", caption,
+ "--window", window, "--local", "--parent", parent,
+ "--display", display, NULL);
+ }
+ else
+ {
+ execlp(command, command, "--dialog", type, "--caption", caption,
+ "--message", message, "--local", "--parent", parent,
+ "--display", display, NULL);
+ }
+ }
+ else
+ {
+ if (pulldown)
+ {
+ execlp(command, command, "--dialog", type, "--caption", caption,
+ "--window", window, "--parent", parent,
+ "--display", display, NULL);
+ }
+ else
+ {
+ execlp(command, command, "--dialog", type, "--caption", caption,
+ "--message", message, "--parent", parent,
+ "--display", display, NULL);
+ }
+ }
+
+ #ifdef WARNING
+ *logofs << "NXTransDialog: WARNING! Couldn't start '"
+ << command << "'. " << "Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Couldn't start '" << command
+ << "'. Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ //
+ // Retry by looking for the default name
+ // in the default NX path.
+ //
+
+ if (i == 0)
+ {
+
+ strcpy(command, "nxclient");
+
+ char newPath[DEFAULT_STRING_LIMIT];
+
+ strcpy(newPath, "/usr/NX/bin:/opt/NX/bin:/usr/local/NX/bin:");
+
+ #ifdef __APPLE__
+
+ strcat(newPath, "/Applications/NX Client for OSX.app/Contents/MacOS:");
+
+ #endif
+
+ #ifdef __CYGWIN32__
+
+ strcat(newPath, ".:");
+
+ #endif
+
+ int newLength = strlen(newPath);
+
+ char *oldPath = getenv("PATH");
+
+ strncpy(newPath + newLength, oldPath, DEFAULT_STRING_LIMIT - newLength - 1);
+
+ newPath[DEFAULT_STRING_LIMIT - 1] = '\0';
+
+ #ifdef WARNING
+ *logofs << "NXTransDialog: WARNING! Trying with path '"
+ << newPath << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Trying with path '" << newPath
+ << "'.\n";
+
+ //
+ // Solaris doesn't seem to have
+ // function setenv().
+ //
+
+ #ifdef __sun
+
+ char newEnv[DEFAULT_STRING_LIMIT + 5];
+
+ sprintf(newEnv,"PATH=%s", newPath);
+
+ putenv(newEnv);
+
+ #else
+
+ setenv("PATH", newPath, 1);
+
+ #endif
+
+ }
+ }
+
+ //
+ // Hopefully useless.
+ //
+
+ exit(0);
+}
+
+//
+// Start a nxclient process in dialog mode.
+//
+
+int NXTransClient(const char* display)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ int pid;
+
+ #ifdef TEST
+ *logofs << "NXTransClient: Going to fork with NX pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ pid = Fork();
+
+ if (pid != 0)
+ {
+ if (pid < 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransClient: WARNING! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransClient: Created NX client process "
+ << "with pid '" << pid << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return pid;
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransClient: Executing child with pid '"
+ << getpid() << "' and parent '" << getppid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ SystemCleanup("NXTransClient");
+
+ //
+ // Copy the client command before
+ // freeing up the control class.
+ //
+
+ char command[DEFAULT_STRING_LIMIT];
+
+ if (control != NULL)
+ {
+ strcpy(command, control -> ClientPath);
+ }
+ else
+ {
+ char *path = GetClientPath();
+
+ strcpy(command, path);
+
+ delete [] path;
+ }
+
+ //
+ // Get rid of unused resources.
+ //
+
+ MemoryCleanup("NXTransClient");
+
+ #ifdef TEST
+ *logofs << "NXTransClient: Running external NX client with display '"
+ << display << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Provide the display in the environment.
+ //
+
+ char newDisplay[DISPLAY_LENGTH_LIMIT];
+
+ #ifdef __sun
+
+ snprintf(newDisplay, DISPLAY_LENGTH_LIMIT - 1, "DISPLAY=%s", display);
+
+ newDisplay[DISPLAY_LENGTH_LIMIT - 1] = '\0';
+
+ putenv(newDisplay);
+
+ #else
+
+ strncpy(newDisplay, display, DISPLAY_LENGTH_LIMIT - 1);
+
+ newDisplay[DISPLAY_LENGTH_LIMIT - 1] = '\0';
+
+ setenv("DISPLAY", newDisplay, 1);
+
+ #endif
+
+ UnsetEnv("LD_LIBRARY_PATH");
+
+ for (int i = 0; i < 2; i++)
+ {
+ execlp(command, command, NULL);
+
+ #ifdef WARNING
+ *logofs << "NXTransClient: WARNING! Couldn't start '"
+ << command << "'. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Couldn't start '" << command
+ << "'. Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ //
+ // Retry by looking for the default name
+ // in the default NX path.
+ //
+
+ if (i == 0)
+ {
+
+ strcpy(command, "nxclient");
+
+ char newPath[DEFAULT_STRING_LIMIT];
+
+ strcpy(newPath, "/usr/NX/bin:/opt/NX/bin:/usr/local/NX/bin:");
+
+ #ifdef __APPLE__
+
+ strcat(newPath, "/Applications/NX Client for OSX.app/Contents/MacOS:");
+
+ #endif
+
+ #ifdef __CYGWIN32__
+
+ strcat(newPath, ".:");
+
+ #endif
+
+ int newLength = strlen(newPath);
+
+ char *oldPath = getenv("PATH");
+
+ strncpy(newPath + newLength, oldPath, DEFAULT_STRING_LIMIT - newLength - 1);
+
+ newPath[DEFAULT_STRING_LIMIT - 1] = '\0';
+
+ #ifdef WARNING
+ *logofs << "NXTransClient: WARNING! Trying with path '"
+ << newPath << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Trying with path '" << newPath
+ << "'.\n";
+
+ //
+ // Solaris doesn't seem to have
+ // function setenv().
+ //
+
+ #ifdef __sun
+
+ char newEnv[DEFAULT_STRING_LIMIT + 5];
+
+ sprintf(newEnv,"PATH=%s", newPath);
+
+ putenv(newEnv);
+
+ #else
+
+ setenv("PATH", newPath, 1);
+
+ #endif
+ }
+
+ }
+ //
+ // Hopefully useless.
+ //
+
+ exit(0);
+}
+
+//
+// Wait until the timeout is expired.
+// The timeout is expressed in milli-
+// seconds.
+//
+
+int NXTransWatchdog(int timeout)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ int pid;
+
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Going to fork with NX pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ pid = Fork();
+
+ if (pid != 0)
+ {
+ if (pid < 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: WARNING! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransWatchdog: Created NX watchdog process "
+ << "with pid '" << pid << "'.\n" << logofs_flush;
+ }
+ #endif
+
+ return pid;
+ }
+
+ int parent = getppid();
+
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Executing child with pid '"
+ << getpid() << "' and parent '" << parent
+ << "'.\n" << logofs_flush;
+ #endif
+
+ SystemCleanup("NXTransWatchdog");
+
+ //
+ // Get rid of unused resources.
+ //
+
+ MemoryCleanup("NXTransWatchdog");
+
+ //
+ // Run until the timeout is expired
+ // or forever, if no timeout is
+ // provided.
+ //
+
+ T_timestamp startTs = getTimestamp();
+
+ int diffTs = 0;
+
+ for (;;)
+ {
+ //
+ // Complain if the parent is dead.
+ //
+
+ if (CheckParent("NXTransWatchdog", "watchdog", parent) == 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Exiting with no parent "
+ << "running.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ if (timeout > 0)
+ {
+ if (diffTs >= timeout)
+ {
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Timeout of " << timeout
+ << " Ms raised in watchdog.\n" << logofs_flush;
+ #endif
+
+ //
+ // We will just exit. Our parent should be
+ // monitoring us and detect that the process
+ // is gone.
+ //
+
+ HandleCleanup();
+ }
+ }
+
+ if (timeout > 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Waiting for the timeout "
+ << "with " << timeout - diffTs << " Ms to run.\n"
+ << logofs_flush;
+ #endif
+
+ usleep((timeout - diffTs) * 1000);
+
+ diffTs = diffTimestamp(startTs, getNewTimestamp());
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "NXTransWatchdog: Waiting for a signal.\n"
+ << logofs_flush;
+ #endif
+
+ sleep(10);
+ }
+ }
+
+ //
+ // Hopefully useless.
+ //
+
+ exit(0);
+}
+
+int NXTransKeeperHandler(int signal)
+{
+ if (keeper != NULL)
+ {
+ switch (signal)
+ {
+ case SIGTERM:
+ case SIGINT:
+ case SIGHUP:
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeperHandler: Requesting giveup "
+ << "because of signal " << signal << " ,'"
+ << DumpSignal(signal) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ keeper -> setSignal(signal);
+
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+void NXTransKeeperCheck()
+{
+ if (CheckParent("NXTransKeeper", "keeper",
+ keeper -> getParent()) == 0 || keeper -> getSignal() != 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeperCheck: Exiting because of signal "
+ << "or no parent running.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+}
+
+int NXTransKeeper(int caches, int images, const char *root)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (caches == 0 && images == 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: No NX cache house-keeping needed.\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ int pid;
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to fork with NX pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ pid = Fork();
+
+ if (pid != 0)
+ {
+ if (pid < 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: WARNING! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransKeeper: Created NX keeper process "
+ << "with pid '" << pid << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return pid;
+ }
+
+ int parent = getppid();
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Executing child with pid '"
+ << getpid() << "' and parent '" << parent
+ << "'.\n" << logofs_flush;
+ #endif
+
+ SystemCleanup("NXTransKeeper");
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to run with caches " << caches
+ << " images " << images << " and root " << root
+ << " at " << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Create the house-keeper class.
+ //
+
+ int timeout = control -> KeeperTimeout;
+
+ keeper = new Keeper(caches, images, root, 100, parent);
+
+ signalHandler = NXTransKeeperHandler;
+
+ if (keeper == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "NXTransKeeper: PANIC! Failed to create the keeper object.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to create the keeper object.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Get rid of unused resources. Root path
+ // must be copied in keeper's constructor
+ // before control is deleted.
+ //
+
+ MemoryCleanup("NXTransKeeper");
+
+ //
+ // Decrease the priority of this process.
+ //
+ // The following applies to Cygwin: "Cygwin processes can be
+ // set to IDLE_PRIORITY_CLASS, NORMAL_PRIORITY_CLASS, HIGH_-
+ // PRIORITY_CLASS, or REALTIME_PRIORITY_CLASS with the nice
+ // call. If you pass a positive number to nice(), then the
+ // priority level will decrease by one (within the above list
+ // of priorities). A negative number would make it increase
+ // by one. It is not possible to change it by more than one
+ // at a time without making repeated calls".
+ //
+
+ if (nice(5) < 0 && errno != 0)
+ {
+ #ifdef WARNING
+ *logofs << "NXTransKeeper: WARNING! Failed to renice process to +5. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to renice process to +5. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+
+ //
+ // Delay a bit the first run to give
+ // a boost to the session startup.
+ //
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to sleep for "
+ << timeout / 20 << " Ms.\n" << logofs_flush;
+ #endif
+
+ usleep(timeout / 20 * 1000);
+
+ NXTransKeeperCheck();
+
+ //
+ // The house keeping of the persistent
+ // caches is performed only once.
+ //
+
+ if (caches != 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to cleanup the NX cache "
+ << "directories at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ keeper -> cleanupCaches();
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Completed cleanup of NX cache "
+ << "directories at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransKeeper: Nothing to do for the "
+ << "persistent caches.\n" << logofs_flush;
+ }
+ #endif
+
+ if (images == 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Nothing to do for the "
+ << "persistent images.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ //
+ // Take care of the persisten image cache.
+ // Run a number of iterations and then exit,
+ // so we can keep the memory consumption
+ // low. The parent will check our exit code
+ // and will eventually restart us.
+ //
+
+ for (int iterations = 0; iterations < 100; iterations++)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Running iteration " << iterations
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ NXTransKeeperCheck();
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to cleanup the NX images "
+ << "directories at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (keeper -> cleanupImages() < 0)
+ {
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Exiting because of error "
+ << "handling the image cache.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Completed cleanup of NX images "
+ << "directories at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ NXTransKeeperCheck();
+
+ #ifdef TEST
+ *logofs << "NXTransKeeper: Going to sleep for " << timeout
+ << " Ms.\n" << logofs_flush;
+ #endif
+
+ usleep(timeout * 1000);
+ }
+
+ HandleCleanup(2);
+
+ //
+ // Hopefully useless.
+ //
+
+ exit(0);
+}
+
+void SystemCleanup(const char *name)
+{
+ #ifdef TEST
+ *logofs << name << ": Performing system cleanup in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Reinstall signals that might
+ // have been restored by agents.
+ //
+
+ InstallSignals();
+}
+
+void MemoryCleanup(const char *name)
+{
+ #ifdef TEST
+ *logofs << name << ": Performing memory cleanup in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ DisableSignals();
+
+ //
+ // Prevent deletion of unix socket
+ // and lock file.
+ //
+
+ useUnixSocket = 0;
+
+ //
+ // Don't let cleanup kill other
+ // children.
+ //
+
+ lastDialog = 0;
+ lastWatchdog = 0;
+ lastKeeper = 0;
+
+ CleanupListeners();
+
+ CleanupSockets();
+
+ CleanupGlobal();
+
+ EnableSignals();
+}
+
+int UnsetEnv(const char *name)
+{
+ int result;
+
+ #ifdef __sun
+
+ char **pEnv = environ;
+
+ int nameLen = strlen(name) + 1;
+
+ char *varName = new char[nameLen + 1];
+
+ strcpy(varName, name);
+
+ strcat(varName, "=");
+
+ pEnv = environ;
+
+ while (*pEnv != NULL)
+ {
+ if (!strncmp(varName, *pEnv, nameLen))
+ {
+ break;
+ }
+
+ *pEnv++;
+ }
+
+ while (*pEnv != NULL)
+ {
+ *pEnv = *(pEnv + 1);
+
+ pEnv++;
+ }
+
+ result = 0;
+
+ #else
+
+ #ifdef __APPLE__
+
+ unsetenv(name);
+ result = 0;
+
+ #else
+
+ result = unsetenv(name);
+
+ #endif
+
+ #endif
+
+ return result;
+}
diff --git a/nxcomp/src/ClearArea.cpp b/nxcomp/src/ClearArea.cpp
new file mode 100644
index 000000000..0cb152d95
--- /dev/null
+++ b/nxcomp/src/ClearArea.cpp
@@ -0,0 +1,125 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ClearArea.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ClearAreaStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ClearAreaMessage *clearArea = (ClearAreaMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ clearArea -> exposures = *(buffer + 1);
+
+ clearArea -> window = GetULONG(buffer + 4, bigEndian);
+
+ clearArea -> x = GetUINT(buffer + 8, bigEndian);
+ clearArea -> y = GetUINT(buffer + 10, bigEndian);
+ clearArea -> width = GetUINT(buffer + 12, bigEndian);
+ clearArea -> height = GetUINT(buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ClearAreaStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ClearAreaMessage *clearArea = (ClearAreaMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = clearArea -> exposures;
+
+ PutULONG(clearArea -> window, buffer + 4, bigEndian);
+
+ PutUINT(clearArea -> x, buffer + 8, bigEndian);
+ PutUINT(clearArea -> y, buffer + 10, bigEndian);
+ PutUINT(clearArea -> width, buffer + 12, bigEndian);
+ PutUINT(clearArea -> height, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ClearAreaStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ClearAreaMessage *clearArea = (ClearAreaMessage *) message;
+
+ *logofs << name() << ": Identity exposures " << (unsigned int) clearArea -> exposures
+ << ", window " << clearArea -> window << ", x " << clearArea -> x
+ << ", y " << clearArea -> y << ", width " << clearArea -> width
+ << ", height " << clearArea -> height << ", size " << clearArea -> size_
+ << ".\n";
+
+ #endif
+}
+
+void ClearAreaStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 4, 4);
+ md5_append(md5_state_, buffer + 8, 2);
+ md5_append(md5_state_, buffer + 10, 2);
+ md5_append(md5_state_, buffer + 12, 2);
+ md5_append(md5_state_, buffer + 14, 2);
+}
diff --git a/nxcomp/src/ClearArea.h b/nxcomp/src/ClearArea.h
new file mode 100644
index 000000000..8067edffd
--- /dev/null
+++ b/nxcomp/src/ClearArea.h
@@ -0,0 +1,182 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ClearArea_H
+#define ClearArea_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CLEARAREA_ENABLE_CACHE 1
+#define CLEARAREA_ENABLE_DATA 0
+#define CLEARAREA_ENABLE_SPLIT 0
+#define CLEARAREA_ENABLE_COMPRESS 0
+
+#define CLEARAREA_DATA_LIMIT 0
+#define CLEARAREA_DATA_OFFSET 16
+
+#define CLEARAREA_CACHE_SLOTS 3000
+#define CLEARAREA_CACHE_THRESHOLD 5
+#define CLEARAREA_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ClearAreaMessage : public Message
+{
+ friend class ClearAreaStore;
+
+ public:
+
+ ClearAreaMessage()
+ {
+ }
+
+ ~ClearAreaMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char exposures;
+ unsigned int window;
+ unsigned short x;
+ unsigned short y;
+ unsigned short width;
+ unsigned short height;
+};
+
+class ClearAreaStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ClearAreaStore() : MessageStore()
+ {
+ enableCache = CLEARAREA_ENABLE_CACHE;
+ enableData = CLEARAREA_ENABLE_DATA;
+ enableSplit = CLEARAREA_ENABLE_SPLIT;
+ enableCompress = CLEARAREA_ENABLE_COMPRESS;
+
+ dataLimit = CLEARAREA_DATA_LIMIT;
+ dataOffset = CLEARAREA_DATA_OFFSET;
+
+ cacheSlots = CLEARAREA_CACHE_SLOTS;
+ cacheThreshold = CLEARAREA_CACHE_THRESHOLD;
+ cacheLowerThreshold = CLEARAREA_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ClearAreaStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ClearArea";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ClearArea;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ClearAreaMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ClearAreaMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ClearAreaMessage((const ClearAreaMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ClearAreaMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ClearArea_H */
diff --git a/nxcomp/src/ClientCache.cpp b/nxcomp/src/ClientCache.cpp
new file mode 100644
index 000000000..47bb7db1d
--- /dev/null
+++ b/nxcomp/src/ClientCache.cpp
@@ -0,0 +1,392 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ClientCache.h"
+
+ClientCache::ClientCache() :
+
+ freeGCCache(16), freeDrawableCache(16), freeWindowCache(16),
+
+ cursorCache(16), colormapCache(16), visualCache(16), lastFont(0),
+
+ changePropertyPropertyCache(16), changePropertyTypeCache(16),
+ changePropertyData32Cache(16),
+
+ configureWindowBitmaskCache(4),
+
+ convertSelectionRequestorCache(16),
+ convertSelectionLastTimestamp(0),
+
+ copyPlaneBitPlaneCache(8),
+
+ createGCBitmaskCache(8),
+
+ createPixmapIdCache(16), createPixmapLastId(0),
+ createPixmapXCache(8), createPixmapYCache(8),
+
+ createWindowBitmaskCache(8),
+
+ fillPolyNumPointsCache(8), fillPolyIndex(0),
+
+ getSelectionOwnerSelectionCache(8),
+
+ grabButtonEventMaskCache(8), grabButtonConfineCache(8),
+ grabButtonModifierCache(8),
+
+ grabKeyboardLastTimestamp(0),
+
+ imageTextLengthCache(8),
+ imageTextLastX(0), imageTextLastY(0),
+ imageTextCacheX(8), imageTextCacheY(8),
+
+ polySegmentCacheX(8), polySegmentCacheY(8), polySegmentCacheIndex(0),
+
+ polyTextLastX(0), polyTextLastY(0), polyTextCacheX(8),
+ polyTextCacheY(8), polyTextFontCache(8),
+
+ putImageWidthCache(8), putImageHeightCache(8), putImageLastX(0),
+ putImageLastY(0), putImageXCache(8), putImageYCache(8),
+
+ getImagePlaneMaskCache(8),
+
+ queryColorsLastPixel(0),
+
+ setClipRectanglesXCache(8), setClipRectanglesYCache(8),
+
+ setDashesLengthCache(8), setDashesOffsetCache(8),
+
+ setSelectionOwnerCache(8), setSelectionOwnerTimestampCache(8),
+
+ translateCoordsSrcCache(8), translateCoordsDstCache(8),
+ translateCoordsXCache(8), translateCoordsYCache(8),
+
+ sendEventMaskCache(16), sendEventLastSequence(0),
+ sendEventIntDataCache(16),
+
+ putPackedImageSrcLengthCache(16), putPackedImageDstLengthCache(16),
+
+ //
+ // RenderExtension requests.
+ //
+
+ renderFreePictureCache(16),
+
+ renderGlyphSetCache(16),
+ renderFreeGlyphSetCache(16),
+
+ renderIdCache(8),
+
+ renderLengthCache(16), renderFormatCache(16),
+ renderValueMaskCache(8), renderNumGlyphsCache(8),
+
+ renderXCache(16), renderYCache(16),
+ renderLastX(0), renderLastY(0),
+
+ renderWidthCache(16), renderHeightCache(16),
+
+ renderLastId(0),
+
+ renderGlyphXCache(16), renderGlyphYCache(16),
+ renderGlyphX(0), renderGlyphY(0),
+
+ renderLastCompositeGlyphsData(0),
+
+ setCacheParametersCache(8),
+
+ lastIdCache(16), lastId(0)
+
+{
+ unsigned int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ allocColorRGBCache[i] = new IntCache(8);
+
+ convertSelectionAtomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ clearAreaGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 7; i++)
+ {
+ configureWindowAttrCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ copyAreaGeomCache[i] = new IntCache(8);
+ copyPlaneGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 23; i++)
+ {
+ if (CREATEGC_FIELD_WIDTH[i] > 16)
+ {
+ createGCAttrCache[i] = new IntCache(16);
+ }
+ else
+ {
+ createGCAttrCache[i] = new IntCache(CREATEGC_FIELD_WIDTH[i]);
+ }
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ createWindowGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 15; i++)
+ {
+ createWindowAttrCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 10; i++)
+ {
+ fillPolyXRelCache[i] = new IntCache(8);
+ fillPolyXAbsCache[i] = new IntCache(8);
+ fillPolyYRelCache[i] = new IntCache(8);
+ fillPolyYAbsCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ fillPolyRecentX[i] = 0;
+ fillPolyRecentY[i] = 0;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ polyFillRectangleCacheX[i] = new IntCache(8);
+ polyFillRectangleCacheY[i] = new IntCache(8);
+ polyFillRectangleCacheWidth[i] = new IntCache(8);
+ polyFillRectangleCacheHeight[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ polyLineCacheX[i] = new IntCache(8);
+ polyLineCacheY[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ polyPointCacheX[i] = new IntCache(8);
+ polyPointCacheY[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ polyRectangleGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ polySegmentLastX[i] = 0;
+ polySegmentLastY[i] = 0;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ setClipRectanglesGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ polyFillArcCacheX[i] = new IntCache(8);
+ polyFillArcCacheY[i] = new IntCache(8);
+ polyFillArcCacheWidth[i] = new IntCache(8);
+ polyFillArcCacheHeight[i] = new IntCache(8);
+ polyFillArcCacheAngle1[i] = new IntCache(8);
+ polyFillArcCacheAngle2[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ polyArcCacheX[i] = new IntCache(8);
+ polyArcCacheY[i] = new IntCache(8);
+ polyArcCacheWidth[i] = new IntCache(8);
+ polyArcCacheHeight[i] = new IntCache(8);
+ polyArcCacheAngle1[i] = new IntCache(8);
+ polyArcCacheAngle2[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ shapeDataCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ genericRequestDataCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ renderDataCache[i] = new IntCache(16);
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ renderCompositeGlyphsDataCache[i] = new IntCache(16);
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ renderCompositeDataCache[i] = new IntCache(16);
+ }
+}
+
+
+ClientCache::~ClientCache()
+{
+ unsigned int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ delete allocColorRGBCache[i];
+ delete convertSelectionAtomCache[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ delete clearAreaGeomCache[i];
+ }
+
+ for (i = 0; i < 7; i++)
+ {
+ delete configureWindowAttrCache[i];
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ delete copyAreaGeomCache[i];
+ delete copyPlaneGeomCache[i];
+ }
+
+ for (i = 0; i < 23; i++)
+ {
+ delete createGCAttrCache[i];
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ delete createWindowGeomCache[i];
+ }
+
+ for (i = 0; i < 15; i++)
+ {
+ delete createWindowAttrCache[i];
+ }
+
+ for (i = 0; i < 10; i++)
+ {
+ delete fillPolyXRelCache[i];
+ delete fillPolyXAbsCache[i];
+ delete fillPolyYRelCache[i];
+ delete fillPolyYAbsCache[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ delete polyFillRectangleCacheX[i];
+ delete polyFillRectangleCacheY[i];
+ delete polyFillRectangleCacheWidth[i];
+ delete polyFillRectangleCacheHeight[i];
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ delete polyLineCacheX[i];
+ delete polyLineCacheY[i];
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ delete polyPointCacheX[i];
+ delete polyPointCacheY[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ delete polyRectangleGeomCache[i];
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ delete setClipRectanglesGeomCache[i];
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ delete polyFillArcCacheX[i];
+ delete polyFillArcCacheY[i];
+ delete polyFillArcCacheWidth[i];
+ delete polyFillArcCacheHeight[i];
+ delete polyFillArcCacheAngle1[i];
+ delete polyFillArcCacheAngle2[i];
+ }
+
+ for (i = 0; i < 2; i++)
+ {
+ delete polyArcCacheX[i];
+ delete polyArcCacheY[i];
+ delete polyArcCacheWidth[i];
+ delete polyArcCacheHeight[i];
+ delete polyArcCacheAngle1[i];
+ delete polyArcCacheAngle2[i];
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ delete shapeDataCache[i];
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ delete genericRequestDataCache[i];
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ delete renderDataCache[i];
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ delete renderCompositeGlyphsDataCache[i];
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ delete renderCompositeDataCache[i];
+ }
+}
diff --git a/nxcomp/src/ClientCache.h b/nxcomp/src/ClientCache.h
new file mode 100644
index 000000000..ed3361097
--- /dev/null
+++ b/nxcomp/src/ClientCache.h
@@ -0,0 +1,417 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ClientCache_H
+#define ClientCache_H
+
+#include "Misc.h"
+
+#include "IntCache.h"
+#include "CharCache.h"
+#include "OpcodeCache.h"
+#include "XidCache.h"
+#include "FreeCache.h"
+
+#include "ChannelCache.h"
+
+class ClientCache : public ChannelCache
+{
+ public:
+
+ ClientCache();
+
+ ~ClientCache();
+
+ //
+ // Opcode prediction caches.
+ //
+
+ OpcodeCache opcodeCache;
+
+ //
+ // GC and drawables caches.
+ //
+
+ XidCache gcCache;
+ FreeCache freeGCCache;
+
+ XidCache drawableCache;
+ FreeCache freeDrawableCache;
+
+ XidCache windowCache;
+ FreeCache freeWindowCache;
+
+ //
+ // General-purpose caches.
+ //
+
+ IntCache cursorCache;
+ IntCache colormapCache;
+ IntCache visualCache;
+ CharCache depthCache;
+ CharCache resourceCache;
+ CharCache methodCache;
+
+ unsigned int lastFont;
+
+ //
+ // AllocColor request.
+ //
+
+ IntCache *allocColorRGBCache[3];
+
+ //
+ // ChangeProperty request.
+ //
+
+ CharCache changePropertyFormatCache;
+ IntCache changePropertyPropertyCache;
+ IntCache changePropertyTypeCache;
+ IntCache changePropertyData32Cache;
+
+ //
+ // ClearArea request.
+ //
+
+ IntCache *clearAreaGeomCache[4];
+
+ //
+ // ConfigureWindow request.
+ //
+
+ IntCache configureWindowBitmaskCache;
+ IntCache *configureWindowAttrCache[7];
+
+ //
+ // ConvertSelection request.
+ //
+
+ IntCache convertSelectionRequestorCache;
+ IntCache* convertSelectionAtomCache[3];
+ unsigned int convertSelectionLastTimestamp;
+
+ //
+ // CopyArea request.
+ //
+
+ IntCache *copyAreaGeomCache[6];
+
+ //
+ // CopyPlane request.
+ //
+
+ IntCache *copyPlaneGeomCache[6];
+ IntCache copyPlaneBitPlaneCache;
+
+ //
+ // CreateGC request.
+ //
+
+ IntCache createGCBitmaskCache;
+ IntCache *createGCAttrCache[23];
+
+ //
+ // CreatePixmap request.
+ //
+
+ IntCache createPixmapIdCache;
+ unsigned int createPixmapLastId;
+ IntCache createPixmapXCache;
+ IntCache createPixmapYCache;
+
+ //
+ // CreateWindow request.
+ //
+
+ IntCache *createWindowGeomCache[6];
+ IntCache createWindowBitmaskCache;
+ IntCache *createWindowAttrCache[15];
+
+ //
+ // FillPoly request.
+ //
+
+ IntCache fillPolyNumPointsCache;
+ IntCache *fillPolyXRelCache[10];
+ IntCache *fillPolyXAbsCache[10];
+ IntCache *fillPolyYRelCache[10];
+ IntCache *fillPolyYAbsCache[10];
+ unsigned int fillPolyRecentX[8];
+ unsigned int fillPolyRecentY[8];
+ unsigned int fillPolyIndex;
+
+ //
+ // GetSelectionOwner request.
+ //
+
+ IntCache getSelectionOwnerSelectionCache;
+
+ //
+ // GrabButton request (also used for GrabPointer).
+ //
+
+ IntCache grabButtonEventMaskCache;
+ IntCache grabButtonConfineCache;
+ CharCache grabButtonButtonCache;
+ IntCache grabButtonModifierCache;
+
+ //
+ // GrabKeyboard request.
+ //
+
+ unsigned int grabKeyboardLastTimestamp;
+
+ //
+ // ImageText8/16 request.
+ //
+
+ IntCache imageTextLengthCache;
+ unsigned int imageTextLastX;
+ unsigned int imageTextLastY;
+ IntCache imageTextCacheX;
+ IntCache imageTextCacheY;
+
+ //
+ // PolyFillRectangle request.
+ //
+
+ IntCache *polyFillRectangleCacheX[4];
+ IntCache *polyFillRectangleCacheY[4];
+ IntCache *polyFillRectangleCacheWidth[4];
+ IntCache *polyFillRectangleCacheHeight[4];
+
+ //
+ // PolyLine request.
+ //
+
+ IntCache *polyLineCacheX[2];
+ IntCache *polyLineCacheY[2];
+
+ //
+ // PolyPoint request.
+ //
+
+ IntCache *polyPointCacheX[2];
+ IntCache *polyPointCacheY[2];
+
+ //
+ // PolyRectangle request.
+ //
+
+ IntCache *polyRectangleGeomCache[4];
+
+ //
+ // PolySegment request.
+ //
+
+ IntCache polySegmentCacheX;
+ IntCache polySegmentCacheY;
+ unsigned int polySegmentLastX[2];
+ unsigned int polySegmentLastY[2];
+ unsigned int polySegmentCacheIndex;
+
+ //
+ // PolyText8/16 request.
+ //
+
+ unsigned int polyTextLastX;
+ unsigned int polyTextLastY;
+ IntCache polyTextCacheX;
+ IntCache polyTextCacheY;
+ IntCache polyTextFontCache;
+ CharCache polyTextDeltaCache;
+
+ //
+ // PutImage request.
+ //
+
+ IntCache putImageWidthCache;
+ IntCache putImageHeightCache;
+ unsigned int putImageLastX;
+ unsigned int putImageLastY;
+ IntCache putImageXCache;
+ IntCache putImageYCache;
+ CharCache putImageLeftPadCache;
+
+ //
+ // GetImage request.
+ //
+
+ IntCache getImagePlaneMaskCache;
+
+ //
+ // QueryColors request.
+ //
+
+ unsigned int queryColorsLastPixel;
+
+ //
+ // SetClipRectangles request.
+ //
+
+ IntCache setClipRectanglesXCache;
+ IntCache setClipRectanglesYCache;
+ IntCache *setClipRectanglesGeomCache[4];
+
+ //
+ // SetDashes request.
+ //
+
+ IntCache setDashesLengthCache;
+ IntCache setDashesOffsetCache;
+ CharCache setDashesDashCache_[2];
+
+ //
+ // SetSelectionOwner request.
+ //
+
+ IntCache setSelectionOwnerCache;
+ IntCache setSelectionOwnerTimestampCache;
+
+ //
+ // TranslateCoords request.
+ //
+
+ IntCache translateCoordsSrcCache;
+ IntCache translateCoordsDstCache;
+ IntCache translateCoordsXCache;
+ IntCache translateCoordsYCache;
+
+ //
+ // SendEvent request.
+ //
+
+ IntCache sendEventMaskCache;
+ CharCache sendEventCodeCache;
+ CharCache sendEventByteDataCache;
+ unsigned int sendEventLastSequence;
+ IntCache sendEventIntDataCache;
+ CharCache sendEventEventCache;
+
+ //
+ // PolyFillArc request.
+ //
+
+ IntCache *polyFillArcCacheX[2];
+ IntCache *polyFillArcCacheY[2];
+ IntCache *polyFillArcCacheWidth[2];
+ IntCache *polyFillArcCacheHeight[2];
+ IntCache *polyFillArcCacheAngle1[2];
+ IntCache *polyFillArcCacheAngle2[2];
+
+ //
+ // PolyArc request.
+ //
+
+ IntCache *polyArcCacheX[2];
+ IntCache *polyArcCacheY[2];
+ IntCache *polyArcCacheWidth[2];
+ IntCache *polyArcCacheHeight[2];
+ IntCache *polyArcCacheAngle1[2];
+ IntCache *polyArcCacheAngle2[2];
+
+ //
+ // PutPackedImage request.
+ //
+
+ IntCache putPackedImageSrcLengthCache;
+ IntCache putPackedImageDstLengthCache;
+
+ //
+ // Shape extension requests.
+ //
+
+ CharCache shapeOpcodeCache;
+ IntCache *shapeDataCache[8];
+
+ //
+ // Generic requests.
+ //
+
+ CharCache genericRequestOpcodeCache;
+ IntCache *genericRequestDataCache[8];
+
+ //
+ // Render extension requests.
+ //
+
+ OpcodeCache renderOpcodeCache;
+
+ CharCache renderOpCache;
+
+ XidCache renderSrcPictureCache;
+ XidCache renderMaskPictureCache;
+ XidCache renderDstPictureCache;
+ FreeCache renderFreePictureCache;
+
+ IntCache renderGlyphSetCache;
+ FreeCache renderFreeGlyphSetCache;
+
+ IntCache renderIdCache;
+ IntCache renderLengthCache;
+ IntCache renderFormatCache;
+ IntCache renderValueMaskCache;
+ IntCache renderNumGlyphsCache;
+
+ IntCache renderXCache;
+ IntCache renderYCache;
+
+ unsigned int renderLastX;
+ unsigned int renderLastY;
+
+ IntCache renderWidthCache;
+ IntCache renderHeightCache;
+
+ unsigned int renderLastId;
+
+ IntCache *renderDataCache[16];
+
+ IntCache renderGlyphXCache;
+ IntCache renderGlyphYCache;
+
+ unsigned int renderGlyphX;
+ unsigned int renderGlyphY;
+
+ IntCache *renderCompositeGlyphsDataCache[16];
+ unsigned int renderLastCompositeGlyphsData;
+
+ IntCache *renderCompositeDataCache[3];
+
+ //
+ // SetCacheParameters request.
+ //
+
+ IntCache setCacheParametersCache;
+
+ //
+ // Encode new XID values based
+ // on the last value encoded.
+ //
+
+ IntCache lastIdCache;
+ unsigned int lastId;
+};
+
+#endif /* ClientCache_H */
diff --git a/nxcomp/src/ClientChannel.cpp b/nxcomp/src/ClientChannel.cpp
new file mode 100644
index 000000000..f01ad6d7b
--- /dev/null
+++ b/nxcomp/src/ClientChannel.cpp
@@ -0,0 +1,7881 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <X11/X.h>
+#include <X11/Xatom.h>
+
+#include "NXproto.h"
+#include "NXrender.h"
+
+#include "ClientChannel.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "StaticCompressor.h"
+
+#include "Statistics.h"
+#include "Proxy.h"
+
+#include "PutImage.h"
+#include "PutPackedImage.h"
+
+extern Proxy *proxy;
+
+//
+// Set the verbosity level. You also
+// need to define OPCODES in Misc.cpp
+// if you want literals instead of
+// opcodes' numbers.
+//
+
+#define PANIC
+#define WARNING
+#undef OPCODES
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Log the important tracepoints related
+// to writing packets to the peer proxy.
+//
+
+#undef FLUSH
+
+//
+// Log the operations related to splits.
+//
+
+#undef SPLIT
+
+//
+// Define this to trace the invocations
+// of the agent's callbacks.
+//
+
+#undef CALLBACK
+
+//
+// By defining this, a simple procedure is activated at
+// startup which just allocates and deallocates plenty
+// of cache objects. This is used to help determine the
+// current memory requirements.
+//
+
+#undef MEMORY
+
+//
+// Inspects target of common X operations.
+//
+
+#undef TARGETS
+
+#ifdef TARGETS
+
+#include <set>
+#include <map>
+
+typedef set < unsigned int, less<unsigned int> > T_windows;
+typedef set < unsigned int, less<unsigned int> > T_pixmaps;
+typedef map < unsigned int, unsigned int, less<unsigned int> > T_gcontexts;
+
+T_windows windows;
+T_pixmaps pixmaps;
+T_gcontexts gcontexts;
+
+#endif
+
+//
+// Define this to log when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Here are the static members.
+//
+
+#ifdef REFERENCES
+
+int ClientChannel::references_ = 0;
+
+#endif
+
+ClientChannel::ClientChannel(Transport *transport, StaticCompressor *compressor)
+
+ : Channel(transport, compressor), readBuffer_(transport_, this)
+{
+ //
+ // Sequence number of the next message
+ // being encoded or decoded.
+ //
+
+ clientSequence_ = 0;
+ serverSequence_ = 0;
+
+ //
+ // Current sequence known by NX agent.
+ //
+
+ lastSequence_ = 0;
+
+ //
+ // This is used to test the synchronous
+ // flush in the proxy.
+ //
+
+ lastRequest_ = 0;
+
+ //
+ // Store information about the images
+ // being streamed.
+ //
+
+ splitState_.resource = nothing;
+ splitState_.pending = 0;
+ splitState_.commit = 0;
+ splitState_.mode = split_none;
+
+ //
+ // Number of outstanding tainted replies.
+ //
+
+ taintCounter_ = 0;
+
+ #ifdef MEMORY
+
+ *logofs << "ClientChannel: Created 1 ClientCache and 1 ServerCache. "
+ << "You have 30 seconds to check the allocated size.\n"
+ << logofs_flush;
+
+ sleep(30);
+
+ ClientCache *clientCacheTestArray[100];
+ ServerCache *serverCacheTestArray[100];
+
+ for (int i = 0; i < 100; i++)
+ {
+ clientCacheTestArray[i] = new ClientCache();
+ }
+
+ *logofs << "ClientChannel: Created further 100 ClientCache. "
+ << "You have 30 seconds to check the allocated size.\n"
+ << logofs_flush;
+
+ sleep(30);
+
+ for (int i = 0; i < 100; i++)
+ {
+ serverCacheTestArray[i] = new ServerCache();
+ }
+
+ *logofs << "ClientChannel: Created further 100 ServerCache. "
+ << "You have 30 seconds to check the allocated size.\n"
+ << logofs_flush;
+
+ sleep(30);
+
+ for (int i = 0; i < 100; i++)
+ {
+ delete clientCacheTestArray[i];
+ delete serverCacheTestArray[i];
+ }
+
+ *logofs << "ClientChannel: Deleted 100 ClientCache and 100 ServerCache. "
+ << "You have 30 seconds to check the allocated size.\n"
+ << logofs_flush;
+
+ sleep(30);
+
+ #endif
+
+ #ifdef REFERENCES
+ *logofs << "ClientChannel: Created new object at "
+ << this << " for FD#" << fd_ << " out of "
+ << ++references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+ClientChannel::~ClientChannel()
+{
+ #ifdef REFERENCES
+ *logofs << "ClientChannel: Deleted object at "
+ << this << " for FD#" << fd_ << " out of "
+ << --references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+//
+// Beginning of handleRead().
+//
+
+int ClientChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length)
+{
+ #ifdef TEST
+ *logofs << "handleRead: Called for FD#" << fd_
+ << " with " << encodeBuffer.getLength()
+ << " bytes already encoded.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Pointer to located message and
+ // its size in bytes.
+ //
+
+ const unsigned char *inputMessage;
+ unsigned int inputLength;
+
+ //
+ // Set when message is found in
+ // cache.
+ //
+
+ int hit;
+
+ //
+ // Check if we can borrow the buffer
+ // from the caller.
+ //
+
+ if (message != NULL && length != 0)
+ {
+ readBuffer_.readMessage(message, length);
+ }
+ else
+ {
+ //
+ // Get the data from the transport.
+ //
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: Trying to read from FD#"
+ << fd_ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = readBuffer_.readMessage();
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Read result on FD#" << fd_
+ << " is " << result << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (result < 0)
+ {
+ //
+ // Let the proxy close the channel.
+ //
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ *logofs << "handleRead: PANIC! No data read from FD#"
+ << fd_ << " while encoding messages.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+
+ #endif
+
+ return 0;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleRead: Encoding messages for FD#" << fd_
+ << " with " << readBuffer_.getLength() << " bytes "
+ << "in the buffer.\n" << logofs_flush;
+ #endif
+
+ //
+ // Extract any complete message which
+ // is available in the buffer.
+ //
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL)
+ {
+ hit = 0;
+
+ if (firstRequest_)
+ {
+ //
+ // Need to add the length of the first
+ // request as it was not present in
+ // previous versions.
+ //
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeValue(inputLength, 8);
+
+ for (unsigned int i = 0; i < inputLength; i++)
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8);
+ }
+
+ firstRequest_ = 0;
+
+ #if defined(TEST) || defined(OPCODES)
+
+ int bits = encodeBuffer.diffBits();
+
+ *logofs << "handleRead: Handled first request. " << inputLength
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ priority_++;
+ }
+ else
+ {
+ //
+ // First of all we get the opcode.
+ //
+
+ unsigned char inputOpcode = *inputMessage;
+
+ #if defined(TEST) || defined(INFO)
+
+ //
+ // This is used to test the synchronous
+ // flush in the parent proxy.
+ //
+
+ lastRequest_ = inputOpcode;
+
+ #endif
+
+ //
+ // Check if the request is supported by the
+ // remote. If not, only handle it locally and
+ // taint the opcode as a X_NoOperation. Also
+ // try to short-circuit some replies at this
+ // side. XSync requests, for example, weight
+ // for half of the total round-trips.
+ //
+
+ if (handleTaintRequest(inputOpcode, inputMessage,
+ inputLength) < 0)
+ {
+ return -1;
+ }
+
+ encodeBuffer.encodeOpcodeValue(inputOpcode, clientCache_ -> opcodeCache);
+
+ //
+ // Update the current sequence.
+ //
+
+ clientSequence_++;
+ clientSequence_ &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Last client sequence number for FD#"
+ << fd_ << " is " << clientSequence_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // If differential compression is disabled
+ // then use the most simple encoding.
+ //
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ int result = handleFastReadRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ //
+ // Go to the message's specific encoding.
+ //
+
+ switch (inputOpcode)
+ {
+ case X_AllocColor:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> colormapCache);
+ const unsigned char *nextSrc = inputMessage + 8;
+ unsigned int colorData[3];
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ unsigned int value = GetUINT(nextSrc, bigEndian_);
+ encodeBuffer.encodeCachedValue(value, 16,
+ *(clientCache_ -> allocColorRGBCache[i]), 4);
+ colorData[i] = value;
+ nextSrc += 2;
+ }
+
+ sequenceQueue_.push(clientSequence_, inputOpcode,
+ colorData[0], colorData[1], colorData[2]);
+
+ priority_++;
+ }
+ break;
+ case X_ReparentWindow:
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 12, bigEndian_), 16, 11);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 14, bigEndian_), 16, 11);
+ }
+ break;
+ case X_ChangeProperty:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ChangeProperty);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ unsigned char format = inputMessage[16];
+ encodeBuffer.encodeCachedValue(format, 8,
+ clientCache_ -> changePropertyFormatCache);
+ unsigned int dataLength = GetULONG(inputMessage + 20, bigEndian_);
+ encodeBuffer.encodeValue(dataLength, 32, 6);
+ encodeBuffer.encodeValue(inputMessage[1], 2);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
+ clientCache_ -> changePropertyPropertyCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29,
+ clientCache_ -> changePropertyTypeCache, 9);
+ const unsigned char *nextSrc = inputMessage + 24;
+ if (format == 8)
+ {
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, dataLength);
+ }
+ else if (format == 32)
+ {
+ for (unsigned int i = 0; i < dataLength; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32,
+ clientCache_ -> changePropertyData32Cache);
+ nextSrc += 4;
+ }
+ }
+ else
+ {
+ for (unsigned int i = 0; i < dataLength; i++)
+ {
+ encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16);
+ nextSrc += 2;
+ }
+ }
+ }
+ break;
+ case X_SendEvent:
+ {
+ //
+ // TODO: This can be improved. In the worst
+ // cases, it appears to provide a poor 1.6:1
+ // ratio.
+ //
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_SendEvent);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ unsigned int window = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (window == 0 || window == 1)
+ {
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeBoolValue(window);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeXidValue(window, clientCache_ -> windowCache);
+ }
+
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 32,
+ clientCache_ -> sendEventMaskCache, 9);
+ encodeBuffer.encodeCachedValue(*(inputMessage + 12), 8,
+ clientCache_ -> sendEventCodeCache);
+ encodeBuffer.encodeCachedValue(*(inputMessage + 13), 8,
+ clientCache_ -> sendEventByteDataCache);
+
+ unsigned int newSeq = GetUINT(inputMessage + 14, bigEndian_);
+ unsigned int diffSeq = newSeq - clientCache_ -> sendEventLastSequence;
+ clientCache_ -> sendEventLastSequence = newSeq;
+ encodeBuffer.encodeValue(diffSeq, 16, 4);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 32,
+ clientCache_ -> sendEventIntDataCache);
+
+ for (unsigned int i = 20; i < 44; i++)
+ {
+ encodeBuffer.encodeCachedValue((unsigned int) inputMessage[i], 8,
+ clientCache_ -> sendEventEventCache);
+ }
+ }
+ break;
+ case X_ChangeWindowAttributes:
+ {
+ encodeBuffer.encodeValue((inputLength - 12) >> 2, 4);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ unsigned int bitmask = GetULONG(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeCachedValue(bitmask, 15,
+ clientCache_ -> createWindowBitmaskCache);
+ const unsigned char *nextSrc = inputMessage + 12;
+ unsigned int mask = 0x1;
+ for (unsigned int j = 0; j < 15; j++)
+ {
+ if (bitmask & mask)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32,
+ *clientCache_ -> createWindowAttrCache[j]);
+ nextSrc += 4;
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_ClearArea:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_ClearArea target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_ClearArea target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_ClearArea target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ClearArea);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ const unsigned char *nextSrc = inputMessage + 8;
+ for (unsigned int i = 0; i < 4; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> clearAreaGeomCache[i], 8);
+ nextSrc += 2;
+ }
+ }
+ break;
+ case X_CloseFont:
+ {
+ unsigned int font = GetULONG(inputMessage + 4, bigEndian_);
+ encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5);
+ clientCache_ -> lastFont = font;
+ }
+ break;
+ case X_ConfigureWindow:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ConfigureWindow);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ unsigned int bitmask = GetUINT(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeCachedValue(bitmask, 7,
+ clientCache_ -> configureWindowBitmaskCache);
+ unsigned int mask = 0x1;
+ const unsigned char *nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < 7; i++)
+ {
+ if (bitmask & mask)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_),
+ CONFIGUREWINDOW_FIELD_WIDTH[i],
+ *clientCache_ -> configureWindowAttrCache[i], 8);
+ nextSrc += 4;
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_ConvertSelection:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> convertSelectionRequestorCache, 9);
+ const unsigned char* nextSrc = inputMessage + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29,
+ *(clientCache_ -> convertSelectionAtomCache[i]), 9);
+ nextSrc += 4;
+ }
+ unsigned int _timestamp = GetULONG(nextSrc, bigEndian_);
+ encodeBuffer.encodeValue(_timestamp -
+ clientCache_ -> convertSelectionLastTimestamp, 32, 4);
+ clientCache_ -> convertSelectionLastTimestamp = _timestamp;
+ }
+ break;
+ case X_CopyArea:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_CopyArea source id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_CopyArea source id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_CopyArea source id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ t_id = GetULONG(inputMessage + 8, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_CopyArea target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_CopyArea target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_CopyArea target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CopyArea);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 12,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *nextSrc = inputMessage + 16;
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> copyAreaGeomCache[i], 8);
+ nextSrc += 2;
+ }
+ }
+ break;
+ case X_CopyGC:
+ {
+ #ifdef TARGETS
+
+ unsigned int s_g_id = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int d_g_id = GetULONG(inputMessage + 8, bigEndian_);
+
+ *logofs << "handleRead: X_CopyGC source gcontext id is " << s_g_id
+ << " destination gcontext id is " << d_g_id << ".\n"
+ << logofs_flush;
+
+ #endif
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> gcCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12,
+ bigEndian_), 23, clientCache_ -> createGCBitmaskCache);
+ }
+ break;
+ case X_CopyPlane:
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 12,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *nextSrc = inputMessage + 16;
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> copyPlaneGeomCache[i], 8);
+ nextSrc += 2;
+ }
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 28, bigEndian_), 32,
+ clientCache_ -> copyPlaneBitPlaneCache, 10);
+ }
+ break;
+ case X_CreateGC:
+ {
+ #ifdef TARGETS
+
+ unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int t_id = GetULONG(inputMessage + 8, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_CreateGC id " << g_id
+ << " target id is pixmap " << t_id
+ << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_CreateGC id " << g_id
+ << " target id is window " << t_id
+ << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_CreateGC id " << g_id
+ << " target id is unrecognized.\n"
+ << logofs_flush;
+ }
+
+ gcontexts.insert(T_gcontexts::value_type(g_id, t_id));
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CreateGC);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> lastId, clientCache_ -> lastIdCache,
+ clientCache_ -> gcCache,
+ clientCache_ -> freeGCCache);
+
+ const unsigned char *nextSrc = inputMessage + 8;
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> drawableCache);
+ nextSrc += 4;
+ unsigned int bitmask = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ encodeBuffer.encodeCachedValue(bitmask, 23,
+ clientCache_ -> createGCBitmaskCache);
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (bitmask & mask)
+ {
+ unsigned int value = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i];
+ if (fieldWidth <= 4)
+ {
+ encodeBuffer.encodeValue(value, fieldWidth);
+ }
+ else
+ {
+ encodeBuffer.encodeCachedValue(value, fieldWidth,
+ *clientCache_ -> createGCAttrCache[i]);
+ }
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_ChangeGC:
+ {
+ #ifdef TARGETS
+
+ unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ T_gcontexts::iterator i = gcontexts.find(g_id);
+
+ if (i != gcontexts.end())
+ {
+ unsigned int t_id = i -> second;
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_ChangeGC gcontext id is " << g_id
+ << " target id is pixmap " << t_id << ".\n"
+ << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_ChangeGC gcontext id is " << g_id
+ << " target id is window " << t_id << ".\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_ChangeGC gcontext is " << g_id
+ << " target id is unrecognized.\n"
+ << logofs_flush;
+ }
+ }
+ else
+ {
+ *logofs << "handleRead: X_ChangeGC gcontext id " << g_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ gcontexts.erase(g_id);
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ChangeGC);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *nextSrc = inputMessage + 8;
+ unsigned int bitmask = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ encodeBuffer.encodeCachedValue(bitmask, 23,
+ clientCache_ -> createGCBitmaskCache);
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (bitmask & mask)
+ {
+ unsigned int value = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i];
+ if (fieldWidth <= 4)
+ {
+ encodeBuffer.encodeValue(value, fieldWidth);
+ }
+ else
+ {
+ encodeBuffer.encodeCachedValue(value, fieldWidth,
+ *clientCache_ -> createGCAttrCache[i]);
+ }
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_CreatePixmap:
+ {
+ #ifdef TARGETS
+
+ *logofs << "handleRead: X_CreatePixmap depth " << (unsigned) inputMessage[1]
+ << ", pixmap id " << GetULONG(inputMessage + 4, bigEndian_)
+ << ", drawable " << GetULONG(inputMessage + 8, bigEndian_)
+ << ", width " << GetUINT(inputMessage + 12, bigEndian_)
+ << ", height " << GetUINT(inputMessage + 14, bigEndian_)
+ << ", size " << GetUINT(inputMessage + 2, bigEndian_) << 2
+ << ".\n" << logofs_flush;
+
+ unsigned int p_id = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned short p_sx = GetUINT(inputMessage + 12, bigEndian_);
+ unsigned short p_sy = GetUINT(inputMessage + 14, bigEndian_);
+
+ *logofs << "handleRead: X_CreatePixmap id is " << p_id
+ << " width is " << p_sx << " height is " << p_sy
+ << ".\n" << logofs_flush;
+
+ if (p_sx * p_sy <= 64 * 64)
+ {
+ *logofs << "handleRead: X_CreatePixmap id " << p_id << " of size "
+ << p_sx << "x" << p_sy << "=" << p_sx * p_sy
+ << " will be painted at client side.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_CreatePixmap id " << p_id << " of size "
+ << p_sx << "x" << p_sy << "=" << p_sx * p_sy
+ << " will be painted at server side.\n" << logofs_flush;
+ }
+
+ pixmaps.insert(p_id);
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CreatePixmap);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ break;
+ case X_CreateWindow:
+ {
+ #ifdef TARGETS
+
+ unsigned int w_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ *logofs << "handleRead: X_CreateWindow id is " << w_id
+ << ".\n" << logofs_flush;
+
+ windows.insert(w_id);
+
+ #endif
+
+ unsigned bitmask = GetULONG(inputMessage + 28, bigEndian_);
+ encodeBuffer.encodeCachedValue((unsigned int) inputMessage[1], 8,
+ clientCache_ -> depthCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
+ clientCache_ -> windowCache);
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeNewXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> lastId, clientCache_ -> lastIdCache,
+ clientCache_ -> windowCache,
+ clientCache_ -> freeWindowCache);
+
+ const unsigned char *nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> createWindowGeomCache[i], 8);
+ nextSrc += 2;
+ }
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 24,
+ bigEndian_), 29, clientCache_ -> visualCache);
+ encodeBuffer.encodeCachedValue(bitmask, 15,
+ clientCache_ -> createWindowBitmaskCache);
+ nextSrc = inputMessage + 32;
+ unsigned int mask = 0x1;
+ for (unsigned int j = 0; j < 15; j++)
+ {
+ if (bitmask & mask)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 32,
+ *clientCache_ -> createWindowAttrCache[j]);
+ nextSrc += 4;
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_DeleteProperty:
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 29, 9);
+ }
+ break;
+ case X_FillPoly:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_FillPoly target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_FillPoly target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_FillPoly target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_FillPoly);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ unsigned int numPoints = ((inputLength - 16) >> 2);
+
+ // Since ProtoStep10 (#issue 108)
+ encodeBuffer.encodeCachedValue(numPoints, 16,
+ clientCache_ -> fillPolyNumPointsCache, 4);
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
+ clientCache_ -> gcCache);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[12], 2);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]);
+ int relativeCoordMode = (inputMessage[13] != 0);
+ const unsigned char *nextSrc = inputMessage + 16;
+ unsigned int pointIndex = 0;
+
+ for (unsigned int i = 0; i < numPoints; i++)
+ {
+ if (relativeCoordMode)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> fillPolyXRelCache[pointIndex], 8);
+ nextSrc += 2;
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> fillPolyYRelCache[pointIndex], 8);
+ nextSrc += 2;
+ }
+ else
+ {
+ unsigned int x = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int y = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int j;
+ for (j = 0; j < 8; j++)
+ if ((x == clientCache_ -> fillPolyRecentX[j]) &&
+ (y == clientCache_ -> fillPolyRecentY[j]))
+ break;
+ if (j < 8)
+ {
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeValue(j, 3);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> fillPolyXAbsCache[pointIndex], 8);
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> fillPolyYAbsCache[pointIndex], 8);
+ clientCache_ -> fillPolyRecentX[clientCache_ -> fillPolyIndex] = x;
+ clientCache_ -> fillPolyRecentY[clientCache_ -> fillPolyIndex] = y;
+ clientCache_ -> fillPolyIndex++;
+ if (clientCache_ -> fillPolyIndex == 8)
+ clientCache_ -> fillPolyIndex = 0;
+ }
+ }
+
+ if (++pointIndex == 10) pointIndex = 0;
+ }
+ }
+ break;
+ case X_FreeColors:
+ {
+ unsigned int numPixels = GetUINT(inputMessage + 2, bigEndian_) - 3;
+ encodeBuffer.encodeValue(numPixels, 16, 4);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> colormapCache);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 32, 4);
+ const unsigned char *nextSrc = inputMessage + 12;
+ while (numPixels)
+ {
+ encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 8);
+ nextSrc += 4;
+ numPixels--;
+ }
+ }
+ break;
+ case X_FreeCursor:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, clientCache_ -> cursorCache, 9);
+ }
+ break;
+ case X_FreeGC:
+ {
+ #ifdef TARGETS
+
+ unsigned int g_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ T_gcontexts::iterator i = gcontexts.find(g_id);
+
+ if (i != gcontexts.end())
+ {
+ unsigned int t_id = i -> second;
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_FreeGC gcontext id is " << g_id
+ << " target id is pixmap " << t_id << ".\n"
+ << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_FreeGC gcontext id is " << g_id
+ << " target id is window " << t_id << ".\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_FreeGC gcontext id is " << g_id
+ << " target id is unrecognized.\n"
+ << logofs_flush;
+ }
+ }
+ else
+ {
+ *logofs << "handleRead: X_FreeGC gcontext id " << g_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ gcontexts.erase(g_id);
+
+ #endif
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> freeGCCache);
+ }
+ break;
+ case X_FreePixmap:
+ {
+ #ifdef TARGETS
+
+ unsigned int p_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ *logofs << "handleRead: X_FreePixmap id is " << p_id << ".\n" << logofs_flush;
+
+ pixmaps.erase(p_id);
+
+ #endif
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> freeDrawableCache);
+ }
+ break;
+ case X_GetAtomName:
+ {
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 29, 9);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetGeometry:
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> drawableCache);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetInputFocus:
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetModifierMapping:
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetKeyboardMapping:
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[4], 8);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[5], 8);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetProperty:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_GetProperty);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ unsigned int property = GetULONG(inputMessage + 8, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode, property);
+
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ unsigned int property = GetULONG(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(property, 29, 9);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 12, bigEndian_), 29, 9);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 16, bigEndian_), 32, 2);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 20, bigEndian_), 32, 8);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode, property);
+
+ priority_++;
+ }
+ break;
+ case X_GetSelectionOwner:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> getSelectionOwnerSelectionCache, 9);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GrabButton:
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ clientCache_ -> grabButtonEventMaskCache);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[10]);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[11]);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29,
+ clientCache_ -> grabButtonConfineCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 29,
+ clientCache_ -> cursorCache, 9);
+ encodeBuffer.encodeCachedValue(inputMessage[20], 8,
+ clientCache_ -> grabButtonButtonCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 22, bigEndian_), 16,
+ clientCache_ -> grabButtonModifierCache);
+ }
+ break;
+ case X_GrabPointer:
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ clientCache_ -> grabButtonEventMaskCache);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[10]);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[11]);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12,
+ bigEndian_), 29,
+ clientCache_ -> grabButtonConfineCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16,
+ bigEndian_), 29, clientCache_ -> cursorCache, 9);
+
+ unsigned int _timestamp = GetULONG(inputMessage + 20, bigEndian_);
+ encodeBuffer.encodeValue(_timestamp -
+ clientCache_ -> grabKeyboardLastTimestamp, 32, 4);
+ clientCache_ -> grabKeyboardLastTimestamp = _timestamp;
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GrabKeyboard:
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ unsigned int _timestamp = GetULONG(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(_timestamp -
+ clientCache_ -> grabKeyboardLastTimestamp, 32, 4);
+ clientCache_ -> grabKeyboardLastTimestamp = _timestamp;
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GrabServer:
+ case X_UngrabServer:
+ case X_NoOperation:
+ {
+ }
+ break;
+ case X_PolyText8:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyText8 target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyText8 target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyText8 target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyText8);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
+ int xDiff = x - clientCache_ -> polyTextLastX;
+ clientCache_ -> polyTextLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache_ -> polyTextCacheX);
+ unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
+ int yDiff = y - clientCache_ -> polyTextLastY;
+ clientCache_ -> polyTextLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache_ -> polyTextCacheY);
+ const unsigned char *end = inputMessage + inputLength - 1;
+ const unsigned char *nextSrc = inputMessage + 16;
+ while (nextSrc < end)
+ {
+ unsigned int textLength = (unsigned int) *nextSrc++;
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeValue(textLength, 8);
+ if (textLength == 255)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, 1), 29,
+ clientCache_ -> polyTextFontCache);
+ nextSrc += 4;
+ }
+ else
+ {
+ encodeBuffer.encodeCachedValue(*nextSrc++, 8,
+ clientCache_ -> polyTextDeltaCache);
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, textLength);
+ nextSrc += textLength;
+ }
+ }
+ encodeBuffer.encodeBoolValue(0);
+ }
+ break;
+ case X_PolyText16:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyText16 target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyText16 target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyText16 target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyText16);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
+ int xDiff = x - clientCache_ -> polyTextLastX;
+ clientCache_ -> polyTextLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache_ -> polyTextCacheX);
+ unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
+ int yDiff = y - clientCache_ -> polyTextLastY;
+ clientCache_ -> polyTextLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache_ -> polyTextCacheY);
+ const unsigned char *end = inputMessage + inputLength - 1;
+ const unsigned char *nextSrc = inputMessage + 16;
+ while (nextSrc < end)
+ {
+ unsigned int textLength = (unsigned int) *nextSrc++;
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeValue(textLength, 8);
+ if (textLength == 255)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, 1), 29,
+ clientCache_ -> polyTextFontCache);
+ nextSrc += 4;
+ }
+ else
+ {
+ encodeBuffer.encodeCachedValue(*nextSrc++, 8,
+ clientCache_ -> polyTextDeltaCache);
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, textLength * 2);
+ nextSrc += textLength * 2;
+ }
+ }
+ encodeBuffer.encodeBoolValue(0);
+ }
+ break;
+ case X_ImageText8:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_ImageText8 target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_ImageText8 target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_ImageText8 target id "
+ << t_id << " is unrecognized.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ImageText8);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ unsigned int textLength = (unsigned int) inputMessage[1];
+ encodeBuffer.encodeCachedValue(textLength, 8,
+ clientCache_ -> imageTextLengthCache, 4);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
+ int xDiff = x - clientCache_ -> imageTextLastX;
+ clientCache_ -> imageTextLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache_ -> imageTextCacheX);
+ unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
+ int yDiff = y - clientCache_ -> imageTextLastY;
+ clientCache_ -> imageTextLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache_ -> imageTextCacheY);
+ const unsigned char *nextSrc = inputMessage + 16;
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, textLength);
+ }
+ break;
+ case X_ImageText16:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_ImageText16 target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_ImageText16 target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_ImageText16 target id "
+ << t_id << " is unrecognized.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ImageText16);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ unsigned int textLength = (unsigned int) inputMessage[1];
+ encodeBuffer.encodeCachedValue(textLength, 8,
+ clientCache_ -> imageTextLengthCache, 4);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ unsigned int x = GetUINT(inputMessage + 12, bigEndian_);
+ int xDiff = x - clientCache_ -> imageTextLastX;
+ clientCache_ -> imageTextLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache_ -> imageTextCacheX);
+ unsigned int y = GetUINT(inputMessage + 14, bigEndian_);
+ int yDiff = y - clientCache_ -> imageTextLastY;
+ clientCache_ -> imageTextLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache_ -> imageTextCacheY);
+ const unsigned char *nextSrc = inputMessage + 16;
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, textLength * 2);
+ }
+ break;
+ case X_InternAtom:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_InternAtom);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ //
+ // Set the priority, also if doing so will
+ // penalize all the well written clients
+ // using XInternAtoms() to pipeline multi-
+ // ple replies.
+ //
+
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ unsigned int nameLength = GetUINT(inputMessage + 4, bigEndian_);
+ encodeBuffer.encodeValue(nameLength, 16, 6);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ const unsigned char *nextSrc = inputMessage + 8;
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, nameLength);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_ListExtensions:
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_ListFonts:
+ {
+ unsigned int textLength = GetUINT(inputMessage + 6, bigEndian_);
+ encodeBuffer.encodeValue(textLength, 16, 6);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 4, bigEndian_), 16, 6);
+ const unsigned char* nextSrc = inputMessage + 8;
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, textLength);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_LookupColor:
+ case X_AllocNamedColor:
+ {
+ unsigned int textLength = GetUINT(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(textLength, 16, 6);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, clientCache_ -> colormapCache);
+ const unsigned char *nextSrc = inputMessage + 12;
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, textLength);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_MapWindow:
+ case X_UnmapWindow:
+ case X_MapSubwindows:
+ case X_GetWindowAttributes:
+ case X_DestroyWindow:
+ case X_DestroySubwindows:
+ case X_QueryPointer:
+ case X_QueryTree:
+ {
+ #ifdef TARGETS
+
+ if (inputOpcode == X_DestroyWindow)
+ {
+ unsigned int w_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ *logofs << "handleRead: X_DestroyWindow id is "
+ << w_id << ".\n" << logofs_flush;
+
+ windows.erase(w_id);
+ }
+
+ #endif
+
+ if (inputOpcode == X_DestroyWindow)
+ {
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeFreeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> freeWindowCache);
+ }
+ else
+ {
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> windowCache);
+ }
+
+ if ((inputOpcode == X_QueryPointer) ||
+ (inputOpcode == X_GetWindowAttributes) ||
+ (inputOpcode == X_QueryTree))
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ }
+ break;
+ case X_OpenFont:
+ {
+ unsigned int nameLength = GetUINT(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(nameLength, 16, 7);
+ unsigned int font = GetULONG(inputMessage + 4, bigEndian_);
+ encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5);
+ clientCache_ -> lastFont = font;
+ const unsigned char *nextSrc = inputMessage + 12;
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, nameLength);
+ }
+ break;
+ case X_PolyFillRectangle:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyFillRectangle target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyFillRectangle target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyFillRectangle target id "
+ << t_id << " is unrecognized.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyFillRectangle);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+ unsigned int lastWidth = 0, lastHeight = 0;
+
+ //
+ // TODO: Could send the size at the beginning
+ // instead of a bool at each iteration.
+ //
+
+ for (unsigned int i = 12; i < inputLength;)
+ {
+ unsigned int x = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newX = x;
+ x -= lastX;
+ lastX = newX;
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> polyFillRectangleCacheX[index], 8);
+ i += 2;
+ unsigned int y = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newY = y;
+ y -= lastY;
+ lastY = newY;
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> polyFillRectangleCacheY[index], 8);
+ i += 2;
+ unsigned int width = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newWidth = width;
+ width -= lastWidth;
+ lastWidth = newWidth;
+ encodeBuffer.encodeCachedValue(width, 16,
+ *clientCache_ -> polyFillRectangleCacheWidth[index], 8);
+ i += 2;
+ unsigned int height = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newHeight = height;
+ height -= lastHeight;
+ lastHeight = newHeight;
+ encodeBuffer.encodeCachedValue(height, 16,
+ *clientCache_ -> polyFillRectangleCacheHeight[index], 8);
+ i += 2;
+
+ if (++index == 4) index = 0;
+
+ encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0);
+ }
+ }
+ break;
+ case X_PolyFillArc:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyFillArc target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyFillArc target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyFillArc target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyFillArc);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+ unsigned int lastWidth = 0, lastHeight = 0;
+ unsigned int lastAngle1 = 0, lastAngle2 = 0;
+
+ //
+ // TODO: Could send the size at the beginning
+ // instead of a bool at each iteration.
+ //
+
+ for (unsigned int i = 12; i < inputLength;)
+ {
+ unsigned int x = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newX = x;
+ x -= lastX;
+ lastX = newX;
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> polyFillArcCacheX[index], 8);
+ i += 2;
+ unsigned int y = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newY = y;
+ y -= lastY;
+ lastY = newY;
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> polyFillArcCacheY[index], 8);
+ i += 2;
+ unsigned int width = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newWidth = width;
+ width -= lastWidth;
+ lastWidth = newWidth;
+ encodeBuffer.encodeCachedValue(width, 16,
+ *clientCache_ -> polyFillArcCacheWidth[index], 8);
+ i += 2;
+ unsigned int height = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newHeight = height;
+ height -= lastHeight;
+ lastHeight = newHeight;
+ encodeBuffer.encodeCachedValue(height, 16,
+ *clientCache_ -> polyFillArcCacheHeight[index], 8);
+ i += 2;
+ unsigned int angle1 = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newAngle1 = angle1;
+ angle1 -= lastAngle1;
+ lastAngle1 = newAngle1;
+ encodeBuffer.encodeCachedValue(angle1, 16,
+ *clientCache_ -> polyFillArcCacheAngle1[index], 8);
+ i += 2;
+ unsigned int angle2 = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newAngle2 = angle2;
+ angle2 -= lastAngle2;
+ lastAngle2 = newAngle2;
+ encodeBuffer.encodeCachedValue(angle2, 16,
+ *clientCache_ -> polyFillArcCacheAngle2[index], 8);
+ i += 2;
+
+ if (++index == 2) index = 0;
+
+ encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0);
+ }
+ }
+ break;
+ case X_PolyArc:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyArc target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyArc target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyArc target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyArc);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+ unsigned int lastWidth = 0, lastHeight = 0;
+ unsigned int lastAngle1 = 0, lastAngle2 = 0;
+
+ //
+ // TODO: Could send the size at the beginning
+ // instead of a bool at each iteration.
+ //
+
+ for (unsigned int i = 12; i < inputLength;)
+ {
+ unsigned int x = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newX = x;
+ x -= lastX;
+ lastX = newX;
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> polyArcCacheX[index], 8);
+ i += 2;
+ unsigned int y = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newY = y;
+ y -= lastY;
+ lastY = newY;
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> polyArcCacheY[index], 8);
+ i += 2;
+ unsigned int width = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newWidth = width;
+ width -= lastWidth;
+ lastWidth = newWidth;
+ encodeBuffer.encodeCachedValue(width, 16,
+ *clientCache_ -> polyArcCacheWidth[index], 8);
+ i += 2;
+ unsigned int height = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newHeight = height;
+ height -= lastHeight;
+ lastHeight = newHeight;
+ encodeBuffer.encodeCachedValue(height, 16,
+ *clientCache_ -> polyArcCacheHeight[index], 8);
+ i += 2;
+ unsigned int angle1 = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newAngle1 = angle1;
+ angle1 -= lastAngle1;
+ lastAngle1 = newAngle1;
+ encodeBuffer.encodeCachedValue(angle1, 16,
+ *clientCache_ -> polyArcCacheAngle1[index], 8);
+ i += 2;
+ unsigned int angle2 = GetUINT(inputMessage + i, bigEndian_);
+ unsigned int newAngle2 = angle2;
+ angle2 -= lastAngle2;
+ lastAngle2 = newAngle2;
+ encodeBuffer.encodeCachedValue(angle2, 16,
+ *clientCache_ -> polyArcCacheAngle2[index], 8);
+ i += 2;
+
+ if (++index == 2) index = 0;
+
+ encodeBuffer.encodeBoolValue((i < inputLength) ? 1 : 0);
+ }
+ }
+ break;
+ case X_PolyPoint:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyPoint target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyPoint target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyPoint target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyPoint);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_) - 3, 16, 4);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8, bigEndian_),
+ clientCache_ -> gcCache);
+ const unsigned char *nextSrc = inputMessage + 12;
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+
+ for (unsigned int i = 12; i < inputLength; i += 4)
+ {
+ unsigned int x = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int tmp = x;
+ x -= lastX;
+ lastX = tmp;
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> polyPointCacheX[index], 8);
+ unsigned int y = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ tmp = y;
+ y -= lastY;
+ lastY = tmp;
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> polyPointCacheY[index], 8);
+
+ if (++index == 2) index = 0;
+ }
+ }
+ break;
+ case X_PolyLine:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolyLine target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolyLine target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolyLine target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyLine);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_) - 3, 16, 4);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *nextSrc = inputMessage + 12;
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+
+ for (unsigned int i = 12; i < inputLength; i += 4)
+ {
+ unsigned int x = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int tmp = x;
+ x -= lastX;
+ lastX = tmp;
+ encodeBuffer.encodeCachedValue(x, 16,
+ *clientCache_ -> polyLineCacheX[index], 8);
+ unsigned int y = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ tmp = y;
+ y -= lastY;
+ lastY = tmp;
+ encodeBuffer.encodeCachedValue(y, 16,
+ *clientCache_ -> polyLineCacheY[index], 8);
+
+ if (++index == 2) index = 0;
+ }
+ }
+ break;
+ case X_PolyRectangle:
+ {
+ encodeBuffer.encodeValue((GetUINT(inputMessage + 2,
+ bigEndian_) - 3) >> 1, 16, 3);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *end = inputMessage + inputLength;
+ const unsigned char *nextSrc = inputMessage + 12;
+ while (nextSrc < end)
+ {
+ for (unsigned int i = 0; i < 4; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> polyRectangleGeomCache[i], 8);
+ nextSrc += 2;
+ }
+ }
+ }
+ break;
+ case X_PolySegment:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PolySegment target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PolySegment target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PolySegment target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolySegment);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeValue((GetUINT(inputMessage + 2,
+ bigEndian_) - 3) >> 1, 16, 4);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 8,
+ bigEndian_), clientCache_ -> gcCache);
+ const unsigned char *end = inputMessage + inputLength;
+ const unsigned char *nextSrc = inputMessage + 12;
+ // unsigned int index = 0;
+ // unsigned int lastX1, lastY1, lastX2, lastY2;
+ while (nextSrc < end)
+ {
+ unsigned int x = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int xDiff0 =
+ x - clientCache_ -> polySegmentLastX[0];
+ unsigned int xDiff1 =
+ x - clientCache_ -> polySegmentLastX[1];
+ int xDiff0Abs = (int) xDiff0;
+ if (xDiff0Abs < 0)
+ xDiff0Abs = -xDiff0Abs;
+ int xDiff1Abs = (int) xDiff1;
+ if (xDiff1Abs < 0)
+ xDiff1Abs = -xDiff1Abs;
+
+ unsigned int y = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ unsigned int yDiff0 =
+ y - clientCache_ -> polySegmentLastY[0];
+ unsigned int yDiff1 =
+ y - clientCache_ -> polySegmentLastY[1];
+ int yDiff0Abs = (int) yDiff0;
+ if (yDiff0Abs < 0)
+ yDiff0Abs = -yDiff0Abs;
+ int yDiff1Abs = (int) yDiff1;
+ if (yDiff1Abs < 0)
+ yDiff1Abs = -yDiff1Abs;
+
+ int diff0 = xDiff0Abs + yDiff0Abs;
+ int diff1 = xDiff1Abs + yDiff1Abs;
+ if (diff0 < diff1)
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeCachedValue(xDiff0, 16,
+ clientCache_ -> polySegmentCacheX, 6);
+ encodeBuffer.encodeCachedValue(yDiff0, 16,
+ clientCache_ -> polySegmentCacheY, 6);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeCachedValue(xDiff1, 16,
+ clientCache_ -> polySegmentCacheX, 6);
+ encodeBuffer.encodeCachedValue(yDiff1, 16,
+ clientCache_ -> polySegmentCacheY, 6);
+ }
+
+ clientCache_ -> polySegmentLastX[clientCache_ -> polySegmentCacheIndex] = x;
+ clientCache_ -> polySegmentLastY[clientCache_ -> polySegmentCacheIndex] = y;
+
+ clientCache_ -> polySegmentCacheIndex =
+ clientCache_ -> polySegmentCacheIndex == 1 ? 0 : 1;
+ }
+ }
+ break;
+ case X_PutImage:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_PutImage target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_PutImage target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_PutImage target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PutImage);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ break;
+ case X_QueryBestSize:
+ {
+ encodeBuffer.encodeValue((unsigned int)inputMessage[1], 2);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 8, bigEndian_), 16, 8);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 10, bigEndian_), 16, 8);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_QueryColors:
+ {
+ // Differential encoding.
+ encodeBuffer.encodeBoolValue(1);
+
+ unsigned int numColors = ((inputLength - 8) >> 2);
+ encodeBuffer.encodeValue(numColors, 16, 5);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> colormapCache);
+ const unsigned char *nextSrc = inputMessage + 8;
+ unsigned int predictedPixel = clientCache_ -> queryColorsLastPixel;
+ for (unsigned int i = 0; i < numColors; i++)
+ {
+ unsigned int pixel = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ if (pixel == predictedPixel)
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue(pixel, 32, 9);
+ }
+ if (i == 0)
+ clientCache_ -> queryColorsLastPixel = pixel;
+ predictedPixel = pixel + 1;
+ }
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_QueryExtension:
+ {
+ #ifdef TEST
+
+ char data[256];
+
+ int length = GetUINT(inputMessage + 4, bigEndian_);
+
+ if (length > 256)
+ {
+ length = 256;
+ }
+
+ strncpy(data, (char *) inputMessage + 8, length);
+
+ *(data + length) = '\0';
+
+ *logofs << "handleRead: Going to query extension '"
+ << data << "' for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int nameLength = GetUINT(inputMessage + 4, bigEndian_);
+ encodeBuffer.encodeValue(nameLength, 16, 6);
+ const unsigned char *nextSrc = inputMessage + 8;
+
+ for (; nameLength; nameLength--)
+ {
+ encodeBuffer.encodeValue((unsigned int) *nextSrc++, 8);
+ }
+
+ unsigned int extension = 0;
+
+ if (strncmp((char *) inputMessage + 8, "SHAPE", 5) == 0)
+ {
+ extension = X_NXInternalShapeExtension;
+ }
+ else if (strncmp((char *) inputMessage + 8, "RENDER", 6) == 0)
+ {
+ extension = X_NXInternalRenderExtension;
+ }
+
+ sequenceQueue_.push(clientSequence_, inputOpcode, extension);
+
+ priority_++;
+ }
+ break;
+ case X_QueryFont:
+ {
+ unsigned int font = GetULONG(inputMessage + 4, bigEndian_);
+ encodeBuffer.encodeValue(font - clientCache_ -> lastFont, 29, 5);
+ clientCache_ -> lastFont = font;
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_SetClipRectangles:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_SetClipRectangles);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ hit = 1;
+
+ break;
+ }
+
+ unsigned int numRectangles = ((inputLength - 12) >> 3);
+
+ // Since ProtoStep9 (#issue 108)
+ encodeBuffer.encodeValue(numRectangles, 15, 4);
+
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> gcCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ clientCache_ -> setClipRectanglesXCache, 8);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 10, bigEndian_), 16,
+ clientCache_ -> setClipRectanglesYCache, 8);
+ const unsigned char *nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < numRectangles; i++)
+ {
+ for (unsigned int j = 0; j < 4; j++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *clientCache_ -> setClipRectanglesGeomCache[j], 8);
+ nextSrc += 2;
+ }
+ }
+ }
+ break;
+ case X_SetDashes:
+ {
+ unsigned int numDashes = GetUINT(inputMessage + 10, bigEndian_);
+ encodeBuffer.encodeCachedValue(numDashes, 16,
+ clientCache_ -> setDashesLengthCache, 5);
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4, bigEndian_),
+ clientCache_ -> gcCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ clientCache_ -> setDashesOffsetCache, 5);
+ const unsigned char *nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < numDashes; i++)
+ encodeBuffer.encodeCachedValue(*nextSrc++, 8,
+ clientCache_ -> setDashesDashCache_[i & 1], 5);
+ }
+ break;
+ case X_SetSelectionOwner:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> setSelectionOwnerCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
+ clientCache_ -> getSelectionOwnerSelectionCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 32,
+ clientCache_ -> setSelectionOwnerTimestampCache, 9);
+ }
+ break;
+ case X_TranslateCoords:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_TranslateCoords source id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_TranslateCoords source id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_TranslateCoords source id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ t_id = GetULONG(inputMessage + 8, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_TranslateCoords target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_TranslateCoords target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_TranslateCoords target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_TranslateCoords);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ clientCache_ -> translateCoordsSrcCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
+ clientCache_ -> translateCoordsDstCache, 9);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_), 16,
+ clientCache_ -> translateCoordsXCache, 8);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 14, bigEndian_), 16,
+ clientCache_ -> translateCoordsYCache, 8);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetImage:
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_GetImage source id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_GetImage source id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_GetImage source id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_GetImage);
+
+ if (handleEncodeCached(encodeBuffer, clientCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ // Format.
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2);
+ // Drawable.
+ encodeBuffer.encodeXidValue(GetULONG(inputMessage + 4,
+ bigEndian_), clientCache_ -> drawableCache);
+ // X.
+ unsigned int x = GetUINT(inputMessage + 8, bigEndian_);
+ int xDiff = x - clientCache_ -> putImageLastX;
+ clientCache_ -> putImageLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache_ -> putImageXCache, 8);
+ // Y.
+ unsigned int y = GetUINT(inputMessage + 10, bigEndian_);
+ int yDiff = y - clientCache_ -> putImageLastY;
+ clientCache_ -> putImageLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache_ -> putImageYCache, 8);
+ // Width.
+ unsigned int width = GetUINT(inputMessage + 12, bigEndian_);
+ encodeBuffer.encodeCachedValue(width, 16,
+ clientCache_ -> putImageWidthCache, 8);
+ // Height.
+ unsigned int height = GetUINT(inputMessage + 14, bigEndian_);
+ encodeBuffer.encodeCachedValue(height, 16,
+ clientCache_ -> putImageHeightCache, 8);
+ // Plane mask.
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_), 32,
+ clientCache_ -> getImagePlaneMaskCache, 5);
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetPointerMapping:
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ case X_GetKeyboardControl:
+ {
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+
+ priority_++;
+ }
+ break;
+ default:
+ {
+ if (inputOpcode == opcodeStore_ -> renderExtension)
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalRenderExtension);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> shapeExtension)
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalShapeExtension);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> putPackedImage)
+ {
+ #ifdef TARGETS
+
+ unsigned int t_id = GetULONG(inputMessage + 4, bigEndian_);
+
+ if (pixmaps.find(t_id) != pixmaps.end())
+ {
+ *logofs << "handleRead: X_NXPutPackedImage target id is pixmap "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else if (windows.find(t_id) != windows.end())
+ {
+ *logofs << "handleRead: X_NXPutPackedImage target id is window "
+ << t_id << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: X_NXPutPackedImage target id " << t_id
+ << " is unrecognized.\n" << logofs_flush;
+ }
+
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding packed image request for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // The field carries the destination data
+ // length. We add the request's size of
+ // the final X_PutImage.
+ //
+
+ unsigned int outputLength = GetULONG(inputMessage + 20, bigEndian_) + 24;
+
+ statistics -> addPackedBytesIn(inputLength);
+
+ statistics -> addPackedBytesOut(outputLength);
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXPutPackedImage);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> setUnpackColormap)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding set unpack colormap request "
+ << "for FD#" << fd_ << " with size " << inputLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackColormap);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> setUnpackAlpha)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding set unpack alpha request "
+ << "for FD#" << fd_ << " with size " << inputLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackAlpha);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> setUnpackGeometry)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding set unpack geometry request "
+ << "for FD#" << fd_ << " with size " << inputLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackGeometry);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+ }
+ else if (inputOpcode == opcodeStore_ -> startSplit)
+ {
+ if (handleStartSplitRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> endSplit)
+ {
+ if (handleEndSplitRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> commitSplit)
+ {
+ if (handleCommitSplitRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> abortSplit)
+ {
+ if (handleAbortSplitRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> finishSplit)
+ {
+ if (handleFinishSplitRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> freeSplit)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding free split request "
+ << "for FD#" << fd_ << " with size " << inputLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8,
+ clientCache_ -> resourceCache);
+ }
+ else if (inputOpcode == opcodeStore_ -> freeUnpack)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding free unpack request "
+ << "for FD#" << fd_ << " with size " << inputLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8,
+ clientCache_ -> resourceCache);
+ }
+ else if (inputOpcode == opcodeStore_ -> getControlParameters)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding get control parameters "
+ << "request for FD#" << fd_ << " with size "
+ << inputLength << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Add the reply to the write buffer. If found
+ // to contain a message, it it will be flushed
+ // to the X client before leaving the loop.
+ //
+
+ unsigned char *reply = writeBuffer_.addMessage(32);
+
+ *(reply + 0) = X_Reply;
+
+ PutUINT(clientSequence_, reply + 2, bigEndian_);
+
+ PutULONG(0, reply + 4, bigEndian_);
+
+ //
+ // Save the sequence number we used
+ // to auto-generate this reply.
+ //
+
+ lastSequence_ = clientSequence_;
+
+ #ifdef TEST
+ *logofs << "handleRead: Registered " << lastSequence_
+ << " as last auto-generated sequence number.\n"
+ << logofs_flush;
+ #endif
+
+ *(reply + 1) = control -> LinkMode;
+
+ *(reply + 8) = control -> LocalVersionMajor;
+ *(reply + 9) = control -> LocalVersionMinor;
+ *(reply + 10) = control -> LocalVersionPatch;
+
+ *(reply + 11) = control -> RemoteVersionMajor;
+ *(reply + 12) = control -> RemoteVersionMinor;
+ *(reply + 13) = control -> RemoteVersionPatch;
+
+ PutUINT(control -> SplitTimeout, reply + 14, bigEndian_);
+ PutUINT(control -> MotionTimeout, reply + 16, bigEndian_);
+
+ *(reply + 18) = control -> SplitMode;
+
+ PutULONG(control -> SplitDataThreshold, reply + 20, bigEndian_);
+
+ *(reply + 24) = control -> PackMethod;
+ *(reply + 25) = control -> PackQuality;
+
+ *(reply + 26) = control -> LocalDataCompressionLevel;
+ *(reply + 27) = control -> LocalStreamCompressionLevel;
+ *(reply + 28) = control -> LocalDeltaCompression;
+
+ *(reply + 29) = (control -> LocalDeltaCompression == 1 &&
+ control -> PersistentCacheEnableLoad == 1);
+ *(reply + 30) = (control -> LocalDeltaCompression == 1 &&
+ control -> PersistentCacheEnableSave == 1);
+ *(reply + 31) = (control -> LocalDeltaCompression == 1 &&
+ control -> PersistentCacheEnableLoad == 1 &&
+ control -> PersistentCacheName != NULL);
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> getCleanupParameters)
+ {
+ #ifdef WARNING
+ *logofs << "handleRead: WARNING! Encoding fake get cleanup "
+ << "parameters request for FD#" << fd_ << " with size "
+ << inputLength << ".\n" << logofs_flush;
+ #endif
+ }
+ else if (inputOpcode == opcodeStore_ -> getImageParameters)
+ {
+ #ifdef WARNING
+ *logofs << "handleRead: WARNING! Encoding fake get cleanup "
+ << "parameters request for FD#" << fd_ << " with size "
+ << inputLength << ".\n" << logofs_flush;
+ #endif
+ }
+ else if (inputOpcode == opcodeStore_ -> getUnpackParameters)
+ {
+ #ifdef DEBUG
+ *logofs << "handleRead: Encoding get unpack parameters "
+ << "request for FD#" << fd_ << " with size "
+ << inputLength << ".\n" << logofs_flush;
+ #endif
+
+ sequenceQueue_.push(clientSequence_, inputOpcode);
+ }
+ else if (inputOpcode == opcodeStore_ -> getShmemParameters)
+ {
+ if (handleShmemRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> setExposeParameters)
+ {
+ //
+ // Enable or disable expose events
+ // coming from the real server.
+ //
+
+ encodeBuffer.encodeBoolValue(*(inputMessage + 4));
+ encodeBuffer.encodeBoolValue(*(inputMessage + 5));
+ encodeBuffer.encodeBoolValue(*(inputMessage + 6));
+ }
+ else if (inputOpcode == opcodeStore_ -> setCacheParameters)
+ {
+ if (handleCacheRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (inputOpcode == opcodeStore_ -> getFontParameters)
+ {
+ if (handleFontRequest(encodeBuffer, inputOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalGenericRequest);
+
+ hit = handleEncode(encodeBuffer, clientCache_, messageStore,
+ inputOpcode, inputMessage, inputLength);
+
+ //
+ // Don't flush if the opcode is unrecognized.
+ // We may optionally flush it is an extension
+ // but would penalize the well written clients.
+ //
+ // if (inputOpcode > 127)
+ // {
+ // priority_++;
+ // }
+ //
+ }
+ }
+ } // End of switch on opcode.
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ const char *cacheString = (hit ? "cached " : "");
+
+ *logofs << "handleRead: Handled " << cacheString << "request OPCODE#"
+ << (unsigned int) inputOpcode << " (" << DumpOpcode(inputOpcode)
+ << ")" << " for FD#" << fd_ << " sequence " << clientSequence_
+ << ". " << inputLength << " bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+
+ #endif
+
+ if (hit)
+ {
+ statistics -> addCachedRequest(inputOpcode);
+ }
+
+ statistics -> addRequestBits(inputOpcode, inputLength << 3, bits);
+
+ if (inputOpcode == opcodeStore_ -> renderExtension)
+ {
+ if (hit)
+ {
+ statistics -> addRenderCachedRequest(*(inputMessage + 1));
+ }
+
+ statistics -> addRenderRequestBits(*(inputMessage + 1), inputLength << 3, bits);
+ }
+
+ } // End if (firstRequest_)... else ...
+
+ } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != 0) ...
+
+ //
+ // Check if we need to flush because of
+ // prioritized data.
+ //
+
+ if (priority_ > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of " << priority_ << " prioritized "
+ << "messages for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncPriority() < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset the priority flag.
+ //
+
+ priority_ = 0;
+ }
+
+ //
+ // Flush if we exceeded the token length.
+ //
+
+ if (proxy -> canAsyncFlush() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of token length exceeded.\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncFlush() < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (transport_ -> pending() != 0 ||
+ readBuffer_.checkMessage() != 0)
+ {
+ *logofs << "handleRead: PANIC! Buffer for X descriptor FD#"
+ << fd_ << " has " << transport_ -> pending()
+ << " bytes to read.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Reset the read buffer.
+ //
+
+ readBuffer_.fullReset();
+
+ return 1;
+}
+
+//
+// End of handleRead().
+//
+
+//
+// Beginning of handleWrite().
+//
+
+int ClientChannel::handleWrite(const unsigned char *message, unsigned int length)
+{
+ #ifdef TEST
+ *logofs << "handleWrite: Called for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Create the buffer from which to
+ // decode messages.
+ //
+
+ DecodeBuffer decodeBuffer(message, length);
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleWrite: Decoding messages for FD#" << fd_
+ << " with " << length << " bytes in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ if (firstReply_)
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: First reply detected.\n" << logofs_flush;
+ #endif
+
+ unsigned int outputOpcode;
+
+ decodeBuffer.decodeValue(outputOpcode, 8);
+ unsigned int secondByte;
+ decodeBuffer.decodeValue(secondByte, 8);
+ unsigned int major;
+ decodeBuffer.decodeValue(major, 16);
+ unsigned int minor;
+ decodeBuffer.decodeValue(minor, 16);
+ unsigned int extraLength;
+ decodeBuffer.decodeValue(extraLength, 16);
+ unsigned int outputLength = 8 + (extraLength << 2);
+
+ unsigned char *outputMessage = writeBuffer_.addMessage(outputLength);
+ *outputMessage = (unsigned char) outputOpcode;
+ outputMessage[1] = (unsigned char) secondByte;
+ PutUINT(major, outputMessage + 2, bigEndian_);
+ PutUINT(minor, outputMessage + 4, bigEndian_);
+ PutUINT(extraLength, outputMessage + 6, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ unsigned int cached;
+ decodeBuffer.decodeBoolValue(cached);
+
+ if (cached)
+ {
+ memcpy(nextDest, ServerCache::lastInitReply.getData(), outputLength - 8);
+ }
+ else
+ {
+ for (unsigned i = 8; i < outputLength; i++)
+ {
+ unsigned int nextByte;
+ decodeBuffer.decodeValue(nextByte, 8);
+ *nextDest++ = (unsigned char) nextByte;
+ }
+
+ ServerCache::lastInitReply.set(outputLength - 8, outputMessage + 8);
+ }
+
+ imageByteOrder_ = outputMessage[30];
+ bitmapBitOrder_ = outputMessage[31];
+ scanlineUnit_ = outputMessage[32];
+ scanlinePad_ = outputMessage[33];
+
+ firstReply_ = 0;
+
+ } // End of if (firstReply_)
+
+ //
+ // This was previously in a 'else' block.
+ // Due to the way the first request was
+ // handled, we could not decode multiple
+ // messages in the first frame.
+ //
+
+ { // Start of the decoding block.
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Starting loop on opcodes.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char outputOpcode;
+
+ //
+ // NX client needs this line to consider
+ // the initialization phase successfully
+ // completed.
+ //
+
+ if (firstClient_ == -1)
+ {
+ cerr << "Info" << ": Established X client connection.\n" ;
+
+ firstClient_ = fd_;
+ }
+
+ while (decodeBuffer.decodeOpcodeValue(outputOpcode, serverCache_ -> opcodeCache, 1))
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoded a new OPCODE#"
+ << (unsigned int) outputOpcode << ".\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char *outputMessage = NULL;
+ unsigned int outputLength = 0;
+
+ //
+ // General-purpose temp variables
+ // for decoding ints and chars.
+ //
+
+ unsigned int value = 0;
+ unsigned char cValue = 0;
+
+ //
+ // Check first if we need to abort any split,
+ // then if this is a reply, finally if it is
+ // en event or error.
+ //
+
+ if (outputOpcode == opcodeStore_ -> splitEvent)
+ {
+ //
+ // It's an abort split, not a normal
+ // burst of proxy data.
+ //
+
+ handleSplitEvent(decodeBuffer);
+
+ continue;
+ }
+ else if (outputOpcode == X_Reply)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding sequence number of reply.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int sequenceNum;
+ unsigned int sequenceDiff;
+
+ decodeBuffer.decodeCachedValue(sequenceDiff, 16,
+ serverCache_ -> replySequenceCache, 7);
+
+ sequenceNum = (serverSequence_ + sequenceDiff) & 0xffff;
+
+ serverSequence_ = sequenceNum;
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Last server sequence number for FD#"
+ << fd_ << " is " << serverSequence_ << " with "
+ << "difference " << sequenceDiff << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // In case of reply we can follow the X server and
+ // override any event's sequence number generated
+ // by this side.
+ //
+
+ #ifdef TEST
+ *logofs << "handleWrite: Updating last event's sequence "
+ << lastSequence_ << " to reply's sequence number "
+ << serverSequence_ << " for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ lastSequence_ = serverSequence_;
+
+ unsigned short int requestSequenceNum;
+ unsigned char requestOpcode;
+
+ #ifdef DEBUG
+
+ requestSequenceNum = 0;
+ requestOpcode = 0;
+
+ *logofs << "handleWrite: Peek of sequence number returns ";
+
+ *logofs << sequenceQueue_.peek(requestSequenceNum, requestOpcode);
+
+ *logofs << " with sequence " << requestSequenceNum << " and opcode "
+ << (unsigned int) requestOpcode << ".\n" << logofs_flush;
+
+ #endif
+
+ if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) == 1 &&
+ (requestSequenceNum == sequenceNum))
+ {
+ unsigned int requestData[3];
+
+ sequenceQueue_.pop(requestSequenceNum, requestOpcode,
+ requestData[0], requestData[1], requestData[2]);
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Identified reply to OPCODE#"
+ << (unsigned int) requestOpcode << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Is differential encoding disabled?
+ //
+
+ if (control -> RemoteDeltaCompression == 0)
+ {
+ int result = handleFastWriteReply(decodeBuffer, requestOpcode,
+ outputMessage, outputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ switch (requestOpcode)
+ {
+ case X_AllocColor:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned char *nextDest = outputMessage + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ PutUINT(requestData[i], nextDest, bigEndian_);
+ }
+ else
+ {
+ decodeBuffer.decodeValue(value, 16, 6);
+ PutUINT(requestData[i] + value, nextDest, bigEndian_);
+ }
+ nextDest += 2;
+ }
+ decodeBuffer.decodeValue(value, 32, 9);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+ }
+ break;
+ case X_GetAtomName:
+ {
+ unsigned int nameLength;
+ decodeBuffer.decodeValue(nameLength, 16, 6);
+ outputLength = RoundUp4(nameLength) + 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(nameLength, outputMessage + 8, bigEndian_);
+ unsigned char* nextDest = outputMessage + 32;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, nameLength);
+ }
+ break;
+ case X_GetGeometry:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> depthCache);
+ outputMessage[1] = cValue;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> getGeometryRootCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache_ -> getGeometryGeomCache[i], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ }
+ break;
+ case X_GetInputFocus:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> getInputFocusWindowCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ }
+ break;
+ case X_GetKeyboardMapping:
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ unsigned int dataLength =
+ ServerCache::getKeyboardMappingLastMap.getLength();
+ outputLength = 32 + dataLength;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] =
+ ServerCache::getKeyboardMappingLastKeysymsPerKeycode;
+ memcpy(outputMessage + 32,
+ ServerCache::getKeyboardMappingLastMap.getData(),
+ dataLength);
+ break;
+ }
+ unsigned int numKeycodes;
+ decodeBuffer.decodeValue(numKeycodes, 8);
+ unsigned int keysymsPerKeycode;
+ decodeBuffer.decodeValue(keysymsPerKeycode, 8, 4);
+ ServerCache::getKeyboardMappingLastKeysymsPerKeycode =
+ keysymsPerKeycode;
+ outputLength = 32 + numKeycodes * keysymsPerKeycode * 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) keysymsPerKeycode;
+ unsigned char *nextDest = outputMessage + 32;
+ unsigned char previous = 0;
+ for (unsigned int count = numKeycodes * keysymsPerKeycode;
+ count; --count)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ PutULONG((unsigned int) NoSymbol, nextDest, bigEndian_);
+ else
+ {
+ unsigned int keysym;
+ decodeBuffer.decodeCachedValue(keysym, 24,
+ serverCache_ -> getKeyboardMappingKeysymCache, 9);
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> getKeyboardMappingLastByteCache, 5);
+ previous += cValue;
+ PutULONG((keysym << 8) | previous, nextDest, bigEndian_);
+ }
+ nextDest += 4;
+ }
+ ServerCache::getKeyboardMappingLastMap.set(outputLength - 32,
+ outputMessage + 32);
+ }
+ break;
+ case X_GetModifierMapping:
+ {
+ unsigned int keycodesPerModifier;
+ decodeBuffer.decodeValue(keycodesPerModifier, 8);
+ outputLength = 32 + (keycodesPerModifier << 3);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) keycodesPerModifier;
+ unsigned char *nextDest = outputMessage + 32;
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ memcpy(outputMessage + 32,
+ ServerCache::getModifierMappingLastMap.getData(),
+ ServerCache::getModifierMappingLastMap.getLength());
+ break;
+ }
+ for (unsigned int count = outputLength - 32; count; count--)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ *nextDest++ = 0;
+ else
+ {
+ decodeBuffer.decodeValue(value, 8);
+ *nextDest++ = value;
+ }
+ }
+ ServerCache::getModifierMappingLastMap.set(outputLength - 32,
+ outputMessage + 32);
+ }
+ break;
+ case X_GetProperty:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_GetProperty);
+
+ handleDecode(decodeBuffer, serverCache_, messageStore,
+ requestOpcode, outputMessage, outputLength);
+ }
+ break;
+ case X_GetSelectionOwner:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> getSelectionOwnerCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ }
+ break;
+ case X_GetWindowAttributes:
+ {
+ outputLength = 44;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> visualCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> getWindowAttributesClassCache, 3);
+ PutUINT(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> getWindowAttributesBitGravityCache);
+ outputMessage[14] = cValue;
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> getWindowAttributesWinGravityCache);
+ outputMessage[15] = cValue;
+ decodeBuffer.decodeCachedValue(value, 32,
+ serverCache_ -> getWindowAttributesPlanesCache, 9);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ serverCache_ -> getWindowAttributesPixelCache, 9);
+ PutULONG(value, outputMessage + 20, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[24] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[25] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[26] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[27] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> colormapCache, 9);
+ PutULONG(value, outputMessage + 28, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ serverCache_ -> getWindowAttributesAllEventsCache);
+ PutULONG(value, outputMessage + 32, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ serverCache_ -> getWindowAttributesYourEventsCache);
+ PutULONG(value, outputMessage + 36, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> getWindowAttributesDontPropagateCache);
+ PutUINT(value, outputMessage + 40, bigEndian_);
+ }
+ break;
+ case X_GrabKeyboard:
+ case X_GrabPointer:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 3);
+ outputMessage[1] = (unsigned char) value;
+ }
+ break;
+ case X_InternAtom:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 29, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ }
+ break;
+ case X_ListExtensions:
+ {
+ decodeBuffer.decodeValue(value, 32, 8);
+ outputLength = 32 + (value << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned int numExtensions;
+ decodeBuffer.decodeValue(numExtensions, 8);
+ outputMessage[1] = (unsigned char) numExtensions;
+ unsigned char *nextDest = outputMessage + 32;
+ for (; numExtensions; numExtensions--)
+ {
+ unsigned int _length;
+ decodeBuffer.decodeValue(_length, 8);
+ *nextDest++ = (unsigned char) _length;
+ for (; _length; _length--)
+ {
+ decodeBuffer.decodeValue(value, 8);
+ *nextDest++ = value;
+ }
+ }
+ }
+ break;
+ case X_ListFonts:
+ {
+ //
+ // Differential compression can achieve a 12:1 to 14:1
+ // ratio, while the best ZLIB compression can achieve
+ // a mere 4:1 to 5:1. In the first case, though, the
+ // huge amount of data constituting the message would
+ // be stored uncompressed at the remote side. We need
+ // to find a compromise. The solution is to use diffe-
+ // rential compression at startup and ZLIB compression
+ // later on.
+ //
+
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_ListFonts);
+
+ if (handleDecodeCached(decodeBuffer, serverCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ decodeBuffer.decodeValue(value, 32, 8);
+ outputLength = 32 + (value << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned int numFonts;
+ decodeBuffer.decodeValue(numFonts, 16, 6);
+ PutUINT(numFonts, outputMessage + 8, bigEndian_);
+
+ // Differential or plain data compression?
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ unsigned char* nextDest = outputMessage + 32;
+ for (; numFonts; numFonts--)
+ {
+ unsigned int _length;
+ decodeBuffer.decodeValue(_length, 8);
+ *nextDest++ = (unsigned char)_length;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, _length);
+ nextDest += _length;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ else
+ {
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ else if (decompressed > 0)
+ {
+ handleSave(messageStore, outputMessage, outputLength,
+ compressedData, compressedDataSize);
+ }
+ else
+ {
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ }
+ }
+ break;
+ case X_LookupColor:
+ case X_AllocNamedColor:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned char *nextDest = outputMessage + 8;
+ if (requestOpcode == X_AllocNamedColor)
+ {
+ decodeBuffer.decodeValue(value, 32, 9);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ unsigned int count = 3;
+ do
+ {
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, nextDest, bigEndian_);
+ unsigned int visualColor;
+ decodeBuffer.decodeValue(visualColor, 16, 5);
+ visualColor += value;
+ visualColor &= 0xffff;
+ PutUINT(visualColor, nextDest + 6, bigEndian_);
+ nextDest += 2;
+ }
+ while (--count);
+ }
+ break;
+ case X_QueryBestSize:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 16, 8);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 8);
+ PutUINT(value, outputMessage + 10, bigEndian_);
+ }
+ break;
+ case X_QueryColors:
+ {
+ // Differential or plain data compression?
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ unsigned int numColors =
+ serverCache_ -> queryColorsLastReply.getLength() / 6;
+ outputLength = 32 + (numColors << 3);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(numColors, outputMessage + 8, bigEndian_);
+ const unsigned char *nextSrc =
+ serverCache_ -> queryColorsLastReply.getData();
+ unsigned char *nextDest = outputMessage + 32;
+ for (; numColors; numColors--)
+ {
+ for (unsigned int i = 0; i < 6; i++)
+ *nextDest++ = *nextSrc++;
+ nextDest += 2;
+ }
+ }
+ else
+ {
+ unsigned int numColors;
+ decodeBuffer.decodeValue(numColors, 16, 5);
+ outputLength = 32 + (numColors << 3);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(numColors, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 32;
+ for (unsigned int c = 0; c < numColors; c++)
+ {
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeValue(value, 16);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ }
+ serverCache_ -> queryColorsLastReply.set(numColors * 6,
+ outputMessage + 32);
+ const unsigned char *nextSrc = nextDest - 1;
+ nextDest = outputMessage + 32 + ((numColors - 1) << 3) + 5;
+ for (; numColors > 1; numColors--)
+ {
+ for (unsigned int i = 0; i < 6; i++)
+ *nextDest-- = *nextSrc--;
+ nextDest -= 2;
+ }
+ }
+ }
+ else
+ {
+ // Reply length.
+ unsigned int numColors;
+ decodeBuffer.decodeValue(numColors, 16, 5);
+ outputLength = 32 + (numColors << 3);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(numColors, outputMessage + 8, bigEndian_);
+
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, requestOpcode, 32,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ break;
+ case X_QueryExtension:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[8] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[9] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[10] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[11] = (unsigned char) value;
+
+ //
+ // We use a predefined opcode to address
+ // extensions' message stores, while real
+ // opcodes are used for communication with
+ // X server and clients.
+ //
+
+ if (requestData[0] == X_NXInternalShapeExtension)
+ {
+ opcodeStore_ -> shapeExtension = outputMessage[9];
+
+ #ifdef TEST
+ *logofs << "handleWrite: Shape extension opcode for FD#" << fd_
+ << " is " << (unsigned int) opcodeStore_ -> shapeExtension
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else if (requestData[0] == X_NXInternalRenderExtension)
+ {
+ opcodeStore_ -> renderExtension = outputMessage[9];
+
+ #ifdef TEST
+ *logofs << "handleWrite: Render extension opcode for FD#" << fd_
+ << " is " << (unsigned int) opcodeStore_ -> renderExtension
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ }
+ break;
+ case X_QueryFont:
+ {
+ //
+ // Use differential compression at startup and plain
+ // data compression later. Check X_ListFonts message
+ // for an explaination.
+ //
+
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_QueryFont);
+
+ if (handleDecodeCached(decodeBuffer, serverCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ // Differential or plain data compression?
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ unsigned int numProperties;
+ unsigned int numCharInfos;
+ decodeBuffer.decodeValue(numProperties, 16, 8);
+ decodeBuffer.decodeValue(numCharInfos, 32, 10);
+ outputLength = 60 + numProperties * 8 + numCharInfos * 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(numProperties, outputMessage + 46, bigEndian_);
+ PutULONG(numCharInfos, outputMessage + 56, bigEndian_);
+ handleDecodeCharInfo(decodeBuffer, outputMessage + 8);
+ handleDecodeCharInfo(decodeBuffer, outputMessage + 24);
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, outputMessage + 40, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, outputMessage + 42, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, outputMessage + 44, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[48] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[49] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[50] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[51] = (unsigned char) value;
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, outputMessage + 52, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, outputMessage + 54, bigEndian_);
+ unsigned char *nextDest = outputMessage + 60;
+ decodeBuffer.decodeBoolValue(value);
+
+ int end = 0;
+
+ if (value == 1)
+ {
+ unsigned int index;
+ decodeBuffer.decodeValue(index, 4);
+ unsigned int _length;
+ const unsigned char *data;
+ ServerCache::queryFontFontCache.get(index, _length, data);
+ memcpy(nextDest, data, _length);
+
+ end = 1;
+ }
+
+ if (end == 0)
+ {
+ unsigned char *saveDest = nextDest;
+ unsigned int _length = numProperties * 8 + numCharInfos * 12;
+ for (; numProperties; numProperties--)
+ {
+ decodeBuffer.decodeValue(value, 32, 9);
+ PutULONG(value, nextDest, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 9);
+ PutULONG(value, nextDest + 4, bigEndian_);
+ nextDest += 8;
+ }
+ for (; numCharInfos; numCharInfos--)
+ {
+ handleDecodeCharInfo(decodeBuffer, nextDest);
+
+ nextDest += 12;
+ }
+ ServerCache::queryFontFontCache.set(_length, saveDest);
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ else
+ {
+ // Reply length.
+ unsigned int replyLength;
+ decodeBuffer.decodeValue(replyLength, 32, 16);
+ outputLength = 32 + (replyLength << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, requestOpcode, messageStore -> dataOffset,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ else if (decompressed > 0)
+ {
+ handleSave(messageStore, outputMessage, outputLength,
+ compressedData, compressedDataSize);
+ }
+ else
+ {
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ }
+ }
+ break;
+ case X_QueryPointer:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> queryPointerRootCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> queryPointerChildCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyRootXCache, 8);
+ serverCache_ -> motionNotifyLastRootX += value;
+ PutUINT(serverCache_ -> motionNotifyLastRootX, outputMessage + 16,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyRootYCache, 8);
+ serverCache_ -> motionNotifyLastRootY += value;
+ PutUINT(serverCache_ -> motionNotifyLastRootY, outputMessage + 18,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyEventXCache, 8);
+ PutUINT(serverCache_ -> motionNotifyLastRootX + value,
+ outputMessage + 20, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyEventYCache, 8);
+ PutUINT(serverCache_ -> motionNotifyLastRootY + value,
+ outputMessage + 22, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyStateCache);
+ PutUINT(value, outputMessage + 24, bigEndian_);
+ }
+ break;
+ case X_QueryTree:
+ {
+ unsigned int children;
+ decodeBuffer.decodeValue(children, 16, 8);
+
+ outputLength = 32 + (children << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ PutULONG(outputLength, outputMessage + 4, bigEndian_);
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> queryTreeWindowCache);
+
+ PutULONG(value, outputMessage + 8, bigEndian_);
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> queryTreeWindowCache);
+
+ PutULONG(value, outputMessage + 12, bigEndian_);
+
+ unsigned char *next = outputMessage + 32;
+
+ PutUINT(children, outputMessage + 16, bigEndian_);
+
+ for (unsigned int i = 0; i < children; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> queryTreeWindowCache);
+
+ PutULONG(value, next + (i * 4), bigEndian_);
+ }
+ }
+ break;
+ case X_TranslateCoords:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> translateCoordsChildCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> translateCoordsXCache, 8);
+ PutUINT(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> translateCoordsYCache, 8);
+ PutUINT(value, outputMessage + 14, bigEndian_);
+ }
+ break;
+ case X_GetImage:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_GetImage);
+
+ if (handleDecodeCached(decodeBuffer, serverCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ // Depth.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> depthCache);
+ // Reply length.
+ unsigned int replyLength;
+ decodeBuffer.decodeValue(replyLength, 32, 9);
+ outputLength = 32 + (replyLength << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) cValue;
+ // Visual.
+ unsigned int visual;
+ decodeBuffer.decodeCachedValue(visual, 29,
+ serverCache_ -> visualCache);
+ PutULONG(visual, outputMessage + 8, bigEndian_);
+
+ // Since ProtoStep8 (#issue 108)
+ handleCopy(decodeBuffer, requestOpcode, messageStore ->
+ dataOffset, outputMessage, outputLength);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_GetPointerMapping:
+ {
+ unsigned int nextByte;
+ decodeBuffer.decodeValue(nextByte, 8, 4);
+ unsigned int replyLength;
+ decodeBuffer.decodeValue(replyLength, 32, 4);
+ outputLength = 32 + (replyLength << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) nextByte;
+ unsigned char *nextDest = outputMessage + 32;
+ for (unsigned int i = 32; i < outputLength; i++)
+ {
+ decodeBuffer.decodeValue(nextByte, 8, 4);
+ *nextDest++ = (unsigned char) nextByte;
+ }
+ }
+ break;
+ case X_GetKeyboardControl:
+ {
+ unsigned int nextByte;
+ decodeBuffer.decodeValue(nextByte, 8, 2);
+ unsigned int replyLength;
+ decodeBuffer.decodeValue(replyLength, 32, 8);
+ outputLength = 32 + (replyLength << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) nextByte;
+ unsigned char *nextDest = outputMessage + 8;
+ for (unsigned int i = 8; i < outputLength; i++)
+ {
+ decodeBuffer.decodeValue(nextByte, 8, 4);
+ *nextDest++ = (unsigned char) nextByte;
+ }
+ }
+ break;
+ default:
+ {
+ if (requestOpcode == opcodeStore_ -> getUnpackParameters)
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Received get unpack parameters reply "
+ << "OPCODE#" << (unsigned int) opcodeStore_ -> getUnpackParameters
+ << ".\n" << logofs_flush;
+ #endif
+
+ outputLength = 32 + PACK_METHOD_LIMIT;
+
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ unsigned int method;
+
+ //
+ // Let agent use only the unpack methods
+ // implemented at both sides.
+ //
+
+ for (int i = 0; i < PACK_METHOD_LIMIT; i++)
+ {
+ decodeBuffer.decodeBoolValue(method);
+
+ control -> RemoteUnpackMethods[i] = method;
+
+ *(outputMessage + 32 + i) =
+ (control -> LocalUnpackMethods[i] == 1 &&
+ method == 1);
+ }
+ }
+ else if (requestOpcode == opcodeStore_ -> getShmemParameters)
+ {
+ if (handleShmemReply(decodeBuffer, requestOpcode,
+ outputMessage, outputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (requestOpcode == opcodeStore_ -> getFontParameters)
+ {
+ if (handleFontReply(decodeBuffer, requestOpcode,
+ outputMessage, outputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleWrite: PANIC! No matching request for "
+ << "reply with sequence number " << sequenceNum
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No matching request for "
+ << "reply with sequence number " << sequenceNum
+ << ".\n";
+
+ return -1;
+ }
+ }
+ }
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Handled reply to OPCODE#"
+ << (unsigned) requestOpcode << " (" << DumpOpcode(requestOpcode)
+ << ")" << " for FD#" << fd_ << " with sequence " << serverSequence_
+ << ". Output size is " << outputLength << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addRepliedRequest(requestOpcode);
+ }
+ else // End of if (sequenceQueue_.peek() && ...)
+ {
+ //
+ // Reply didn't match any request opcode.
+ // Check again if differential encoding
+ // is disabled.
+ //
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Identified generic reply.\n"
+ << logofs_flush;
+ #endif
+
+ requestOpcode = X_Reply;
+
+ if (control -> RemoteDeltaCompression == 0)
+ {
+ int result = handleFastWriteReply(decodeBuffer, requestOpcode,
+ outputMessage, outputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ //
+ // All replies whose opcode is not pushed in
+ // sequence number queue are cached together.
+ // Among such replies are those to extension
+ // requests.
+ //
+
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_NXInternalGenericReply);
+
+ handleDecode(decodeBuffer, serverCache_, messageStore,
+ requestOpcode, outputMessage, outputLength);
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Handled generic reply for FD#" << fd_
+ << " with sequence " << serverSequence_ << ". Output size is "
+ << outputLength << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addRepliedRequest(requestOpcode);
+
+ } // End of if (sequenceQueue_.peek() && ...) else ...
+
+ //
+ // If any output was produced then write opcode,
+ // sequence number and size to the buffer.
+ //
+
+ if (outputLength > 0)
+ {
+ *outputMessage = outputOpcode;
+
+ PutUINT(serverSequence_, outputMessage + 2, bigEndian_);
+
+ PutULONG((outputLength - 32) >> 2, outputMessage + 4, bigEndian_);
+ }
+
+ } // End of if (outputOpcode == 1)...
+ else
+ {
+ //
+ // It's an event or error.
+ //
+
+ unsigned int sequenceNum;
+ unsigned int sequenceDiff;
+
+ decodeBuffer.decodeCachedValue(sequenceDiff, 16,
+ serverCache_ -> eventSequenceCache, 7);
+
+ sequenceNum = (serverSequence_ + sequenceDiff) & 0xffff;
+
+ serverSequence_ = sequenceNum;
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Last server sequence number for FD#"
+ << fd_ << " is " << serverSequence_ << " with "
+ << "difference " << sequenceDiff << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if this is an error that matches
+ // a sequence number for which we were
+ // expecting a reply.
+ //
+
+ if (outputOpcode == X_Error)
+ {
+ unsigned short int errorSequenceNum;
+ unsigned char errorOpcode;
+
+ if (sequenceQueue_.peek(errorSequenceNum, errorOpcode) &&
+ ((unsigned) errorSequenceNum == serverSequence_))
+ {
+ //
+ // Remove the queued sequence of the reply.
+ //
+
+ #ifdef TEST
+ *logofs << "handleWrite: WARNING! Removing reply to OPCODE#"
+ << (unsigned) errorOpcode << " sequence "
+ << errorSequenceNum << " for FD#" << fd_
+ << " due to error.\n" << logofs_flush;
+ #endif
+
+ sequenceQueue_.pop(errorSequenceNum, errorOpcode);
+
+ //
+ // Send to the client the current sequence
+ // number, not the number that matched the
+ // reply. Because we are generating replies
+ // at our side, Xlib can incur in a sequence
+ // lost if the error comes after the auto-
+ // generated reply.
+ //
+
+ if (control -> SessionMode == session_proxy)
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Updating last event's sequence "
+ << lastSequence_ << " to X server's error sequence "
+ << "number " << serverSequence_ << " for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ lastSequence_ = serverSequence_;
+ }
+ }
+
+ //
+ // In case of errors always send to client the
+ // original X server's sequence associated to
+ // the failing request.
+ //
+
+ if (control -> SessionMode != session_proxy)
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Updating last event's sequence "
+ << lastSequence_ << " to X server's error sequence "
+ << "number " << serverSequence_ << " for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ lastSequence_ = serverSequence_;
+ }
+ }
+
+ //
+ // Check if by producing events at client side we
+ // have modified the events' sequence numbering.
+ // In this case taint the original sequence to
+ // comply with the last one known by client.
+ //
+
+/*
+FIXME: Recover the sequence number if the proxy
+ is not connected to an agent.
+*/
+ if (serverSequence_ > lastSequence_ ||
+ control -> SessionMode != session_proxy)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Updating last event's sequence "
+ << lastSequence_ << " to X server's sequence number "
+ << serverSequence_ << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ lastSequence_ = serverSequence_;
+ }
+ #ifdef DEBUG
+ else if (serverSequence_ < lastSequence_)
+ {
+ //
+ // Use our last auto-generated sequence.
+ //
+
+ *logofs << "handleWrite: Tainting sequence number "
+ << serverSequence_ << " to last event's sequence "
+ << lastSequence_ << " for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // Check if remote side used fast encoding.
+ //
+
+ if (control -> RemoteDeltaCompression == 0)
+ {
+ int result = handleFastWriteEvent(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ //
+ // Make space for message in the outgoing buffer
+ // and write opcode and sequence number.
+ //
+
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ *outputMessage = outputOpcode;
+
+ PutUINT(lastSequence_, outputMessage + 2, bigEndian_);
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Going to handle event or error OPCODE#"
+ << (unsigned int) outputOpcode << " for FD#" << fd_
+ << " sequence " << lastSequence_ << " (real was "
+ << serverSequence_ << ").\n" << logofs_flush;
+ #endif
+
+ switch (outputOpcode)
+ {
+ case X_Error:
+ {
+ unsigned char code;
+ decodeBuffer.decodeCachedValue(code, 8,
+ serverCache_ -> errorCodeCache);
+ outputMessage[1] = code;
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Handled error ERR_CODE#"
+ << (unsigned int) code << " for FD#" << fd_;
+ #endif
+
+ if ((code != 11) && (code != 8) &&
+ (code != 15) && (code != 1))
+ {
+ decodeBuffer.decodeValue(value, 32, 16);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << " RES_ID#" << value;
+ #endif
+ }
+
+ if (code >= 18)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> errorMinorCache);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << " MIN_OP#" << value;
+ #endif
+ }
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> errorMajorCache);
+ outputMessage[10] = cValue;
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << " MAJ_OP#" << (unsigned int) cValue;
+ #endif
+
+ if (code >= 18)
+ {
+ unsigned char *nextDest = outputMessage + 11;
+ for (unsigned int i = 11; i < 32; i++)
+ {
+ decodeBuffer.decodeValue(value, 8);
+ *nextDest++ = (unsigned char) cValue;
+ }
+ }
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << " sequence " << lastSequence_ << " (real was "
+ << serverSequence_ << ") . Size is "
+ << (unsigned int) outputLength << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ case KeyPress:
+ case KeyRelease:
+ case MotionNotify:
+ case EnterNotify:
+ case LeaveNotify:
+ {
+ if (outputOpcode == MotionNotify)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ }
+ else if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify)
+ {
+ decodeBuffer.decodeValue(value, 3);
+ }
+ else if (outputOpcode == KeyRelease)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ value = serverCache_ -> keyPressLastKey;
+ }
+ else
+ {
+ decodeBuffer.decodeValue(value, 8);
+ }
+ }
+ else if (outputOpcode == ButtonPress || outputOpcode == ButtonRelease)
+ {
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> buttonCache);
+ value = (unsigned int) cValue;
+ }
+ else
+ {
+ decodeBuffer.decodeValue(value, 8);
+ }
+
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 32,
+ serverCache_ -> motionNotifyTimestampCache, 9);
+ serverCache_ -> lastTimestamp += value;
+ PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4,
+ bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ int skipRest = 0;
+ if (outputOpcode == KeyRelease)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ *nextDest++ = serverCache_ -> keyPressCache[i];
+ }
+ skipRest = 1;
+ }
+ }
+
+ if (!skipRest)
+ {
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ *serverCache_ -> motionNotifyWindowCache[i], 6);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyRootXCache, 6);
+ serverCache_ -> motionNotifyLastRootX += value;
+ PutUINT(serverCache_ -> motionNotifyLastRootX, outputMessage + 20,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyRootYCache, 6);
+ serverCache_ -> motionNotifyLastRootY += value;
+ PutUINT(serverCache_ -> motionNotifyLastRootY, outputMessage + 22,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyEventXCache, 6);
+ PutUINT(serverCache_ -> motionNotifyLastRootX + value,
+ outputMessage + 24, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyEventYCache, 6);
+ PutUINT(serverCache_ -> motionNotifyLastRootY + value,
+ outputMessage + 26, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> motionNotifyStateCache);
+ PutUINT(value, outputMessage + 28, bigEndian_);
+ if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify)
+ {
+ decodeBuffer.decodeValue(value, 2);
+ }
+ else
+ {
+ decodeBuffer.decodeBoolValue(value);
+ }
+ outputMessage[30] = (unsigned char) value;
+ if (outputOpcode == EnterNotify || outputOpcode == LeaveNotify)
+ {
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[31] = (unsigned char) value;
+ }
+ else if (outputOpcode == KeyPress)
+ {
+ serverCache_ -> keyPressLastKey = outputMessage[1];
+ for (unsigned int i = 8; i < 31; i++)
+ {
+ serverCache_ -> keyPressCache[i - 8] = outputMessage[i];
+ }
+ }
+ }
+ }
+ break;
+ case ColormapNotify:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> colormapNotifyWindowCache, 8);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> colormapNotifyColormapCache, 8);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[12] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[13] = (unsigned char) value;
+ }
+ break;
+ case ConfigureNotify:
+ {
+ unsigned char *nextDest = outputMessage + 4;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ *serverCache_ -> configureNotifyWindowCache[i], 9);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ for (unsigned int j = 0; j < 5; j++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache_ -> configureNotifyGeomCache[j], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ decodeBuffer.decodeBoolValue(value);
+ *nextDest = value;
+ }
+ break;
+ case CreateNotify:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> createNotifyWindowCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 29, 5);
+ serverCache_ -> createNotifyLastWindow += value;
+ serverCache_ -> createNotifyLastWindow &= 0x1fffffff;
+ PutULONG(serverCache_ -> createNotifyLastWindow, outputMessage + 8,
+ bigEndian_);
+ unsigned char* nextDest = outputMessage + 12;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ decodeBuffer.decodeValue(value, 16, 9);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ decodeBuffer.decodeBoolValue(value);
+ *nextDest = (unsigned char) value;
+ }
+ break;
+ case Expose:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> exposeWindowCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache_ -> exposeGeomCache[i], 6);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ }
+ break;
+ case FocusIn:
+ case FocusOut:
+ {
+ decodeBuffer.decodeValue(value, 3);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> focusInWindowCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[8] = (unsigned char) value;
+ }
+ break;
+ case KeymapNotify:
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ memcpy(outputMessage + 1, ServerCache::lastKeymap.getData(), 31);
+ else
+ {
+ unsigned char *nextDest = outputMessage + 1;
+ for (unsigned int i = 1; i < 32; i++)
+ {
+ decodeBuffer.decodeValue(value, 8);
+ *nextDest++ = (unsigned char) value;
+ }
+ ServerCache::lastKeymap.set(31, outputMessage + 1);
+ }
+ }
+ break;
+ case MapNotify:
+ case UnmapNotify:
+ case DestroyNotify:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> mapNotifyEventCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> mapNotifyWindowCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ if (outputOpcode == MapNotify || outputOpcode == UnmapNotify)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[12] = (unsigned char) value;
+ }
+ }
+ break;
+ case NoExpose:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> noExposeDrawableCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ serverCache_ -> noExposeMinorCache);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ serverCache_ -> noExposeMajorCache);
+ outputMessage[10] = cValue;
+ }
+ break;
+ case PropertyNotify:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> propertyNotifyWindowCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> propertyNotifyAtomCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 9);
+ serverCache_ -> lastTimestamp += value;
+ PutULONG(serverCache_ -> lastTimestamp, outputMessage + 12,
+ bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[16] = (unsigned char) value;
+ }
+ break;
+ case ReparentNotify:
+ {
+ unsigned char* nextDest = outputMessage + 4;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> reparentNotifyWindowCache, 9);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ decodeBuffer.decodeValue(value, 16, 6);
+ PutUINT(value, nextDest, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 6);
+ PutUINT(value, nextDest + 2, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[20] = (unsigned char)value;
+ }
+ break;
+ case SelectionClear:
+ {
+ decodeBuffer.decodeValue(value, 32, 9);
+ serverCache_ -> lastTimestamp += value;
+ PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearWindowCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearAtomCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ }
+ break;
+ case SelectionRequest:
+ {
+ decodeBuffer.decodeValue(value, 32, 9);
+ serverCache_ -> lastTimestamp += value;
+ PutULONG(serverCache_ -> lastTimestamp, outputMessage + 4,
+ bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearWindowCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearWindowCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearAtomCache, 9);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearAtomCache, 9);
+ PutULONG(value, outputMessage + 20, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> selectionClearAtomCache, 9);
+ PutULONG(value, outputMessage + 24, bigEndian_);
+ }
+ break;
+ case VisibilityNotify:
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache_ -> visibilityNotifyWindowCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[8] = (unsigned char) value;
+ }
+ break;
+ default:
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Using generic event compression "
+ << "for OPCODE#" << (unsigned int) outputOpcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(*(outputMessage + 1), 8,
+ serverCache_ -> genericEventCharCache);
+
+ for (unsigned int i = 0; i < 14; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache_ -> genericEventIntCache[i]);
+
+ PutUINT(value, outputMessage + i * 2 + 4, bigEndian_);
+ }
+ }
+ } // End of switch (outputOpcode)...
+
+ #if defined(TEST) || defined(OPCODES)
+ if (outputOpcode != X_Error)
+ {
+ *logofs << "handleWrite: Handled event OPCODE#"
+ << (unsigned int) outputOpcode << " for FD#"
+ << fd_ << " sequence " << lastSequence_ << " (real was "
+ << serverSequence_ << "). Size is " << outputLength
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Check if we need to suppress the error.
+ //
+
+ if (outputOpcode == X_Error &&
+ handleTaintSyncError(*(outputMessage + 10)) > 0)
+ {
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: WARNING! Suppressed error OPCODE#"
+ << (unsigned int) outputOpcode << " for FD#"
+ << fd_ << " sequence " << lastSequence_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ writeBuffer_.removeMessage(32);
+ }
+
+ } // End of if (outputOpcode == 1)... else ...
+
+ //
+ // Check if we produced enough data. We need to
+ // decode all provided messages. Just update the
+ // finish flag in case of failure.
+ //
+
+ handleFlush(flush_if_needed);
+
+ } // End of while (decodeBuffer.decodeOpcodeValue(outputOpcode, 8, ...
+
+ } // End of the decoding block.
+
+ //
+ // Write any remaining data to the X connection.
+ //
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+//
+// End of handleWrite().
+//
+
+//
+// Other members.
+//
+
+int ClientChannel::handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
+ T_store_action action, int position, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #if defined(TEST) || defined(SPLIT)
+
+ // Since ProtoStep8 (#issue 108)
+ *logofs << "handleSplit: PANIC! SPLIT! Split should "
+ << "not be enabled for message " << "OPCODE#"
+ << (unsigned int) store -> opcode() << ".\n"
+ << logofs_flush;
+
+ HandleCleanup();
+
+ #endif
+
+ //
+ // Refuse the split if it is not introduced
+ // by a start split.
+ //
+
+ // Since ProtoStep7 (#issue 108)
+ if (splitState_.resource == nothing || enableSplit_ == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Nothing to do for message "
+ << "OPCODE#" << (unsigned int) store -> opcode()
+ << " of size " << size << " position " << position
+ << " with action [" << DumpAction(action) << "] at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeBoolValue(0);
+
+ return 0;
+ }
+
+ //
+ // It's not advisable to allocate the store at
+ // the time we receive the start-split because
+ // we may process all the splits received and
+ // deallocate the store even before we receive
+ // the end split. Another message for the same
+ // split sequence may then come and we would
+ // have a null split store.
+ //
+
+ handleSplitStoreAlloc(&splitResources_, splitState_.resource);
+
+ //
+ // Check if the split was actually requested by
+ // the agent and if the request was saved in the
+ // message store. The split can also be refused
+ // if the message is smaller than the threshold
+ // or if the split store is already full.
+ //
+
+ if (mustSplitMessage(splitState_.resource) == 0)
+ {
+ if (action == IS_HIT || canSplitMessage(splitState_.mode, size) == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitState_.mode == split_none)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplit: PANIC! SPLIT! Split state has "
+ << "mode 'none'.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ if (action != IS_HIT && (int) size >=
+ control -> SplitDataThreshold)
+ {
+ #ifdef WARNING
+ *logofs << "handleSplit: WARNING! SPLIT! Split stores have "
+ << clientStore_ -> getSplitTotalSize() << " messages "
+ << "and " << clientStore_ -> getSplitTotalStorageSize()
+ << " allocated bytes.\n" << logofs_flush;
+ #endif
+ }
+
+ #endif
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Message OPCODE#"
+ << (unsigned int) store -> opcode() << " of size " << size
+ << " [not split] with resource " << splitState_.resource
+ << " mode " << splitState_.mode << " position " << position
+ << " and action [" << DumpAction(action) << "] at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeBoolValue(0);
+
+ return 0;
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Message OPCODE#"
+ << (unsigned int) store -> opcode() << " of size " << size
+ << " [split] with resource " << splitState_.resource
+ << " mode " << splitState_.mode << " position " << position
+ << " and action [" << DumpAction(action) << "] at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeBoolValue(1);
+
+ T_checksum checksum = NULL;
+
+ if (action == IS_ADDED)
+ {
+ checksum = store -> getChecksum(position);
+ }
+ else if (action == is_discarded)
+ {
+ //
+ // Generate the checksum on the fly.
+ //
+
+ checksum = store -> getChecksum(buffer, size, bigEndian_);
+ }
+
+ //
+ // The method must abort the connection
+ // if it can't allocate the split.
+ //
+
+ Split *splitMessage = clientStore_ -> getSplitStore(splitState_.resource) ->
+ add(store, splitState_.resource, splitState_.mode,
+ position, action, checksum, buffer, size);
+
+ //
+ // Send the checksum. By using the checksum,
+ // the remote end will try to locate the
+ // message and load it from disk.
+ //
+
+ if (action == IS_HIT)
+ {
+ splitMessage -> setState(split_loaded);
+ }
+ else if (handleSplitChecksum(encodeBuffer, checksum) == 0)
+ {
+ //
+ // If the checksum is not sent, for example
+ // because loading of messages from disk is
+ // disabled, then mark the split as missed.
+ //
+
+ #ifdef WARNING
+ *logofs << "handleSplit: WARNING! Checksum not sent. "
+ << "Marking the split as [missed].\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_missed);
+ }
+
+ if (action == is_discarded)
+ {
+ delete [] checksum;
+ }
+
+ //
+ // Check if we are ready to send a new split
+ // for this store.
+ //
+
+ handleSplitPending(splitState_.resource);
+
+ #if defined(TEST) || defined(SPLIT)
+
+ *logofs << "handleSplit: SPLIT! There are " << clientStore_ ->
+ getSplitTotalSize() << " messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+
+ clientStore_ -> dumpSplitStore(splitState_.resource);
+
+ #endif
+
+ return 1;
+}
+
+int ClientChannel::handleSplit(EncodeBuffer &encodeBuffer)
+{
+ //
+ // Determine the maximum amount of bytes
+ // we can write in this iteration.
+ //
+
+ int total = control -> SplitDataPacketLimit;
+
+ int bytes = total;
+ int splits = 0;
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Handling splits "
+ << "for FD#" << fd_ << " with " << clientStore_ ->
+ getSplitTotalSize() << " elements and " << total
+ << " bytes to write at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Looping to find "
+ << "if there is any split to send.\n"
+ << logofs_flush;
+ #endif
+
+ SplitStore *splitStore;
+
+ Split *splitMessage;
+
+ //
+ // Divide the available bandwidth among all the active
+ // split stores by implementing a simple round-robin
+ // mechanism. This can be extended by using an external
+ // function returning the number of bytes to be written
+ // based on the state of the split (splits which didn't
+ // receive yet a confirmation event could be delayed),
+ // the current bitrate, and by letting the agent asso-
+ // ciate a priority to the resource in the start split
+ // operation.
+ //
+
+ splitState_.pending = 0;
+
+ splitResources_.rotate();
+
+ //
+ // Copy the list since elements can be removed
+ // in the middle of the loop.
+ //
+
+ T_list splitList = splitResources_.copyList();
+
+ for (T_list::iterator j = splitList.begin();
+ j != splitList.end(); j++)
+ {
+ int resource = *j;
+
+ #ifdef DEBUG
+ *logofs << "handleSplit: SPLIT! Looping with current "
+ << "resource " << resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore != NULL)
+ {
+ //
+ // Don't send more than the the packet size
+ // bytes but ensure that we abort any split
+ // found in the disk cache.
+ //
+
+ for (;;)
+ {
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStore(resource);
+
+ #endif
+
+ splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage == NULL)
+ {
+ //
+ // We have created the store after a start
+ // split but no message was added yet.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: WARNING! SPLIT! The split store "
+ << "is still empty.\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+
+ //
+ // Splits already aborted can't be in the
+ // split store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> getState() == split_aborted)
+ {
+ *logofs << "handleSplit: PANIC! SPLIT! Found an "
+ << "aborted split in store [" << resource
+ << "].\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Check if there are more messages in the
+ // store that can be aborted or if we have
+ // exceeded the number of bytes we can send
+ // for this iteration.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Checking closure "
+ << "of the inner loop with " << bytes
+ << " bytes to write and split state ["
+ << DumpState(splitMessage -> getState())
+ << "].\n" << logofs_flush;
+ #endif
+
+ if ((splitMessage -> getMode() == split_sync &&
+ splitMessage -> getState() == split_added) ||
+ (bytes <= 0 && splitMessage ->
+ getState() != split_loaded))
+ {
+ break;
+ }
+
+ //
+ // If the split was loaded at the remote
+ // side abort it immediately.
+ //
+
+ if (splitMessage -> getState() == split_loaded)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Sending more data "
+ << "for store [" << resource << "] with "
+ << "a split to be aborted.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (bytes > 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Sending more data "
+ << "for store [" << resource << "] with "
+ << bytes << " bytes to send.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Check if the split store was deleted.
+ //
+
+ splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore == NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Exiting from the "
+ << "inner loop with split store [" << resource
+ << "] destroyed.\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Completed handling splits "
+ << "for store [" << resource << "] with " << bytes
+ << " bytes still to send.\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if there is still a split to
+ // send for the store just processed.
+ //
+
+ handleSplitPending(resource);
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splits == 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplit: PANIC! Function called but "
+ << "no split message was sent.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ *logofs << "handleSplit: SPLIT! Sent " << splits
+ << " splits and " << total - bytes << " bytes for FD#" << fd_
+ << " with " << clientStore_ -> getSplitTotalStorageSize()
+ << " bytes and [" << clientStore_ -> getSplitTotalSize()
+ << "] splits remaining.\n" << logofs_flush;
+
+ *logofs << "handleSplit: SPLIT! The pending split flag is "
+ << splitState_.pending << " with " << clientStore_ ->
+ getSplitTotalSize() << " splits in the split stores.\n"
+ << logofs_flush;
+
+ clientStore_ -> dumpSplitStores();
+
+ #endif
+
+ return 1;
+}
+
+int ClientChannel::handleSplitSend(EncodeBuffer &encodeBuffer, int resource,
+ int &splits, int &bytes)
+{
+ #if defined(TEST) || defined(SPLIT)
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ Split *splitMessage = splitStore -> getFirstSplit();
+
+ if (splitStore -> getResource() != resource ||
+ splitMessage -> getResource() != resource)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitSend: PANIC! The resource doesn't "
+ << "match the split store.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ *logofs << "handleSplitSend: SPLIT! Sending message "
+ << "OPCODE#" << (unsigned) opcodeStore_ -> splitData
+ << " for resource " << splitMessage -> getResource()
+ << " with request " << splitMessage -> getRequest()
+ << " position " << splitMessage -> getPosition()
+ << " and " << bytes << " bytes to write.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Use a special opcode to signal the other
+ // side this is part of a split and not a
+ // new message.
+ //
+
+ encodeBuffer.encodeOpcodeValue(opcodeStore_ -> splitData,
+ clientCache_ -> opcodeCache);
+
+ encodeBuffer.encodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ int result = clientStore_ -> getSplitStore(resource) ->
+ send(encodeBuffer, bytes);
+
+ if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplit: PANIC! Error sending splits for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error sending splits for FD#"
+ << fd_ << ".\n";
+
+ return -1;
+ }
+
+ //
+ // Get the bits written and update the
+ // statistics for this special opcode.
+ //
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(SPLIT)|| defined(OPCODES)
+ *logofs << "handleSplitSend: SPLIT! Handled request OPCODE#"
+ << (unsigned int) opcodeStore_ -> splitData << " ("
+ << DumpOpcode(opcodeStore_ -> splitData) << ")" << " for FD#"
+ << fd_ << " sequence none. 0 bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ statistics -> addRequestBits(opcodeStore_ -> splitData, 0, bits);
+
+ bytes -= bits >> 3;
+
+ splits++;
+
+ if (result == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitSend: SPLIT! Split at the head "
+ << "of the list was completely transferred.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // The split at the head of the list was
+ // completely transferred.
+ //
+
+ handleRestart(sequence_deferred, resource);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleSplitSend: SPLIT! More data to send "
+ << "for the split at the head of the list.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return result;
+}
+
+int ClientChannel::handleSplitChecksum(EncodeBuffer &encodeBuffer, T_checksum checksum)
+{
+ //
+ // Send the checksum only if the loading
+ // or the saving of the message to the
+ // persistent image cache is enabled.
+ //
+
+ if ((control -> ImageCacheEnableLoad == 1 ||
+ control -> ImageCacheEnableSave == 1) &&
+ (enableLoad_ == 1 || enableSave_ == 1))
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ encodeBuffer.encodeValue((unsigned int) checksum[i], 8);
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitChecksum: SPLIT! Sent checksum "
+ << "[" << DumpChecksum(checksum) << "].\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ return 0;
+ }
+}
+
+void ClientChannel::handleSplitPending()
+{
+ #if defined(TEST) || defined(SPLIT)
+
+ int previous = splitState_.pending;
+
+ #endif
+
+ if (clientStore_ -> getSplitTotalSize() == 0)
+ {
+ splitState_.pending = 0;
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitPending: SPLIT! Set the pending "
+ << "split flag to " << splitState_.pending
+ << " with split stores empty.\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // Loop through the stores to find if
+ // there is any split that has become
+ // ready.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitPending: WARNING! SPLIT! Looping to "
+ << "find if there is any split pending.\n"
+ << logofs_flush;
+ #endif
+
+ splitState_.pending = 0;
+
+ T_list &splitList = splitResources_.getList();
+
+ for (T_list::iterator j = splitList.begin();
+ j != splitList.end(); j++)
+ {
+ int resource = *j;
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore != NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStore(resource);
+
+ #endif
+
+ Split *splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage != NULL && canSendSplit(splitMessage) == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitPending: SPLIT! Found a pending "
+ << "split in store [" << resource << "].\n"
+ << logofs_flush;
+ #endif
+
+ splitState_.pending = 1;
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> getState() == split_loaded)
+ {
+ *logofs << "handleSplitPending: PANIC! SPLIT! Found a "
+ << "loaded split in store [" << resource
+ << "].\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ break;
+ }
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitPending: SPLIT! Set the pending "
+ << "split flag to " << splitState_.pending
+ << " with " << clientStore_ -> getSplitTotalSize()
+ << " splits in the split stores.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitState_.pending != previous)
+ {
+ *logofs << "handleSplitPending: SPLIT! Pending state "
+ << "changed from " << previous << " to "
+ << splitState_.pending << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+}
+
+int ClientChannel::handleSplitEvent(EncodeBuffer &encodeBuffer, Split *splitMessage)
+{
+ SplitStore *splitStore;
+
+ int resource = splitMessage -> getResource();
+
+ #if defined(TEST) || defined(INFO)
+
+ splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitEvent: PANIC! The split store can't "
+ << "be NULL handling abort splits.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ else if (splitMessage -> getState() != split_loaded)
+ {
+ *logofs << "handleSplitEvent: PANIC! Can't find the split "
+ << "to be aborted.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Send any split that it is possible to
+ // abort until the store is either empty
+ // or the next split can't be aborted.
+ //
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ while ((splitStore = clientStore_ ->
+ getSplitStore(resource)) != NULL &&
+ (splitMessage = splitStore -> getFirstSplit()) != NULL &&
+ splitMessage -> getState() == split_loaded)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Aborting split with "
+ << "checksum [" << DumpChecksum(splitMessage ->
+ getChecksum()) << "] for resource " << resource
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int any = 0;
+
+ if (handleSplitSend(encodeBuffer, resource, any, any) < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if ((splitStore = clientStore_ ->
+ getSplitStore(resource)) == NULL)
+ {
+ *logofs << "handleSplitEvent: SPLIT! The split store ["
+ << resource << "] has been destroyed.\n"
+ << logofs_flush;
+ }
+ else if ((splitMessage = splitStore ->
+ getFirstSplit()) == NULL)
+ {
+ *logofs << "handleSplitEvent: SPLIT! The split store ["
+ << resource << "] is empty.\n"
+ << logofs_flush;
+ }
+ else if (splitMessage -> getState() != split_loaded)
+ {
+ *logofs << "handleSplitEvent: SPLIT! The split at the "
+ << "head of store [" << resource << "] doesn't "
+ << "need to be aborted.\n" << logofs_flush;
+ }
+
+ #endif
+
+ return 1;
+}
+
+int ClientChannel::handleSplitEvent(DecodeBuffer &decodeBuffer)
+{
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Handling abort "
+ << "split messages for FD#" << fd_ << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ // Since ProtoStep7 (#issue 108)
+
+ //
+ // Decode the information about the
+ // message to be updated.
+ //
+
+ unsigned char resource;
+
+ decodeBuffer.decodeCachedValue(resource, 8,
+ serverCache_ -> resourceCache);
+
+ unsigned int loaded;
+
+ decodeBuffer.decodeBoolValue(loaded);
+
+ unsigned char request;
+ unsigned int size;
+
+ if (loaded == 1)
+ {
+ decodeBuffer.decodeOpcodeValue(request, serverCache_ -> abortOpcodeCache);
+
+ decodeBuffer.decodeValue(size, 32, 14);
+ }
+ else
+ {
+ request = 0;
+ size = 0;
+ }
+
+ unsigned int value;
+
+ md5_byte_t checksum[MD5_LENGTH];
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ decodeBuffer.decodeValue(value, 8);
+
+ checksum[i] = (unsigned char) value;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Checking split "
+ << "with checksum [" << DumpChecksum(checksum)
+ << "] loaded " << loaded << " request " << (unsigned int)
+ request << " compressed size " << size << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ Split *splitMessage = handleSplitFind(checksum, resource);
+
+ if (splitMessage != NULL)
+ {
+ if (loaded == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Marked split with "
+ << "checksum [" << DumpChecksum(checksum) << "] "
+ << "as [loaded] at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_loaded);
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> compressedSize() != (int) size)
+ {
+ *logofs << "handleSplitEvent: WARNING! SPLIT! Updating "
+ << "compressed data size from " << splitMessage ->
+ compressedSize() << " to " << size << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ splitMessage -> compressedSize(size);
+
+ //
+ // The splits to be aborted are checked by the split
+ // store at the time we are going to send a new chunk
+ // of split data. The splits must be strictly handled
+ // in the same order as they were added to the split
+ // store and the split we want to abort here may be
+ // not at the head of the list.
+ //
+
+ if (splitMessage == clientStore_ ->
+ getSplitStore(resource) -> getFirstSplit())
+ {
+ //
+ // We don't need to flush this packet immediately.
+ // The abort can be sent at any time to the remote
+ // proxy. What's important is that we restart the
+ // agent resource as soon as possible.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ T_timestamp startTs = getTimestamp();
+
+ *logofs << "handleSplitEvent: SPLIT! Encoding abort "
+ << "split events for FD#" << fd_ << " with "
+ << "resource " << (unsigned) resource << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Spent "
+ << diffTimestamp(startTs, getTimestamp()) << " Ms "
+ << "handling abort split events for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if we can clear the pending flag.
+ //
+
+ handleSplitPending();
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleSplitEvent: WARNING! SPLIT! Abort split "
+ << "event not sent because not at the head "
+ << "of the list.\n" << logofs_flush;
+ }
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Marked split with "
+ << "checksum [" << DumpChecksum(checksum) << "] "
+ << "as [missed] at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_missed);
+
+ //
+ // Check if we can set the pending flag.
+ //
+
+ handleSplitPending(resource);
+ }
+ }
+ else
+ {
+ //
+ // The split report came after the split was already
+ // sent or the split store deleted. If the message
+ // had been loaded from disk by the remote side, we
+ // need to update the compressed size in our message
+ // store or the checksum will not match at the time
+ // we will try to save the message store on disk.
+ //
+
+ if (loaded == 1 && size != 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: WARNING! SPLIT! Can't find "
+ << "the split. Updating in the message store.\n"
+ << logofs_flush;
+ #endif
+
+ MessageStore *store = clientStore_ -> getRequestStore(request);
+
+ if (store != NULL)
+ {
+ store -> updateData(checksum, size);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitEvent: PANIC! The message store "
+ << "can't be null.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ #endif
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleSplitEvent: WARNING! SPLIT! No need to "
+ << "update the store with loaded " << loaded
+ << " and compressed size " << size << ".\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ return 1;
+}
+
+Split *ClientChannel::handleSplitFind(T_checksum checksum, int resource)
+{
+ //
+ // It can be that we handled all the splits,
+ // restarted the resource and deleted the
+ // store before the event could even reach
+ // our side.
+ //
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore != NULL)
+ {
+ Split *splitMessage;
+
+ T_splits *splitList = splitStore -> getSplits();
+
+ for (T_splits::iterator i = splitList -> begin();
+ i != splitList -> end(); i++)
+ {
+ splitMessage = (*i);
+
+ if (splitMessage -> getChecksum() != NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitFind: SPLIT! Comparing with message ["
+ << DumpChecksum(splitMessage -> getChecksum())
+ << "].\n" << logofs_flush;
+ #endif
+
+ if (memcmp(checksum, splitMessage -> getChecksum(), MD5_LENGTH) == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitFind: SPLIT! Located split for "
+ << "checksum [" << DumpChecksum(checksum) << "] "
+ << "in store [" << splitStore -> getResource()
+ << "].\n" << logofs_flush;
+ #endif
+
+ return splitMessage;
+ }
+ }
+ }
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleSplitFind: WARNING! SPLIT! The split store "
+ << "was already deleted.\n" << logofs_flush;
+ }
+ #endif
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitFind: WARNING! SPLIT! Can't find the "
+ << "split for checksum [" << DumpChecksum(checksum)
+ << "].\n" << logofs_flush;
+ #endif
+
+ return NULL;
+}
+
+int ClientChannel::handleRestart(T_sequence_mode mode, int resource)
+{
+ //
+ // The agent must send a start-split message, followed by the
+ // X messages that may be optionally split by the proxy. Usu-
+ // ally, in the middle of a start-split/end-split sequence is
+ // a single PutImage() or PutPackedImage(), that, in turn,
+ // can generate multiple partial requests, like a SetUnpack-
+ // Colormap() and SetUnpackAlpha() followed by the image that
+ // must be transferred. Multiple requests may be also genera-
+ // ted because the maximum size of a X request has been exce-
+ // eded, so that Xlib has divided the single image in multi-
+ // ple sub-image requests. The agent doesn't need to take care
+ // of that, except tracking the result of the split operation.
+ //
+ // By monitoring the notify events sent by the proxy, the
+ // agent will have to implement its own strategy to deal with
+ // its resources (for example its clients). For example:
+ //
+ // - It will issue a new image request and suspend a client
+ // if the image was not entirely sent in the main X oputput
+ // stream.
+ //
+ // - It will choose to commit or discard the messages after
+ // they are recomposed at the remote side. The set of mes-
+ // sages that will have to be committed will include all
+ // messages that were part of the split (the colormap, the
+ // alpha channel).
+ //
+ // - It will restart its own client, in the case it had been
+ // suspended.
+ //
+ // A more useful strategy would be to replace the original im-
+ // age with a tiny 'placeholder' if a split took place, and
+ // synchronize the content of the drawable at later time. This
+ // is generally referred as 'lazy encoding'.
+ //
+ // The agent will be able to identify the original split ope-
+ // ration (the one marked with the start-spit) by the small
+ // integer number (0-255) referred to as the 'resource' field.
+ //
+ // Before the proxy will be able to report the status of the
+ // split, the agent will have to close the sequence by issueing
+ // an end-split. The proxy will then report the result of the
+ // operation, so that the agent will have the option of suspend-
+ // ing the client or marking the drawable as dirty and take
+ // care of synchronizing it at later time.
+ //
+ // One of the following cases may be encountered:
+ //
+ // notify_no_split: All messages were sent in the main out-
+ // put stream, so that no split actually
+ // took place.
+ //
+ // notify_start_split: One or more messages were split, so,
+ // at discrection of the agent, the client
+ // may be suspended until the transferral
+ // is completed.
+ //
+ // notify_commit_split: One of the requests that made up the
+ // split was recomposed. The agent should
+ // either commit the given request or tell
+ // the proxy to discard it.
+ //
+ // notify_end_split: The split was duly completed. The agent
+ // can restart the client.
+ //
+ // notify_empty_split: No more split operation are pending.
+ // The agent can use this information to
+ // implement specific strategies requiring
+ // that all messages have been recomposed
+ // at the remote end, like updating the
+ // drawables that were not synchronized
+ // because of the lazy encoding.
+ //
+ // By checking the split and commit store we can determine if we
+ // need to send a new notification event to the agent. There can
+ // be four different cases:
+ //
+ // - If the split store is not null and not empty, we are still
+ // in the middle of a split.
+ //
+ // - If the commit store is not empty, we completely recomposed
+ // a full message and can send a new commit notify.
+ //
+ // - If the split store has become empty, we recomposed all the
+ // messages added for the given resource, and so will be able
+ // to restart the resource.
+ //
+ // - If no more messages are in the split stores, we can notify
+ // an empty split event to the agent.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Handling ["
+ << (mode == sequence_immediate ? "immediate" : "deferred")
+ << "] restart events for resource " << resource << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (mode == sequence_immediate)
+ {
+ //
+ // We have received an end-split request. If the store
+ // was not deleted already, we mark the last split added
+ // as the one ending the row for this resource. If the
+ // commit() function returns 0 it means that the split
+ // store is either empty or that we did not add any split
+ // for this resource. This is because when connected to
+ // an old proxy version we only have a single store for
+ // all the resources.
+ //
+ // It can happen that all the split messages that were
+ // originally appended to the list were completely sent
+ // before our client had the chance of ending the split
+ // sequence. In this case the split store will be empty
+ // or already deleted and so we will be able to restart
+ // the resource.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitStore == NULL)
+ {
+ *logofs << "handleRestart: WARNING! SPLIT! Split store ["
+ << resource << "] was already deleted.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ clientStore_ -> dumpSplitStore(resource);
+ }
+
+ #endif
+
+ if (splitStore == NULL || splitStore -> getSize() == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Immediate agent split event "
+ << "TYPE#" << (unsigned) opcodeStore_ -> noSplitNotify
+ << " [no split] with resource " << resource
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (handleNotify(notify_no_split, sequence_immediate,
+ resource, nothing, nothing) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Immediate agent split event "
+ << "TYPE#" << (unsigned) opcodeStore_ -> startSplitNotify
+ << " [start split] with resource " << resource
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (handleNotify(notify_start_split, sequence_immediate,
+ resource, nothing, nothing) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ //
+ // We have completely transferred a message
+ // that was put in the split store.
+ //
+ // The id of the resource can be different
+ // than the index of the store if we are
+ // connected to an old proxy.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitStore == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleRestart: PANIC! The split store can't "
+ << "be NULL handling deferred restart events.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ else
+ {
+ clientStore_ -> dumpSplitStore(resource);
+ }
+
+ #endif
+
+ CommitStore *commitStore = clientStore_ -> getCommitStore();
+
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpCommitStore();
+
+ #endif
+
+ //
+ // Check if there is any commit to notify.
+ //
+
+ Split *split;
+
+ T_splits *commitList = commitStore -> getSplits();
+
+ for (T_splits::iterator i = commitList -> begin();
+ i != commitList -> end(); i++)
+ {
+ split = *i;
+
+ if (split -> getState() != split_notified)
+ {
+ #if defined(TEST) || defined(SPLIT)
+
+ if (split -> getResource() != resource)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplitSend: PANIC! The resource doesn't "
+ << "match the split store.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ int request = split -> getRequest();
+ int position = split -> getPosition();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Deferred agent split event "
+ << "TYPE#" << (unsigned) opcodeStore_ -> commitSplitNotify
+ << " [commit split] with resource " << resource << " request "
+ << request << " position " << position << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (handleNotify(notify_commit_split, sequence_deferred,
+ resource, request, position) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Don't send the notification again.
+ //
+
+ split -> setState(split_notified);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleRestart: SPLIT! Split for request "
+ << split -> getRequest() << " and position "
+ << split -> getPosition() << " was already "
+ << "notified.\n" << logofs_flush;
+ }
+ #endif
+ }
+
+ //
+ // Don't send the end split if we are still
+ // in the middle of a start-split/end-split
+ // sequence. We'll send a no-split at the
+ // time the end-split is received.
+ //
+
+ if (splitStore -> getSize() == 0 &&
+ splitStore -> getResource() != splitState_.resource)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Deferred agent split event "
+ << "TYPE#" << (unsigned) opcodeStore_ -> endSplitNotify
+ << " [end split] with resource " << resource << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (handleNotify(notify_end_split, sequence_deferred,
+ resource, nothing, nothing) < 0)
+ {
+ return -1;
+ }
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else if (splitStore -> getSize() == 0 &&
+ splitStore -> getResource() == splitState_.resource)
+ {
+ *logofs << "handleRestart: SPLIT! WARNING! The split store "
+ << "for resource " << resource << " was emptied in the "
+ << "split sequence at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ //
+ // Remove the split store if it's empty.
+ //
+
+ if (splitStore != NULL && splitStore -> getSize() == 0 &&
+ splitStore -> getResource() != splitState_.resource)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Removing the split store ["
+ << resource << "] at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ handleSplitStoreRemove(&splitResources_, resource);
+
+ if (clientStore_ -> getSplitTotalSize() == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! Deferred agent split event "
+ << "TYPE#" << (unsigned) opcodeStore_ -> emptySplitNotify
+ << " [empty split] for FD#" << fd_ << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (handleNotify(notify_empty_split, sequence_deferred,
+ nothing, nothing, nothing) < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleRestart: SPLIT! There are " << clientStore_ ->
+ getSplitTotalSize() << " messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+
+ if ((clientStore_ -> getSplitTotalSize() != 0 &&
+ clientStore_ -> getSplitTotalStorageSize() == 0) ||
+ (clientStore_ -> getSplitTotalSize() == 0 &&
+ clientStore_ -> getSplitTotalStorageSize() != 0))
+ {
+ #ifdef PANIC
+ *logofs << "handleRestart: PANIC! Inconsistency detected "
+ << "while handling the split stores.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ #endif
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleTaintLameRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // Test the efficiency of the encoding
+ // without these RENDER requests.
+ //
+
+ if (opcode == opcodeStore_ -> renderExtension &&
+ (*(buffer + 1) == X_RenderCompositeGlyphs8 ||
+ *(buffer + 1) == X_RenderCompositeGlyphs16 ||
+ *(buffer + 1) == X_RenderCompositeGlyphs32 ||
+ *(buffer + 1) == X_RenderAddGlyphs ||
+ *(buffer + 1) == X_RenderTrapezoids))
+ {
+ #ifdef TEST
+ *logofs << "handleTaintLameRequest: Tainting request "
+ << "OPCODE#" << (unsigned int) opcode << " MINOR#"
+ << (unsigned int) *(buffer + 1) << " for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ opcode = X_NoOperation;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int ClientChannel::handleTaintSyncRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // Should short-circuit other common replies
+ // whose values could be queried only once.
+ // Examples are X_InterAtom, X_ListExtension
+ // and X_QueryExtension.
+ //
+
+ if (taintCounter_ >= control -> TaintThreshold)
+ {
+ #ifdef DEBUG
+ *logofs << "handleTaintSyncRequest: Reset taint counter after "
+ << taintCounter_ << " replies managed.\n"
+ << logofs_flush;
+ #endif
+
+ taintCounter_ = 0;
+
+ return 0;
+ }
+
+ //
+ // Check if we are rolling the counter.
+ // The client sequence number has not
+ // been incremented yet in the loop.
+ //
+
+ unsigned int sequence = (clientSequence_ + 1) & 0xffff;
+
+ #ifdef DEBUG
+ *logofs << "handleTaintSyncRequest: Opcode is " << (unsigned) opcode
+ << " expected client sequence is " << sequence
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (sequence == 0xffff)
+ {
+ return 0;
+ }
+
+ unsigned short t1;
+ unsigned char t2;
+
+ //
+ // Check if there is a previous reply
+ // pending.
+ //
+
+ if (sequenceQueue_.peek(t1, t2) != 0)
+ {
+ #ifdef DEBUG
+ *logofs << "handleTaintSyncRequest: Skipping taint of reply due to "
+ << "pending request OPCODE#" << t1 << " with sequence "
+ << (unsigned int) t2 << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleTaintSyncRequest: Suppressing get input focus "
+ << "request for FD#" << fd_ << " with sequence "
+ << sequence << ".\n" << logofs_flush;
+ #endif
+
+ unsigned char *reply = writeBuffer_.addMessage(32);
+
+ *(reply + 0) = X_Reply;
+
+ PutUINT(sequence, reply + 2, bigEndian_);
+
+ PutULONG(0, reply + 4, bigEndian_);
+
+ //
+ // Set revert-to to none.
+ //
+
+ *(reply + 1) = 0;
+
+ //
+ // Set focus to none.
+ //
+
+ PutULONG(0, reply + 8, bigEndian_);
+
+ //
+ // Save the sequence number, not incremented
+ // yet, we used to auto-generate this reply.
+ //
+
+ lastSequence_ = clientSequence_ + 1;
+
+ #ifdef TEST
+ *logofs << "handleTaintSyncRequest: Registered " << lastSequence_
+ << " as last auto-generated sequence number.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Taint the request to a X_NoOperation.
+ //
+
+ opcode = X_NoOperation;
+
+ //
+ // We may assume that the client has finished
+ // drawing and flush immediately, even if this
+ // seems to perceively affect the performance.
+ //
+ // priority_++;
+ //
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ taintCounter_++;
+
+ return 1;
+}
+
+int ClientChannel::handleTaintSyncError(unsigned char opcode)
+{
+ if (control -> TaintReplies > 0)
+ {
+ //
+ // By enabling short-circuiting of replies
+ // some window managers can get confused
+ // by some otherwise innocuous X errors.
+ //
+
+ if (opcode == X_GrabKey || opcode == X_ReparentWindow ||
+ opcode == X_ConfigureWindow)
+ {
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleTaintSyncError: WARNING! Suppressed error "
+ << "on OPCODE#" << (unsigned int) opcode << " for FD#"
+ << fd_ << " sequence " << lastSequence_ << " (real was "
+ << serverSequence_ << ").\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int ClientChannel::handleNotify(T_notification_type type, T_sequence_mode mode,
+ int resource, int request, int position)
+{
+ if (finish_ == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleNotify: Discarding notification on "
+ << "channel for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Add a new message to the write buffer.
+ //
+
+ unsigned char *event = writeBuffer_.addMessage(32);
+
+ //
+ // Event is ClientMessage, atom and
+ // window are 0, format is 32.
+ //
+
+ *(event + 0) = ClientMessage;
+
+ PutULONG(0, event + 4, bigEndian_);
+ PutULONG(0, event + 8, bigEndian_);
+
+ *(event + 1) = 32;
+
+ //
+ // If the event follows immediately the request (that is the
+ // sequence mode is 'immediate') then the sequence number is
+ // the one of the last request, else it should be the last
+ // sequence number encoded by peer proxy but, as we are ins-
+ // erting events in the stream, we must ensure that the se-
+ // quence we send is not less than the last sequence we have
+ // auto-generated.
+ //
+
+ if (mode == sequence_immediate)
+ {
+ //
+ // Save the sequence number we used
+ // to auto-generate this event.
+ //
+
+ lastSequence_ = clientSequence_;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleNotify: Registered " << lastSequence_
+ << " as last auto-generated sequence number.\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ if (serverSequence_ > lastSequence_)
+ {
+ #ifdef DEBUG
+ *logofs << "handleNotify: Updating last event's sequence "
+ << lastSequence_ << " to X server's sequence number "
+ << serverSequence_ << " for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ lastSequence_ = serverSequence_;
+ }
+ #ifdef DEBUG
+ else if (serverSequence_ < lastSequence_)
+ {
+ //
+ // Use our last auto-generated sequence.
+ //
+
+ *logofs << "handleNotify: Tainting sequence number "
+ << serverSequence_ << " to last event's sequence "
+ << lastSequence_ << " for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ PutUINT(lastSequence_, event + 2, bigEndian_);
+
+ //
+ // Be sure we set to void the fields that
+ // are not significant for the specific
+ // notification message.
+ //
+
+ PutULONG(nothing, event + 16, bigEndian_);
+ PutULONG(nothing, event + 20, bigEndian_);
+ PutULONG(nothing, event + 24, bigEndian_);
+
+ switch (type)
+ {
+ case notify_no_split:
+ {
+ PutULONG(opcodeStore_ -> noSplitNotify,
+ event + 12, bigEndian_);
+
+ PutULONG(resource, event + 16, bigEndian_);
+
+ break;
+ }
+ case notify_start_split:
+ {
+ PutULONG(opcodeStore_ -> startSplitNotify,
+ event + 12, bigEndian_);
+
+ PutULONG(resource, event + 16, bigEndian_);
+
+ break;
+ }
+ case notify_commit_split:
+ {
+ PutULONG(opcodeStore_ -> commitSplitNotify,
+ event + 12, bigEndian_);
+
+ PutULONG(resource, event + 16, bigEndian_);
+
+ PutULONG(request, event + 20, bigEndian_);
+
+ PutULONG(position, event + 24, bigEndian_);
+
+ break;
+ }
+ case notify_end_split:
+ {
+ PutULONG(opcodeStore_ -> endSplitNotify,
+ event + 12, bigEndian_);
+
+ PutULONG(resource, event + 16, bigEndian_);
+
+ break;
+ }
+ case notify_empty_split:
+ {
+ PutULONG(opcodeStore_ -> emptySplitNotify,
+ event + 12, bigEndian_);
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "handleNotify: PANIC! Unrecognized notify "
+ << "TYPE#" << type << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO) || defined (SPLIT)
+
+ *logofs << "handleNotify: Sending "
+ << (mode == sequence_immediate ? "immediate " : "deferred ")
+ << "agent notify event TYPE#" << GetULONG(event + 12, bigEndian_)
+ << logofs_flush;
+
+ if (resource != nothing)
+ {
+ *logofs << " with resource " << GetULONG(event + 16, bigEndian_)
+ << logofs_flush;
+
+ if (request != nothing && position != nothing)
+ {
+ *logofs << " request " << GetULONG(event + 20, bigEndian_)
+ << " position " << GetULONG(event + 24, bigEndian_)
+ << logofs_flush;
+ }
+ }
+
+ *logofs << ".\n" << logofs_flush;
+
+ #endif
+
+ //
+ // Send the notification now.
+ //
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleCommitSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ //
+ // Get the data of the request to be
+ // committed.
+ //
+
+ unsigned char request = *(buffer + 5);
+
+ MessageStore *store = clientStore_ -> getRequestStore(request);
+
+ if (store == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleCommitSplitRequest: PANIC! Can't commit split for "
+ << "request OPCODE#" << (unsigned int) request
+ << ". No message store found.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't commit split for request "
+ << "OPCODE#" << (unsigned int) request
+ << ". No message store found.\n";
+
+ return -1;
+ }
+
+ //
+ // The position in cache of the message
+ // to commit. Encode it as difference in
+ // respect to the last encoded value.
+ //
+
+ unsigned int position = GetULONG(buffer + 8, bigEndian_);
+
+ unsigned char resource = *(buffer + 1);
+ unsigned int commit = *(buffer + 4);
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (commit == 1)
+ {
+ *logofs << "handleCommitSplitRequest: SPLIT! Committing request "
+ << "OPCODE#" << (unsigned) request << " at position "
+ << position << " for FD#" << fd_ << " with resource "
+ << (unsigned) resource << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleCommitSplitRequest: SPLIT! Discarding request "
+ << "OPCODE#" << (unsigned) request << " at position "
+ << position << " for FD#" << fd_ << " with resource "
+ << (unsigned) resource << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ encodeBuffer.encodeOpcodeValue(request, clientCache_ -> opcodeCache);
+
+ int diffCommit = position - splitState_.commit;
+
+ splitState_.commit = position;
+
+ encodeBuffer.encodeValue(diffCommit, 32, 5);
+
+ //
+ // Send the resource id and the commit
+ // flag.
+ //
+
+ encodeBuffer.encodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ encodeBuffer.encodeBoolValue(commit);
+
+ //
+ // Remove the split from the split queue.
+ //
+
+ Split *split = handleSplitCommitRemove(request, resource, splitState_.commit);
+
+ if (split == NULL)
+ {
+ return -1;
+ }
+
+ clientStore_ -> getCommitStore() -> update(split);
+
+ //
+ // Free the split.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleCommitSplitRequest: SPLIT! Freeing up the "
+ << "committed split.\n" << logofs_flush;
+ #endif
+
+ delete split;
+
+ return 1;
+}
+
+int ClientChannel::handleAbortSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ unsigned char resource = *(buffer + 1);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Handling abort split "
+ << "request for FD#"<< fd_ << " and resource "
+ << (unsigned int) resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The split "
+ << "store [" << (unsigned int) resource << "] "
+ << "is already empty.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Loop through the messages in the split
+ // store and discard from the memory cache
+ // the messages that are still incomplete.
+ // Then remove the message from the split
+ // store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStore(resource);
+
+ #endif
+
+ int splits = 0;
+
+ Split *splitMessage;
+
+ for (;;)
+ {
+ splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage == NULL)
+ {
+ //
+ // Check if we had created the store
+ // but no message was added yet.
+ //
+
+ #ifdef WARNING
+
+ if (splits == 0)
+ {
+ *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The "
+ << "split store [" << (unsigned int) resource
+ << "] is unexpectedly empty.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ break;
+ }
+
+ //
+ // Splits already aborted can't be in the
+ // split store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> getState() == split_aborted)
+ {
+ *logofs << "handleAbortSplitRequest: PANIC! SPLIT! Found an "
+ << "aborted split in store [" << (unsigned int) resource
+ << "].\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ if (splitMessage -> getAction() == IS_HIT)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Removing the "
+ << "split from the memory cache.\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> getStore() -> remove(splitMessage -> getPosition(),
+ use_checksum, discard_data);
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Removing the "
+ << "split from the split store.\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage = splitStore -> pop();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Freeing up the "
+ << "aborted split.\n" << logofs_flush;
+ #endif
+
+ delete splitMessage;
+
+ splits++;
+ }
+
+ //
+ // If the start-split/end-split sequence
+ // was closed, send the notification now,
+ // else wait for the end-split.
+ //
+
+ if (resource != splitState_.resource)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Sending the "
+ << "deferred [end split] event.\n"
+ << logofs_flush;
+ #endif
+
+ handleRestart(sequence_deferred, resource);
+ }
+ #if defined(TEST) || defined(SPLIT)
+ else
+ {
+ *logofs << "handleAbortSplitRequest: WARNING! SPLIT! Still "
+ << "waiting for the closure of the split "
+ << "sequence.\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Check if there is any other store
+ // having splits to send.
+ //
+
+ handleSplitPending();
+
+ return (splits > 0);
+}
+
+int ClientChannel::handleFinishSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ unsigned char resource = *(buffer + 1);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleFinishSplitRequest: SPLIT! Handling finish split "
+ << "request for FD#"<< fd_ << " and resource "
+ << (unsigned int) resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ //
+ // We need to get the protocol statistics
+ // for the finish message we are handling
+ // here because sending a new split will
+ // reset the bits counter.
+ //
+
+ int bits = encodeBuffer.diffBits();
+
+ statistics -> addRequestBits(opcode, size << 3, bits);
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "handleFinishSplitRequest: WARNING! SPLIT! The split "
+ << "store [" << (unsigned int) resource << "] "
+ << "is already empty.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Send all the split queued for the given
+ // resource until the split store becomes
+ // empty.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStore(resource);
+
+ #endif
+
+ Split *splitMessage;
+
+ int total = MESSAGE_DATA_LIMIT;
+
+ int bytes = total;
+ int splits = 0;
+
+ for (;;)
+ {
+ splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage == NULL)
+ {
+ //
+ // We have presumably created the store
+ // after a start split but no message
+ // was added yet.
+ //
+
+ #ifdef WARNING
+ *logofs << "handleFinishSplitRequest: WARNING! SPLIT! The "
+ << "split store [" << (unsigned int) resource
+ << "] is unexpectedly empty.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+
+ //
+ // Splits already aborted can't be in the
+ // split store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> getState() == split_aborted)
+ {
+ *logofs << "handleFinishSplitRequest: PANIC! SPLIT! Found an "
+ << "aborted split in store [" << (unsigned int) resource
+ << "].\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ *logofs << "handleFinishSplitRequest: SPLIT! Sending more "
+ << "data for store [" << (unsigned int) resource
+ << "].\n" << logofs_flush;
+ #endif
+
+ if (handleSplitSend(encodeBuffer, resource, splits, bytes) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Check if the split store was deleted.
+ //
+
+ if (clientStore_ -> getSplitStore(resource) == NULL)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleFinishSplitRequest: SPLIT! Exiting "
+ << "from the finish loop with split store ["
+ << (unsigned int) resource << "] destroyed.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+ }
+
+ //
+ // Check if there is any other store
+ // having splits to send.
+ //
+
+ handleSplitPending();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleFinishSplitRequest: SPLIT! Sent " << splits
+ << " splits and " << total - bytes << " bytes for FD#" << fd_
+ << " with " << clientStore_ -> getSplitTotalStorageSize()
+ << " bytes and [" << clientStore_ -> getSplitTotalSize()
+ << "] splits remaining.\n" << logofs_flush;
+ #endif
+
+ return (splits > 0);
+}
+
+int ClientChannel::handleConfiguration()
+{
+ #ifdef TEST
+ *logofs << "ClientChannel: Setting new buffer parameters.\n"
+ << logofs_flush;
+ #endif
+
+ readBuffer_.setSize(control -> ClientInitialReadSize,
+ control -> ClientMaximumBufferSize);
+
+ writeBuffer_.setSize(control -> TransportXBufferSize,
+ control -> TransportXBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ transport_ -> setSize(control -> TransportXBufferSize,
+ control -> TransportXBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ return 1;
+}
+
+int ClientChannel::handleFinish()
+{
+ #ifdef TEST
+ *logofs << "ClientChannel: Finishing channel for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ priority_ = 0;
+
+ finish_ = 1;
+
+ taintCounter_ = 0;
+
+ splitState_.resource = nothing;
+ splitState_.pending = 0;
+ splitState_.commit = 0;
+ splitState_.mode = split_none;
+
+ transport_ -> finish();
+
+ return 1;
+}
+
+//
+// If differential compression is disabled then use the
+// most simple encoding but handle the image requests
+// and the X_ListExtensions and X_QueryExtension messa-
+// ges (needed to detect the opcode of the shape or the
+// other extensions) in the usual way.
+//
+
+int ClientChannel::handleFastReadRequest(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size)
+{
+ //
+ // All the NX requests are handled in the
+ // main message loop. The X_PutImage can
+ // be handled here only if the split was
+ // not requested (since ProtoStep7 #issue 108).
+ //
+
+ if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) ||
+ (opcode == X_PutImage && splitState_.resource != nothing) ||
+ opcode == X_ListExtensions ||
+ opcode == X_QueryExtension)
+ {
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleFastReadRequest: Encoding raw request OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << " with size " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeMemory(buffer, size);
+
+ //
+ // Put request on the fast track
+ // if it needs a reply.
+ //
+
+ switch (opcode)
+ {
+ case X_GetAtomName:
+ case X_GetGeometry:
+ case X_GetInputFocus:
+ case X_GetModifierMapping:
+ case X_GetKeyboardMapping:
+ case X_GetProperty:
+ case X_GetSelectionOwner:
+ case X_GrabPointer:
+ case X_GrabKeyboard:
+ case X_ListExtensions:
+ case X_ListFonts:
+ case X_LookupColor:
+ case X_AllocNamedColor:
+ case X_QueryPointer:
+ case X_GetWindowAttributes:
+ case X_QueryTree:
+ case X_QueryBestSize:
+ case X_QueryColors:
+ case X_QueryFont:
+ case X_TranslateCoords:
+ case X_GetImage:
+ case X_GetPointerMapping:
+ case X_GetKeyboardControl:
+ case X_InternAtom:
+ case X_AllocColor:
+ {
+ sequenceQueue_.push(clientSequence_, opcode);
+
+ priority_++;
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ *logofs << "handleFastReadRequest: Handled raw request OPCODE#"
+ << (unsigned int) opcode << " (" << DumpOpcode(opcode) << ")"
+ << " for FD#" << fd_ << " sequence " << clientSequence_
+ << ". " << size << " bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+
+ #endif
+
+ statistics -> addRequestBits(opcode, size << 3, bits);
+
+ if (opcode == opcodeStore_ -> renderExtension)
+ {
+ statistics -> addRenderRequestBits(*(buffer + 1), size << 3, bits);
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleFastWriteReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ if ((opcode >= X_NXFirstOpcode &&
+ opcode <= X_NXLastOpcode) ||
+ opcode == X_ListExtensions ||
+ opcode == X_QueryExtension)
+ {
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleFastWriteReply: Decoding raw reply OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ buffer = writeBuffer_.addMessage(8);
+
+ #ifndef __sun
+
+ unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(8);
+
+ *((unsigned int *) buffer) = *next++;
+ *((unsigned int *) (buffer + 4)) = *next;
+
+ #else /* #ifndef __sun */
+
+ memcpy(buffer, decodeBuffer.decodeMemory(8), 8);
+
+ #endif /* #ifndef __sun */
+
+ size = 32 + (GetULONG(buffer + 4, bigEndian_) << 2);
+
+ writeBuffer_.registerPointer(&buffer);
+
+ if (writeBuffer_.getAvailable() < size - 8 ||
+ (int) size >= control -> TransportFlushBufferSize)
+ {
+ #ifdef DEBUG
+ *logofs << "handleFastWriteReply: Using scratch buffer for OPCODE#"
+ << (unsigned int) opcode << " with size " << size << " and "
+ << writeBuffer_.getLength() << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+
+ writeBuffer_.removeMessage(8);
+
+ buffer = writeBuffer_.addScratchMessage(((unsigned char *)
+ decodeBuffer.decodeMemory(size - 8)) - 8, size);
+ }
+ else
+ {
+ writeBuffer_.addMessage(size - 8);
+
+ #ifndef __sun
+
+ if (size == 32)
+ {
+ next = (unsigned int *) decodeBuffer.decodeMemory(size - 8);
+
+ for (int i = 8; i < 32; i += sizeof(unsigned int))
+ {
+ *((unsigned int *) (buffer + i)) = *next++;
+ }
+ }
+ else
+ {
+ memcpy(buffer + 8, decodeBuffer.decodeMemory(size - 8), size - 8);
+ }
+
+ #else /* #ifndef __sun */
+
+ memcpy(buffer + 8, decodeBuffer.decodeMemory(size - 8), size - 8);
+
+ #endif /* #ifndef __sun */
+ }
+
+ writeBuffer_.unregisterPointer();
+
+ //
+ // We don't need to write our local sequence
+ // number. Replies are always sent with the
+ // original X server's sequence number.
+ //
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleFastWriteReply: Handled raw reply OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_ << " with sequence "
+ << serverSequence_ << ". Output size is " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "handleFastWriteReply: Length of sequence queue is "
+ << sequenceQueue_.length() << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addRepliedRequest(opcode);
+
+ handleFlush(flush_if_needed);
+
+ return 1;
+}
+
+int ClientChannel::handleFastWriteEvent(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ #ifdef DEBUG
+ *logofs << "handleFastWriteEvent: Decoding raw "
+ << (opcode == X_Error ? "error" : "event") << " OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = 32;
+
+ buffer = writeBuffer_.addMessage(size);
+
+ #ifndef __sun
+
+ unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(size);
+
+ for (int i = 0; i < 32; i += sizeof(unsigned int))
+ {
+ *((unsigned int *) (buffer + i)) = *next++;
+ }
+
+ #else /* #ifndef __sun */
+
+ memcpy(buffer, decodeBuffer.decodeMemory(size), size);
+
+ #endif /* #ifndef __sun */
+
+ //
+ // Use our local sequence number.
+ //
+
+ PutUINT(lastSequence_, buffer + 2, bigEndian_);
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleFastWriteEvent: Handled raw "
+ << (opcode == X_Error ? "error" : "event") << " OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_ << " with sequence "
+ << lastSequence_ << ". Output size is " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if we need to suppress the error.
+ //
+
+ if (opcode == X_Error && handleTaintSyncError(*(buffer + 10)) > 0)
+ {
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleFastWriteEvent: WARNING! Suppressed error OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << " with sequence " << lastSequence_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ writeBuffer_.removeMessage(32);
+ }
+
+ handleFlush(flush_if_needed);
+
+ return 1;
+}
+
+int ClientChannel::handleShmemRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ //
+ // Will push sequence and set
+ // priority according to stage.
+ //
+
+ unsigned int stage = *(buffer + 1);
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Encoding shmem request "
+ << "OPCODE#" << (unsigned int) opcode << " for FD#"
+ << fd_ << " with size " << size << " at stage "
+ << stage << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(stage, 2);
+
+ if (stage == 0)
+ {
+ unsigned int enableClient = 0;
+ unsigned int enableServer = 0;
+
+ if (control -> ShmemClient == 1)
+ {
+ enableClient = *(buffer + 4);
+ }
+
+ if (control -> ShmemServer == 1)
+ {
+ enableServer = *(buffer + 5);
+ }
+
+ encodeBuffer.encodeBoolValue(enableClient);
+ encodeBuffer.encodeBoolValue(enableServer);
+
+ unsigned int clientSegment = GetULONG(buffer + 8, bigEndian_);
+ unsigned int serverSegment = GetULONG(buffer + 12, bigEndian_);
+
+ encodeBuffer.encodeValue(clientSegment, 29, 9);
+ encodeBuffer.encodeValue(serverSegment, 29, 9);
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Enable client is "
+ << enableClient << " enable server is " << enableServer
+ << " client segment is " << (void *) clientSegment
+ << " server segment is " << (void *) serverSegment
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Size of the shared memory "
+ << "segment will be " << control -> ShmemServerSize
+ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ if (stage != 1)
+ {
+ sequenceQueue_.push(clientSequence_, opcodeStore_ ->
+ getShmemParameters);
+
+ priority_++;
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleShmemReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ #ifdef TEST
+ *logofs << "handleShmemReply: Received shmem parameters "
+ << "reply OPCODE#" << (unsigned int) opcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = 32;
+ buffer = writeBuffer_.addMessage(size);
+
+ unsigned int stage;
+
+ decodeBuffer.decodeValue(stage, 2);
+
+ *(buffer + 1) = stage;
+
+ if (stage == 2)
+ {
+ unsigned int clientEnabled;
+ unsigned int serverEnabled;
+
+ decodeBuffer.decodeBoolValue(clientEnabled);
+ decodeBuffer.decodeBoolValue(serverEnabled);
+
+ //
+ // Client support is not implemented
+ // and not useful. It is here only
+ // for compatibility.
+ //
+
+ clientEnabled = 0;
+
+ *(buffer + 8) = clientEnabled;
+ *(buffer + 9) = serverEnabled;
+
+ PutULONG(0, buffer + 12, bigEndian_);
+
+ if (serverEnabled == 1)
+ {
+ #ifdef TEST
+ *logofs << "handleShmemReply: Enabled shared memory "
+ << "support in X server with segment size "
+ << control -> ShmemServerSize << ".\n"
+ << logofs_flush;
+ #endif
+
+ PutULONG(control -> ShmemServerSize, buffer + 16, bigEndian_);
+ }
+ else
+ {
+ PutULONG(0, buffer + 16, bigEndian_);
+ }
+ }
+ else
+ {
+ *(buffer + 8) = 0;
+ *(buffer + 9) = 0;
+
+ PutULONG(0, buffer + 12, bigEndian_);
+ PutULONG(0, buffer + 16, bigEndian_);
+ }
+
+ return 1;
+}
+
+int ClientChannel::handleFontRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #ifdef TEST
+ *logofs << "handleFontRequest: Encoding font request "
+ << "OPCODE#" << (unsigned int) opcode << " for FD#"
+ << fd_ << " with size " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ sequenceQueue_.push(clientSequence_, opcodeStore_ ->
+ getFontParameters);
+
+ return 1;
+}
+
+int ClientChannel::handleFontReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ #ifdef TEST
+ *logofs << "handleFontReply: Received font operation "
+ << "reply OPCODE#" << (unsigned int) opcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ unsigned int length;
+
+ decodeBuffer.decodeValue(length, 8);
+
+ size = 32 + RoundUp4(length + 1);
+ buffer = writeBuffer_.addMessage(size);
+
+ unsigned char *next = buffer + 32;
+
+ *next++ = length;
+
+ decodeBuffer.decodeTextData(next, length);
+
+ #ifdef TEST
+
+ *logofs << "handleFontReply: Received tunneled font server "
+ << "path '";
+
+ for (unsigned int i = 0; i < length; i++)
+ {
+ *logofs << *(buffer + 32 + 1 + i);
+ }
+
+ *logofs << "' for FD#" << fd_ << ".\n" << logofs_flush;
+
+ #endif
+
+ if (fontPort_ == -1)
+ {
+ //
+ // The local side is not going to forward
+ // the font server connections.
+ //
+
+ #ifdef TEST
+ *logofs << "handleFontReply: WARNING! Returning an empty "
+ << "font server path.\n" << logofs_flush;
+ #endif
+
+ writeBuffer_.removeMessage(size);
+
+ size = 36;
+ buffer = writeBuffer_.addMessage(size);
+
+ //
+ // Set the length of the returned
+ // path to 0.
+ //
+
+ *(buffer + 32) = 0;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "handleFontReply: Returning the received "
+ << "font server path.\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int ClientChannel::handleCacheRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #ifdef TEST
+ *logofs << "handleCacheRequest: Handling cache request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ enableCache_ = *(buffer + 4);
+ enableSplit_ = *(buffer + 5);
+ enableSave_ = *(buffer + 6);
+ enableLoad_ = *(buffer + 7);
+
+ #ifdef TEST
+ *logofs << "handleCacheRequest: Set cache parameters to "
+ << " cache " << enableCache_ << " split " << enableSplit_
+ << " save " << enableSave_ << " load " << enableLoad_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Encode all the parameters as a
+ // single unsigned int so we can
+ // use an int cache.
+ //
+
+ unsigned int mask = enableSave_ << 8 | enableLoad_;
+
+ encodeBuffer.encodeCachedValue(mask, 32, clientCache_ ->
+ setCacheParametersCache);
+ return 0;
+}
+
+int ClientChannel::handleStartSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleStartSplitRequest: SPLIT! Handling start split "
+ << "request for FD#"<< fd_ << ".\n" << logofs_flush;
+ #endif
+
+ if (splitState_.resource != nothing)
+ {
+ #ifdef PANIC
+ *logofs << "handleStartSplitRequest: PANIC! SPLIT! Split requested "
+ << "for resource id " << (unsigned int) *(buffer + 1)
+ << " while handling resource " << splitState_.resource
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Split requested for "
+ << "resource id " << (unsigned int) *(buffer + 1)
+ << " while handling resource " << splitState_.resource
+ << ".\n";
+
+ return -1;
+ }
+ else if (fd_ != firstClient_)
+ {
+ //
+ // It can be that an auxiliary channel is the
+ // first to connect, then comes the agent that
+ // is actually using the NX opcodes.
+ //
+
+ #ifdef WARNING
+ *logofs << "handleStartSplitRequest: WARNING SPLIT! Split requested "
+ << "on FD#" << fd_ << " while expecting FD#" << firstClient_
+ << ".\n" << logofs_flush;
+ #endif
+
+ firstClient_ = fd_;
+ }
+
+ //
+ // Set the agent's resource for which we are
+ // going to split the request.
+ //
+
+ splitState_.resource = *(buffer + 1);
+
+ #if defined(TEST) || defined(SPLIT)
+
+ *logofs << "handleStartSplitRequest: SPLIT! Registered id "
+ << splitState_.resource << " as resource "
+ << "waiting for a split.\n" << logofs_flush;
+
+ if (clientStore_ -> getSplitStore(splitState_.resource) != NULL)
+ {
+ *logofs << "handleStartSplitRequest: WARNING! SPLIT! A split "
+ << "store for resource id " << splitState_.resource
+ << " already exists.\n" << logofs_flush;
+
+ clientStore_ -> dumpSplitStore(splitState_.resource);
+ }
+
+ #endif
+
+ //
+ // Send the selected resource to the remote.
+ //
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeCachedValue(splitState_.resource, 8,
+ clientCache_ -> resourceCache);
+
+ splitState_.mode = (T_split_mode) *(buffer + 4);
+
+ if (splitState_.mode != NXSplitModeAsync &&
+ splitState_.mode != NXSplitModeSync)
+ {
+ splitState_.mode = (T_split_mode) control -> SplitMode;
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleStartSplitRequest: SPLIT! Set split "
+ << "mode to '" << splitState_.mode << "' with "
+ << "provided value '" << (unsigned) *(buffer + 4)
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitState_.mode == NXSplitModeAsync)
+ {
+ *logofs << "handleStartSplitRequest: SPLIT! Selected split "
+ << "mode is [split_async].\n" << logofs_flush;
+ }
+ else if (splitState_.mode == NXSplitModeSync)
+ {
+ *logofs << "handleStartSplitRequest: SPLIT! Selected split "
+ << "mode is [split_sync].\n" << logofs_flush;
+ }
+
+ clientStore_ -> dumpSplitStores();
+
+ #endif
+
+ return 1;
+}
+
+int ClientChannel::handleEndSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleEndSplitRequest: SPLIT! Handling end split "
+ << "request for FD#"<< fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Verify that the agent resource matches.
+ //
+
+ if (splitState_.resource == nothing)
+ {
+ #ifdef PANIC
+ *logofs << "handleEndSplitRequest: PANIC! SPLIT! Received an end of "
+ << "split for resource id " << (unsigned int) *(buffer + 1)
+ << " without a previous start.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Received an end of split "
+ << "for resource id " << (unsigned int) *(buffer + 1)
+ << " without a previous start.\n";
+
+ return -1;
+ }
+ else if (splitState_.resource != *(buffer + 1))
+ {
+ #ifdef PANIC
+ *logofs << "handleEndSplitRequest: PANIC! SPLIT! Invalid resource id "
+ << (unsigned int) *(buffer + 1) << " received while "
+ << "waiting for resource id " << splitState_.resource
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid resource id "
+ << (unsigned int) *(buffer + 1) << " received while "
+ << "waiting for resource id " << splitState_.resource
+ << ".\n";
+
+ return -1;
+ }
+
+ //
+ // Send the selected resource to the remote.
+ //
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeCachedValue(splitState_.resource, 8,
+ clientCache_ -> resourceCache);
+
+ //
+ // Send the split notification events
+ // to the agent.
+ //
+
+ handleRestart(sequence_immediate, splitState_.resource);
+
+ //
+ // Check if we still have splits to send.
+ //
+
+ handleSplitPending();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleEndSplitRequest: SPLIT! Reset id "
+ << splitState_.resource << " as resource "
+ << "selected for splits.\n" << logofs_flush;
+ #endif
+
+ splitState_.resource = nothing;
+ splitState_.mode = split_none;
+
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStores();
+
+ #endif
+
+ return 1;
+}
+
+void ClientChannel::handleDecodeCharInfo(DecodeBuffer &decodeBuffer, unsigned char *nextDest)
+{
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ *serverCache_ -> queryFontCharInfoCache[0], 6);
+
+ PutUINT(value & 0xffff, nextDest, bigEndian_);
+ PutUINT(value >> 16, nextDest + 10, bigEndian_);
+
+ nextDest += 2;
+
+ for (unsigned int i = 1; i < 5; i++)
+ {
+ unsigned int _value;
+
+ decodeBuffer.decodeCachedValue(_value, 16,
+ *serverCache_ -> queryFontCharInfoCache[i], 6);
+
+ PutUINT(_value, nextDest, bigEndian_);
+
+ nextDest += 2;
+ }
+}
+
+int ClientChannel::setBigEndian(int flag)
+{
+ bigEndian_ = flag;
+
+ return 1;
+}
+
+int ClientChannel::setReferences()
+{
+ #ifdef TEST
+ *logofs << "ClientChannel: Initializing the static "
+ << "members for the client channels.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef REFERENCES
+
+ references_ = 0;
+
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/ClientChannel.h b/nxcomp/src/ClientChannel.h
new file mode 100644
index 000000000..ae92648d5
--- /dev/null
+++ b/nxcomp/src/ClientChannel.h
@@ -0,0 +1,434 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ClientChannel_H
+#define ClientChannel_H
+
+#include "List.h"
+#include "Channel.h"
+
+#include "SequenceQueue.h"
+
+#include "ClientReadBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// If defined, the client channel will
+// have the chance of suppressing more
+// opcodes for test purposes.
+//
+
+#undef LAME
+
+//
+// Define this to log a line when a
+// channel is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// This class implements the X client
+// side compression of the protocol.
+//
+
+class ClientChannel : public Channel
+{
+ public:
+
+ ClientChannel(Transport *transport, StaticCompressor *compressor);
+
+ virtual ~ClientChannel();
+
+ virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length);
+
+ virtual int handleWrite(const unsigned char *message, unsigned int length);
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
+ T_store_action action, int position, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store,
+ T_store_action action, int position, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+ {
+ return 0;
+ }
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer);
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split);
+
+ virtual int handleSplitEvent(DecodeBuffer &decodeBuffer);
+
+ virtual int handleMotion(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleCompletion(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleConfiguration();
+
+ virtual int handleFinish();
+
+ virtual int handleAsyncEvents()
+ {
+ return 0;
+ }
+
+ virtual int needSplit() const
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "needSplit: SPLIT! Returning pending split "
+ << "flag " << splitState_.pending << " with "
+ << clientStore_ -> getSplitTotalSize()
+ << " splits in the split stores.\n"
+ << logofs_flush;
+ #endif
+
+ return splitState_.pending;
+ }
+
+ virtual int needMotion() const
+ {
+ return 0;
+ }
+
+ virtual T_channel_type getType() const
+ {
+ return channel_x11;
+ }
+
+ int setBigEndian(int flag);
+
+ //
+ // Initialize the static members.
+ //
+
+ static int setReferences();
+
+ private:
+
+ int handleFastReadRequest(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size);
+
+ int handleFastWriteReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ int handleFastWriteEvent(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Intercept the request before the opcode
+ // is encoded.
+ //
+
+ int handleTaintRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size)
+ {
+ if (control -> TaintReplies > 0 &&
+ opcode == X_GetInputFocus)
+ {
+ return handleTaintSyncRequest(opcode, buffer, size);
+ }
+
+ #ifdef LAME
+
+ return handleTaintLameRequest(opcode, buffer, size);
+
+ #endif
+
+ return 0;
+ }
+
+ int handleTaintLameRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleTaintSyncRequest(unsigned char &opcode, const unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleTaintSyncError(unsigned char opcode);
+
+ //
+ // How to handle sequence counter
+ // in notification event.
+ //
+
+ enum T_sequence_mode
+ {
+ sequence_immediate,
+ sequence_deferred
+ };
+
+ //
+ // Send split notifications to the
+ // agent.
+ //
+
+ int handleRestart(T_sequence_mode mode, int resource);
+
+ int handleNotify(T_notification_type type, T_sequence_mode mode,
+ int resource, int request, int position);
+
+ //
+ // Other utility functions used in
+ // handling of the image streaming.
+ //
+
+ int mustSplitMessage(int resource)
+ {
+ return (clientStore_ -> getSplitStore(resource) ->
+ getSize() != 0);
+ }
+
+ int canSplitMessage(T_split_mode mode, unsigned int size)
+ {
+ return ((int) size >= control -> SplitDataThreshold &&
+ (clientStore_ -> getSplitTotalStorageSize() < control ->
+ SplitTotalStorageSize && clientStore_ ->
+ getSplitTotalSize() < control -> SplitTotalSize));
+ }
+
+ int canSendSplit(Split *split)
+ {
+ return (split -> getMode() != split_sync ||
+ split -> getState() == split_missed ||
+ split -> getState() == split_loaded);
+ }
+
+ int handleSplitSend(EncodeBuffer &encodeBuffer, int resource,
+ int &total, int &bytes);
+
+ Split *handleSplitFind(T_checksum checksum, int resource);
+
+ int handleSplitChecksum(EncodeBuffer &encodeBuffer, T_checksum checksum);
+
+ void handleSplitPending(int resource)
+ {
+ if (splitState_.pending == 0)
+ {
+ if (clientStore_ -> getSplitStore(resource) != NULL &&
+ clientStore_ -> getSplitStore(resource) ->
+ getFirstSplit() != NULL)
+ {
+ splitState_.pending = canSendSplit(clientStore_ ->
+ getSplitStore(resource) -> getFirstSplit());
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitPending: SPLIT! Set the pending "
+ << "split flag to " << splitState_.pending
+ << " with " << clientStore_ -> getSplitTotalSize()
+ << " splits in the split stores.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+ }
+
+ //
+ // Scan all the split stores to find
+ // if there is any split to send.
+ //
+
+ void handleSplitPending();
+
+ //
+ // Handle the MIT-SHM initialization
+ // messages exchanged with the remote
+ // proxy.
+ //
+
+ int handleShmemRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ int handleShmemReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Query the port used to tunnel
+ // the font server connections.
+ //
+
+ int handleFontRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ int handleFontReply(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Let the agent set the cache
+ // policy for image requests.
+ //
+
+ int handleCacheRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Encode the start and end split
+ // requests.
+ //
+
+ int handleStartSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ int handleEndSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Empty a split store and send the
+ // restart event.
+ //
+
+ int handleAbortSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Force the proxy to finalize all
+ // the pending split operations and
+ // restart a resource.
+ //
+
+ int handleFinishSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Tell the remote peer to send the
+ // split requests to the X server.
+ //
+
+ int handleCommitSplitRequest(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Other utilities.
+ //
+
+ void handleDecodeCharInfo(DecodeBuffer &, unsigned char *);
+
+ //
+ // Own read buffer. It is able to identify
+ // full messages read from the X descriptor.
+ //
+
+ ClientReadBuffer readBuffer_;
+
+ //
+ // Sequence number of last request coming
+ // from the X client or the X server.
+ //
+
+ unsigned int clientSequence_;
+ unsigned int serverSequence_;
+
+ //
+ // Last sequence number known by client. It can
+ // be the real sequence generated by server or
+ // the one of the last auto-generated event.
+ //
+
+ unsigned int lastSequence_;
+
+ //
+ // Used to identify replies based on sequence
+ // number of original request.
+ //
+
+ SequenceQueue sequenceQueue_;
+
+ //
+ // This is used to test the synchronous flush
+ // in the proxy.
+ //
+
+ int lastRequest_;
+
+ //
+ // Current resource id selected as target and
+ // other information related to the image split.
+ // The pending and abort flags are set when we
+ // want the proxy to give us a chance to send
+ // more split data. We also save the position
+ // of the last commit operation performed by
+ // channel so we can differentially encode the
+ // position of next message to commit.
+ //
+
+ typedef struct
+ {
+ int resource;
+ int pending;
+ int commit;
+ T_split_mode mode;
+
+ } T_split_state;
+
+ T_split_state splitState_;
+
+ //
+ // List of agent resources.
+ //
+
+ List splitResources_;
+
+ //
+ // How many sync requests we
+ // have tainted so far.
+ //
+
+ int taintCounter_;
+
+ private:
+
+ //
+ // Keep track of object
+ // creation and deletion.
+ //
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+#endif /* ClientChannel_H */
diff --git a/nxcomp/src/ClientProxy.cpp b/nxcomp/src/ClientProxy.cpp
new file mode 100644
index 000000000..3574d3bf3
--- /dev/null
+++ b/nxcomp/src/ClientProxy.cpp
@@ -0,0 +1,553 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Socket.h"
+#include "Agent.h"
+
+#include "ClientProxy.h"
+
+#include "ClientChannel.h"
+#include "GenericChannel.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Log the operations related to sending
+// and receiving the control tokens.
+//
+
+#undef TOKEN
+
+ClientProxy::ClientProxy(int proxyFd) : Proxy(proxyFd)
+{
+ fontServerPort_ = NULL;
+
+ #ifdef DEBUG
+ *logofs << "ClientProxy: Created new object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+ClientProxy::~ClientProxy()
+{
+ delete [] fontServerPort_;
+
+ #ifdef DEBUG
+ *logofs << "ClientProxy: Deleted object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+void ClientProxy::handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily,
+ sockaddr * xServerAddr, unsigned int xServerAddrLength)
+{
+ #ifdef DEBUG
+ *logofs << "ClientProxy: No display configuration to set.\n"
+ << logofs_flush;
+ #endif
+}
+
+void ClientProxy::handlePortConfiguration(ChannelEndPoint &cupsServerPort,
+ ChannelEndPoint &smbServerPort,
+ ChannelEndPoint &mediaServerPort,
+ ChannelEndPoint &httpServerPort,
+ const char *fontServerPort)
+{
+ delete [] fontServerPort_;
+
+ fontServerPort_ = new char[strlen(fontServerPort) + 1];
+
+ strcpy(fontServerPort_, fontServerPort);
+
+ #ifdef DEBUG
+ *logofs << "ClientProxy: Set port configuration to font '"
+ << fontServerPort_ << "'.\n"
+ << logofs_flush;
+ #endif
+}
+
+int ClientProxy::handleNewConnection(T_channel_type type, int clientFd)
+{
+ switch (type)
+ {
+ case channel_x11:
+ {
+ return handleNewXConnection(clientFd);
+ }
+ case channel_cups:
+ {
+ return handleNewGenericConnection(clientFd, channel_cups, "CUPS");
+ }
+ case channel_smb:
+ {
+ return handleNewGenericConnection(clientFd, channel_smb, "SMB");
+ }
+ case channel_media:
+ {
+ return handleNewGenericConnection(clientFd, channel_media, "media");
+ }
+ case channel_http:
+ {
+ return handleNewGenericConnection(clientFd, channel_http, "HTTP");
+ }
+ case channel_slave:
+ {
+ return handleNewSlaveConnection(clientFd);
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Unsupported channel with type '"
+ << getTypeName(type) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unsupported channel with type '"
+ << getTypeName(type) << "'.\n";
+
+ return -1;
+ }
+ }
+}
+
+int ClientProxy::handleNewConnectionFromProxy(T_channel_type type, int channelId)
+{
+ switch (type)
+ {
+ case channel_font:
+ {
+ int port = atoi(fontServerPort_);
+
+ if (port > 0)
+ {
+ //
+ // Connect on the TCP port number.
+ //
+
+ return handleNewGenericConnectionFromProxyTCP(channelId, channel_font, "localhost",
+ port, "font");
+ }
+ else
+ {
+ //
+ // Connect to the Unix path.
+ //
+
+ return handleNewGenericConnectionFromProxyUnix(channelId, channel_font,
+ fontServerPort_, "font");
+ }
+ }
+ case channel_slave:
+ {
+ return handleNewSlaveConnectionFromProxy(channelId);
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Unsupported channel with type '"
+ << getTypeName(type) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unsupported channel with type '"
+ << getTypeName(type) << "'.\n";
+
+ return -1;
+ }
+ }
+}
+
+int ClientProxy::handleNewAgentConnection(Agent *agent)
+{
+ int clientFd = agent -> getLocalFd();
+
+ int channelId = allocateChannelMap(clientFd);
+
+ if (channelId == -1)
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Maximum number of available "
+ << "channels exceeded.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Maximum number of available "
+ << "channels exceeded.\n";
+
+ return -1;
+ }
+
+ transports_[channelId] = agent -> getTransport();
+
+ agent_ = channelId;
+
+ return handleNewXConnection(clientFd);
+}
+
+int ClientProxy::handleNewXConnection(int clientFd)
+{
+ int channelId = getChannel(clientFd);
+
+ //
+ // Check if the channel has been
+ // already mapped.
+ //
+
+ if (channelId == -1)
+ {
+ channelId = allocateChannelMap(clientFd);
+
+ if (channelId == -1)
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Maximum number of available "
+ << "channels exceeded.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Maximum number of available "
+ << "channels exceeded.\n";
+
+ return -1;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "ClientProxy: X client descriptor FD#" << clientFd
+ << " mapped to channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Turn queuing off for path proxy-to-X-client.
+ //
+
+ if (control -> OptionClientNoDelay == 1)
+ {
+ SetNoDelay(clientFd, control -> OptionClientNoDelay);
+ }
+
+ //
+ // If requested, set the size of the TCP send
+ // and receive buffers.
+ //
+
+ if (control -> OptionClientSendBuffer != -1)
+ {
+ SetSendBuffer(clientFd, control -> OptionClientSendBuffer);
+ }
+
+ if (control -> OptionClientReceiveBuffer != -1)
+ {
+ SetReceiveBuffer(clientFd, control -> OptionClientReceiveBuffer);
+ }
+
+ if (allocateTransport(clientFd, channelId) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Starting from protocol level 3 client and server
+ // caches are created in proxy and shared between all
+ // channels. If remote proxy has older protocol level
+ // pointers are NULL and channels must create their
+ // own instances.
+ //
+
+ channels_[channelId] = new ClientChannel(transports_[channelId], compressor_);
+
+ if (channels_[channelId] == NULL)
+ {
+ deallocateTransport(channelId);
+
+ return -1;
+ }
+
+ increaseChannels(channelId);
+
+ //
+ // Propagate channel stores and caches to the new
+ // channel.
+ //
+
+ channels_[channelId] -> setOpcodes(opcodeStore_);
+
+ channels_[channelId] -> setStores(clientStore_, serverStore_);
+
+ channels_[channelId] -> setCaches(clientCache_, serverCache_);
+
+ int port = atoi(fontServerPort_);
+
+ if (port > 0 || *fontServerPort_ != '\0')
+ {
+ channels_[channelId] -> setPorts(1);
+ }
+
+ if (handleControl(code_new_x_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Let channel configure itself according
+ // to control parameters.
+ //
+
+ channels_[channelId] -> handleConfiguration();
+
+ return 1;
+}
+
+int ClientProxy::handleNewXConnectionFromProxy(int channelId)
+{
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Can't create a new X channel "
+ << "with ID#" << channelId << " at this side.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create a new X channel "
+ << "with ID#" << channelId << " at this side.\n";
+
+ return -1;
+}
+
+int ClientProxy::handleLoad(T_load_type type)
+{
+ int channelCount = getChannels(channel_x11);
+
+ if ((channelCount == 0 && type == load_if_first) ||
+ (channelCount > 0 && type == load_if_any))
+ {
+ #ifdef TEST
+ *logofs << "ClientProxy: Going to load content of client store.\n"
+ << logofs_flush;
+ #endif
+
+ int result = handleLoadStores();
+
+ if (result == 1)
+ {
+ if (handleControl(code_load_request) < 0)
+ {
+ return -1;
+ }
+
+ priority_ = 1;
+ }
+ else if (result < 0)
+ {
+ #ifdef WARNING
+ *logofs << "ClientProxy: WARNING! Failed to load content "
+ << "of persistent cache.\n" << logofs_flush;
+ #endif
+
+ //
+ // Don't abort the proxy connection in the case
+ // of a corrupted cache. By not sending the load
+ // message to the remote peer, both sides will
+ // start encoding messages using empty stores.
+ // This behaviour is compatible with old proxy
+ // versions.
+ //
+
+ if (channelCount == 0 && type == load_if_first)
+ {
+ if (handleResetStores() < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Failed to reset message stores.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Can't load the stores with "
+ << channelCount << " remaining channels.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ClientProxy::handleSave()
+{
+ //
+ // If no more X channels are remaining
+ // then save content of message stores.
+ //
+
+ int channelCount = getChannels(channel_x11);
+
+ if (channelCount == 0)
+ {
+ int result = handleSaveStores();
+
+ if (result == 1)
+ {
+ if (handleControl(code_save_request) < 0)
+ {
+ return -1;
+ }
+
+ priority_ = 1;
+
+ return 1;
+ }
+ else if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Failed to save stores "
+ << "to persistent cache.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Can't save the stores with "
+ << channelCount << " remaining channels.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ClientProxy::handleAsyncEvents()
+{
+ if (canRead() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: WARNING! Reading while writing "
+ << "with data available on the proxy link.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleRead() < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int ClientProxy::handleLoadFromProxy()
+{
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Invalid load control message "
+ << "received in proxy.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid load control message "
+ << "received in proxy.\n";
+
+ return -1;
+}
+
+int ClientProxy::handleSaveFromProxy()
+{
+ #ifdef PANIC
+ *logofs << "ClientProxy: PANIC! Invalid save control message "
+ << "received in proxy.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid save control message "
+ << "received in proxy.\n";
+
+ return -1;
+}
+
+int ClientProxy::handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient) const
+{
+ if (clientStore_ -> saveRequestStores(cachefs, md5StateStream, md5StateClient,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> saveReplyStores(cachefs, md5StateStream, md5StateClient,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> saveEventStores(cachefs, md5StateStream, md5StateClient,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int ClientProxy::handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const
+{
+ if (clientStore_ -> loadRequestStores(cachefs, md5StateStream,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> loadReplyStores(cachefs, md5StateStream,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> loadEventStores(cachefs, md5StateStream,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
diff --git a/nxcomp/src/ClientProxy.h b/nxcomp/src/ClientProxy.h
new file mode 100644
index 000000000..b89785b1a
--- /dev/null
+++ b/nxcomp/src/ClientProxy.h
@@ -0,0 +1,113 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ClientProxy_H
+#define ClientProxy_H
+
+#include "Proxy.h"
+
+//
+// Set the verbosity level.
+//
+
+#undef TEST
+#undef DEBUG
+
+class ClientProxy : public Proxy
+{
+ public:
+
+ ClientProxy(int proxyFD);
+
+ virtual ~ClientProxy();
+
+ virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily,
+ sockaddr *xServerAddr, unsigned int xServerAddrLength);
+
+ virtual void handlePortConfiguration(ChannelEndPoint &cupsServerPort,
+ ChannelEndPoint &smbServerPort,
+ ChannelEndPoint &mediaServerPort,
+ ChannelEndPoint &httpServerPort,
+ const char *fontServerPort);
+
+ protected:
+
+ //
+ // Create a new channel.
+ //
+
+ virtual int handleNewConnection(T_channel_type type, int clientFd);
+
+ virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId);
+
+ virtual int handleNewAgentConnection(Agent *agent);
+
+ virtual int handleNewXConnection(int clientFd);
+
+ virtual int handleNewXConnectionFromProxy(int channelId);
+
+ //
+ // Implement persistence according
+ // to our proxy mode.
+ //
+
+ virtual int handleLoad(T_load_type type);
+ virtual int handleSave();
+
+ virtual int handleAsyncEvents();
+
+ virtual int handleLoadFromProxy();
+ virtual int handleSaveFromProxy();
+
+ virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient) const;
+
+ virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const;
+
+ //
+ // Utility function used to realize
+ // a new connection.
+ //
+
+ protected:
+
+ virtual int checkLocalChannelMap(int channelId)
+ {
+ // Since ProtoStep7 (#issue 108)
+ return ((channelId & control -> ChannelMask) != 0);
+ }
+
+ //
+ // Ports where to forward extended services'
+ // TCP connections.
+ //
+
+ private:
+
+ char *fontServerPort_;
+};
+
+
+#endif /* ClientProxy_H */
diff --git a/nxcomp/src/ClientReadBuffer.cpp b/nxcomp/src/ClientReadBuffer.cpp
new file mode 100644
index 000000000..c8f4f69a4
--- /dev/null
+++ b/nxcomp/src/ClientReadBuffer.cpp
@@ -0,0 +1,178 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ClientReadBuffer.h"
+
+#include "ClientChannel.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+unsigned int ClientReadBuffer::suggestedLength(unsigned int pendingLength)
+{
+ //
+ // Even if the pending data is not
+ // enough to make a complete message,
+ // resize the buffer to accommodate
+ // it all.
+ //
+
+ unsigned int readLength = pendingLength;
+
+ if (pendingLength < remaining_)
+ {
+ readLength = remaining_;
+ }
+
+ return readLength;
+}
+
+int ClientReadBuffer::locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength)
+{
+ unsigned int size = end - start;
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: Locating message for FD#"
+ << transport_ -> fd() << " with " << size
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ if (firstMessage_)
+ {
+ if (size < 12)
+ {
+ remaining_ = 12 - size;
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ if (*start == 0x42)
+ {
+ bigEndian_ = 1;
+ }
+ else
+ {
+ bigEndian_ = 0;
+ }
+
+ channel_ -> setBigEndian(bigEndian_);
+
+ dataLength = 12 + RoundUp4(GetUINT(start + 6, bigEndian_)) +
+ RoundUp4(GetUINT(start + 8, bigEndian_));
+
+ //
+ // Send the data immediately if this is unlikely
+ // to be a X connection attempt.
+ //
+
+ if (dataLength > 4096)
+ {
+ #ifdef WARNING
+ *logofs << "ClientReadBuffer: WARNING! Flushing suspicious X "
+ << "connection with first request of " << dataLength
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ dataLength = size;
+ }
+ }
+ else
+ {
+ if (size < 4)
+ {
+ remaining_ = 4 - size;
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ dataLength = (GetUINT(start + 2, bigEndian_) << 2);
+
+ if (dataLength < 4)
+ {
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: WARNING! Assuming length 4 "
+ << "for suspicious message of length " << dataLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ dataLength = 4;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: Length of the next message is "
+ << dataLength << ".\n" << logofs_flush;
+ #endif
+
+ if (size < dataLength)
+ {
+ remaining_ = dataLength - size;
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ firstMessage_ = 0;
+
+ controlLength = 0;
+ trailerLength = 0;
+
+ remaining_ = 0;
+
+ #ifdef TEST
+ *logofs << "ClientReadBuffer: Located message with "
+ << "remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/ClientReadBuffer.h b/nxcomp/src/ClientReadBuffer.h
new file mode 100644
index 000000000..c557417fa
--- /dev/null
+++ b/nxcomp/src/ClientReadBuffer.h
@@ -0,0 +1,65 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ClientReadBuffer_H
+#define ClientReadBuffer_H
+
+#include "Control.h"
+#include "ReadBuffer.h"
+
+class ClientChannel;
+
+class ClientReadBuffer : public ReadBuffer
+{
+ public:
+
+ ClientReadBuffer(Transport *transport, ClientChannel *channel)
+
+ : ReadBuffer(transport), firstMessage_(1), channel_(channel)
+ {
+ }
+
+ virtual ~ClientReadBuffer()
+ {
+ }
+
+ protected:
+
+ virtual unsigned int suggestedLength(unsigned int pendingLength);
+
+ virtual int locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength);
+
+ int bigEndian_;
+
+ int firstMessage_;
+
+ ClientChannel *channel_;
+};
+
+#endif /* ClientReadBuffer_H */
diff --git a/nxcomp/src/ClientStore.cpp b/nxcomp/src/ClientStore.cpp
new file mode 100644
index 000000000..0c12eefd8
--- /dev/null
+++ b/nxcomp/src/ClientStore.cpp
@@ -0,0 +1,226 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ClientStore.h"
+
+//
+// Cached request classes.
+//
+
+#include "ChangeProperty.h"
+#include "SendEvent.h"
+#include "CreateGC.h"
+#include "ChangeGC.h"
+#include "CreatePixmap.h"
+#include "SetClipRectangles.h"
+#include "CopyArea.h"
+#include "PolyLine.h"
+#include "PolySegment.h"
+#include "PolyFillRectangle.h"
+#include "PutImage.h"
+#include "TranslateCoords.h"
+#include "GetImage.h"
+#include "ClearArea.h"
+#include "ConfigureWindow.h"
+#include "ShapeExtension.h"
+#include "RenderExtension.h"
+#include "PolyText8.h"
+#include "PolyText16.h"
+#include "ImageText8.h"
+#include "ImageText16.h"
+#include "PolyPoint.h"
+#include "PolyFillArc.h"
+#include "PolyArc.h"
+#include "FillPoly.h"
+#include "InternAtom.h"
+#include "GetProperty.h"
+#include "SetUnpackGeometry.h"
+#include "SetUnpackColormap.h"
+#include "SetUnpackAlpha.h"
+#include "PutPackedImage.h"
+#include "GenericRequest.h"
+
+//
+// Set the verbosity level.
+//
+
+#define WARNING
+#define PANIC
+#undef TEST
+
+ClientStore::ClientStore(StaticCompressor *compressor)
+
+ : compressor_(compressor)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cout;
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ requests_[i] = NULL;
+ }
+
+ requests_[X_ChangeProperty] = new ChangePropertyStore();
+ requests_[X_SendEvent] = new SendEventStore();
+ requests_[X_CreateGC] = new CreateGCStore();
+ requests_[X_SetClipRectangles] = new SetClipRectanglesStore();
+ requests_[X_CopyArea] = new CopyAreaStore();
+ requests_[X_PolyLine] = new PolyLineStore();
+ requests_[X_PolySegment] = new PolySegmentStore();
+ requests_[X_PolyFillRectangle] = new PolyFillRectangleStore();
+ requests_[X_PutImage] = new PutImageStore(compressor);
+ requests_[X_TranslateCoords] = new TranslateCoordsStore();
+ requests_[X_GetImage] = new GetImageStore();
+ requests_[X_ClearArea] = new ClearAreaStore();
+ requests_[X_ConfigureWindow] = new ConfigureWindowStore();
+ requests_[X_PolyText8] = new PolyText8Store();
+ requests_[X_PolyText16] = new PolyText16Store();
+ requests_[X_ImageText8] = new ImageText8Store();
+ requests_[X_ImageText16] = new ImageText16Store();
+ requests_[X_PolyPoint] = new PolyPointStore();
+ requests_[X_PolyFillArc] = new PolyFillArcStore();
+ requests_[X_PolyArc] = new PolyArcStore();
+ requests_[X_FillPoly] = new FillPolyStore();
+ requests_[X_InternAtom] = new InternAtomStore();
+ requests_[X_GetProperty] = new GetPropertyStore();
+
+ requests_[X_NXInternalShapeExtension] = new ShapeExtensionStore(compressor);
+ requests_[X_NXInternalGenericRequest] = new GenericRequestStore(compressor);
+ requests_[X_NXInternalRenderExtension] = new RenderExtensionStore(compressor);
+ requests_[X_NXSetUnpackGeometry] = new SetUnpackGeometryStore(compressor);
+ requests_[X_NXPutPackedImage] = new PutPackedImageStore(compressor);
+
+ // Since ProtoStep7 (#issue 108)
+ requests_[X_ChangeGC] = new ChangeGCStore();
+ requests_[X_CreatePixmap] = new CreatePixmapStore();
+ requests_[X_NXSetUnpackColormap] = new SetUnpackColormapStore(compressor);
+ requests_[X_NXSetUnpackAlpha] = new SetUnpackAlphaStore(compressor);
+
+ for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++)
+ {
+ splits_[i] = NULL;
+ }
+
+ commits_ = new CommitStore(compressor);
+}
+
+ClientStore::~ClientStore()
+{
+ if (logofs == NULL)
+ {
+ logofs = &cout;
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ delete requests_[i];
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++)
+ {
+ delete splits_[i];
+ }
+
+ delete commits_;
+}
+
+int ClientStore::saveRequestStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (requests_[i] != NULL &&
+ requests_[i] -> saveStore(cachefs, md5StateStream, md5StateClient,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "ClientStore: WARNING! Error saving request store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Error saving request store "
+ << "for opcode '" << (unsigned int) i << "'.\n";
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int ClientStore::loadRequestStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (requests_[i] != NULL &&
+ requests_[i] -> loadStore(cachefs, md5StateStream,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "ClientStore: WARNING! Error loading request store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+void ClientStore::dumpSplitStores() const
+{
+ for (int i = 0; i < CHANNEL_STORE_RESOURCE_LIMIT; i++)
+ {
+ if (splits_[i] != NULL)
+ {
+ splits_[i] -> dump();
+ }
+ }
+
+ if ((getSplitTotalSize() != 0 && getSplitTotalStorageSize() == 0) ||
+ (getSplitTotalSize() == 0 && getSplitTotalStorageSize() != 0))
+ {
+ #ifdef PANIC
+ *logofs << "ClientStore: PANIC! Inconsistency detected "
+ << "while handling the split stores.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+}
diff --git a/nxcomp/src/ClientStore.h b/nxcomp/src/ClientStore.h
new file mode 100644
index 000000000..009d87d9f
--- /dev/null
+++ b/nxcomp/src/ClientStore.h
@@ -0,0 +1,143 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ClientStore_H
+#define ClientStore_H
+
+#include "Message.h"
+#include "Split.h"
+
+#include "ChannelStore.h"
+
+class StaticCompressor;
+
+class ClientStore : public ChannelStore
+{
+ public:
+
+ ClientStore(StaticCompressor *compressor);
+
+ virtual ~ClientStore();
+
+ //
+ // Get the store based on the index.
+ //
+
+ MessageStore *getRequestStore(unsigned char opcode) const
+ {
+ return requests_[opcode];
+ }
+
+ SplitStore *getSplitStore(int resource) const
+ {
+ return splits_[resource];
+ }
+
+ int getSplitTotalSize() const
+ {
+ return SplitStore::getTotalSize();
+ }
+
+ int getSplitTotalStorageSize() const
+ {
+ return SplitStore::getTotalStorageSize();
+ }
+
+ CommitStore *getCommitStore() const
+ {
+ return commits_;
+ }
+
+ int getCommitSize() const
+ {
+ return commits_ -> getSize();
+ }
+
+ void dumpSplitStore(int resource) const
+ {
+ splits_[resource] -> dump();
+ }
+
+ void dumpCommitStore() const
+ {
+ commits_ -> dump();
+ }
+
+ void dumpSplitStores() const;
+
+ SplitStore *createSplitStore(int resource)
+ {
+ splits_[resource] = new SplitStore(compressor_, commits_, resource);
+
+ return splits_[resource];
+ }
+
+ void destroySplitStore(int resource)
+ {
+ delete splits_[resource];
+
+ splits_[resource] = NULL;
+ }
+
+ //
+ // Actually save the message store
+ // to disk according to proxy mode.
+ //
+
+ int saveRequestStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const;
+
+ int loadRequestStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const;
+
+ private:
+
+ //
+ // A client store contains requests.
+ //
+
+ MessageStore *requests_[CHANNEL_STORE_OPCODE_LIMIT];
+
+ //
+ // Client messages being split.
+ //
+
+ SplitStore *splits_[CHANNEL_STORE_RESOURCE_LIMIT];
+
+ //
+ // Messages having been recomposed.
+ //
+
+ CommitStore *commits_;
+
+ //
+ // Passed forward to the other stores.
+ //
+
+ StaticCompressor *compressor_;
+};
+
+#endif /* ClientStore_H */
diff --git a/nxcomp/src/Colormap.cpp b/nxcomp/src/Colormap.cpp
new file mode 100644
index 000000000..afe99ecae
--- /dev/null
+++ b/nxcomp/src/Colormap.cpp
@@ -0,0 +1,106 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Misc.h"
+#include "Unpack.h"
+#include "Colormap.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int UnpackColormap(unsigned char method, unsigned char *src_data, int src_size,
+ unsigned char *dst_data, int dst_size)
+{
+ if (*src_data == 0)
+ {
+ if (dst_size != src_size - 1)
+ {
+ #ifdef TEST
+ *logofs << "UnpackColormap: PANIC! Invalid destination size "
+ << dst_size << " with source " << src_size
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackColormap: Expanding " << src_size - 1
+ << " bytes of plain colormap data.\n" << logofs_flush;
+ #endif
+
+ memcpy(dst_data, src_data + 1, src_size - 1);
+
+ return 1;
+ }
+
+ unsigned int check_size = dst_size;
+
+ int result = ZDecompress(&unpackStream, dst_data, &check_size,
+ src_data + 1, src_size - 1);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackColormap: PANIC! Failure decompressing colormap data. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decompressing colormap data. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+ }
+ else if (check_size != (unsigned int) dst_size)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackColormap: PANIC! Size mismatch in colormap data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size mismatch in colormap data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackColormap: Decompressed " << src_size - 1
+ << " bytes to " << dst_size << " bytes of colormap data.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/Colormap.h b/nxcomp/src/Colormap.h
new file mode 100644
index 000000000..a96d003fa
--- /dev/null
+++ b/nxcomp/src/Colormap.h
@@ -0,0 +1,32 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Colormap_H
+#define Colormap_H
+
+int UnpackColormap(unsigned char method, unsigned char *src_data, int src_size,
+ unsigned char *dst_data, int dst_size);
+
+#endif /* Colormap_H */
diff --git a/nxcomp/src/ConfigureWindow.cpp b/nxcomp/src/ConfigureWindow.cpp
new file mode 100644
index 000000000..32d3153b9
--- /dev/null
+++ b/nxcomp/src/ConfigureWindow.cpp
@@ -0,0 +1,142 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ConfigureWindow.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ConfigureWindowStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ configureWindow -> window = GetULONG(buffer + 4, bigEndian);
+
+ configureWindow -> value_mask = GetUINT(buffer + 8, bigEndian);
+
+ //
+ // To increase effectiveness of the caching algorithm
+ // we remove the unused bytes carried in the data part.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Removing unused bytes from the data payload.\n" << logofs_flush;
+ #endif
+
+ configureWindow -> value_mask &= (1 << 7) - 1;
+
+ unsigned int mask = 0x1;
+ unsigned char *source = (unsigned char *) buffer + CONFIGUREWINDOW_DATA_OFFSET;
+ unsigned long value = 0;
+
+ for (unsigned int i = 0; i < 7; i++)
+ {
+ if (configureWindow -> value_mask & mask)
+ {
+ value = GetULONG(source, bigEndian);
+
+ value &= (1 << CONFIGUREWINDOW_FIELD_WIDTH[i]) - 1;
+
+ PutULONG(value, source, bigEndian);
+
+ source += 4;
+ }
+ mask <<= 1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ConfigureWindowStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(configureWindow -> window, buffer + 4, bigEndian);
+
+ PutUINT(configureWindow -> value_mask, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ConfigureWindowStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ConfigureWindowMessage *configureWindow = (ConfigureWindowMessage *) message;
+
+ *logofs << "ConfigureWindow: window " << configureWindow -> window
+ << ", value_mask " << configureWindow -> value_mask
+ << ", size " << configureWindow -> size_ << ".\n";
+
+ #endif
+}
+
+void ConfigureWindowStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 4, 4);
+ md5_append(md5_state_, buffer + 8, 2);
+}
diff --git a/nxcomp/src/ConfigureWindow.h b/nxcomp/src/ConfigureWindow.h
new file mode 100644
index 000000000..e02c2aae1
--- /dev/null
+++ b/nxcomp/src/ConfigureWindow.h
@@ -0,0 +1,178 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ConfigureWindow_H
+#define ConfigureWindow_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CONFIGUREWINDOW_ENABLE_CACHE 1
+#define CONFIGUREWINDOW_ENABLE_DATA 0
+#define CONFIGUREWINDOW_ENABLE_SPLIT 0
+#define CONFIGUREWINDOW_ENABLE_COMPRESS 0
+
+#define CONFIGUREWINDOW_DATA_LIMIT 32
+#define CONFIGUREWINDOW_DATA_OFFSET 12
+
+#define CONFIGUREWINDOW_CACHE_SLOTS 3000
+#define CONFIGUREWINDOW_CACHE_THRESHOLD 5
+#define CONFIGUREWINDOW_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ConfigureWindowMessage : public Message
+{
+ friend class ConfigureWindowStore;
+
+ public:
+
+ ConfigureWindowMessage()
+ {
+ }
+
+ ~ConfigureWindowMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int window;
+ unsigned short value_mask;
+};
+
+class ConfigureWindowStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ConfigureWindowStore() : MessageStore()
+ {
+ enableCache = CONFIGUREWINDOW_ENABLE_CACHE;
+ enableData = CONFIGUREWINDOW_ENABLE_DATA;
+ enableSplit = CONFIGUREWINDOW_ENABLE_SPLIT;
+ enableCompress = CONFIGUREWINDOW_ENABLE_COMPRESS;
+
+ dataLimit = CONFIGUREWINDOW_DATA_LIMIT;
+ dataOffset = CONFIGUREWINDOW_DATA_OFFSET;
+
+ cacheSlots = CONFIGUREWINDOW_CACHE_SLOTS;
+ cacheThreshold = CONFIGUREWINDOW_CACHE_THRESHOLD;
+ cacheLowerThreshold = CONFIGUREWINDOW_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ConfigureWindowStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ConfigureWindow";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ConfigureWindow;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ConfigureWindowMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ConfigureWindowMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ConfigureWindowMessage((const ConfigureWindowMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ConfigureWindowMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ConfigureWindow_H */
diff --git a/nxcomp/src/Control.cpp b/nxcomp/src/Control.cpp
new file mode 100644
index 000000000..75fd56e3f
--- /dev/null
+++ b/nxcomp/src/Control.cpp
@@ -0,0 +1,822 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "NX.h"
+#include "NXpack.h"
+
+#include "Control.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Flush immediately on prioritized messages.
+//
+
+#define FLUSH_PRIORITY 0
+
+//
+// Maximum number of bytes sent for each token.
+//
+
+#define TOKEN_SIZE 1536
+
+//
+// Maximum number of tokens that can be spent
+// by the client proxy before having to block
+// waiting for a token reply.
+//
+
+#define TOKEN_LIMIT 24
+
+//
+// By default assume the proxy is running as a
+// standalone program.
+//
+
+#define LINK_ENCRYPTED 0
+
+//
+// Maximum number of pids the proxy will record
+// and kill at shutdown.
+//
+
+#define KILL_LIMIT 16
+
+//
+// Allocate on the NX client side channels whose
+// ids are a multiple of 8 (starting from 0). All
+// the other ids can be used to allocate channels
+// at the NX server side (X client side).
+//
+
+#define CHANNEL_MASK 0x07
+
+//
+// Kill session if control parameters cannot be
+// negotiated before this timeout.
+//
+
+#define INIT_TIMEOUT 60000
+
+//
+// Enter the congestion state if the remote does
+// not reply to a ping within the given amount
+// of time.
+//
+
+#define PING_TIMEOUT 5000
+
+//
+// Only send one motion event any N milliseconds.
+//
+
+#define MOTION_TIMEOUT 0
+
+
+//
+// Force an update of the congestion counter if
+// the proxy is idle for this time.
+//
+
+#define IDLE_TIMEOUT 50
+
+//
+// Close X connection if can't write before this
+// timeout.
+//
+
+#define CHANNEL_TIMEOUT 10000
+
+//
+// Warn user (or close proxy connection) if don't
+// receive any data before this timeout.
+//
+
+#define PROXY_TIMEOUT 120000
+
+//
+// How many milliseconds to wait for the shared
+// memory completion event to become available.
+//
+
+#define SHMEM_TIMEOUT 200
+//
+// Before closing down the proxy, wait for the
+// given amount of miliseconds to let all the
+// running applications to close down their
+// connections.
+//
+// A null timeout will cause the proxy to wait
+// indefinitely, until the watchdog process is
+// killed. This is usually the way the proxy is
+// started by the NX server. If on the other
+// hand a timeout is given and there no channel
+// is remaining, the proxy will be closed down
+// using a small timeout, presently of 500 ms.
+//
+
+#define CLEANUP_TIMEOUT 3000
+
+//
+// Wait this amount of milliseconds after any
+// iteration of the house-keeping process.
+//
+
+#define KEEPER_TIMEOUT 60000
+
+//
+// In case of timeout, select can return control
+// to program earlier or later of this amount of
+// ms. Consider this when calculating if timeout
+// is elapsed.
+//
+
+#define LATENCY_TIMEOUT 1
+
+//
+// Control memory allocation in transport
+// and other classes.
+//
+
+#define TRANSPORT_X_BUFFER_SIZE 131072
+#define TRANSPORT_PROXY_BUFFER_SIZE 65536
+#define TRANSPORT_GENERIC_BUFFER_SIZE 16384
+
+#define TRANSPORT_X_BUFFER_THRESHOLD 262144
+#define TRANSPORT_PROXY_BUFFER_THRESHOLD 131072
+#define TRANSPORT_GENERIC_BUFFER_THRESHOLD 32768
+
+//
+// Never allow buffers to exceed this limit.
+//
+
+#define TRANSPORT_MAXIMUM_BUFFER_SIZE 393216
+
+//
+// Immediately flush the accumulated data to
+// the X server if the write buffer exceeds
+// this size.
+//
+
+#define TRANSPORT_FLUSH_BUFFER_SIZE 16384
+
+//
+// Defaults used for socket options.
+//
+
+#define OPTION_PROXY_KEEP_ALIVE 0
+#define OPTION_PROXY_LOW_DELAY 1
+#define OPTION_PROXY_CLIENT_NO_DELAY 1
+#define OPTION_PROXY_SERVER_NO_DELAY 1
+#define OPTION_CLIENT_NO_DELAY 1
+#define OPTION_SERVER_NO_DELAY 1
+
+#define OPTION_PROXY_RECEIVE_BUFFER -1
+#define OPTION_CLIENT_RECEIVE_BUFFER -1
+#define OPTION_SERVER_RECEIVE_BUFFER -1
+
+#define OPTION_PROXY_SEND_BUFFER -1
+#define OPTION_CLIENT_SEND_BUFFER -1
+#define OPTION_SERVER_SEND_BUFFER -1
+
+#define OPTION_PROXY_RETRY_CONNECT 30
+#define OPTION_PROXY_RETRY_ACCEPT 3
+#define OPTION_SERVER_RETRY_CONNECT 3
+
+//
+// Defaults used for cache persistence.
+//
+
+#define PERSISTENT_CACHE_THRESHOLD 102400
+
+#define PERSISTENT_CACHE_ENABLE_LOAD 1
+#define PERSISTENT_CACHE_ENABLE_SAVE 1
+
+#define PERSISTENT_CACHE_CHECK_ON_SHUTDOWN 0
+
+#define PERSISTENT_CACHE_LOAD_PACKED 1
+#define PERSISTENT_CACHE_LOAD_RENDER 1
+
+#define PERSISTENT_CACHE_DISK_LIMIT 33554432
+
+//
+// Defaults used for image cache.
+//
+
+#define IMAGE_CACHE_ENABLE_LOAD 0
+#define IMAGE_CACHE_ENABLE_SAVE 0
+
+#define IMAGE_CACHE_DISK_LIMIT 33554432
+
+//
+// Suggested defaults for read length parameters
+// used by read buffer classes.
+//
+
+#define CLIENT_INITIAL_READ_SIZE 8192
+#define CLIENT_MAXIMUM_BUFFER_SIZE 262144
+
+#define SERVER_INITIAL_READ_SIZE 8192
+#define SERVER_MAXIMUM_BUFFER_SIZE 65536
+
+#define PROXY_INITIAL_READ_SIZE 65536
+#define PROXY_MAXIMUM_BUFFER_SIZE 262144 + 1024
+
+#define GENERIC_INITIAL_READ_SIZE 8192
+#define GENERIC_MAXIMUM_BUFFER_SIZE 8192
+
+//
+// Calculate bitrate in given time frames.
+// Values are in milliseconds.
+//
+
+#define SHORT_BITRATE_TIME_FRAME 5000
+#define LONG_BITRATE_TIME_FRAME 30000
+
+//
+// Bandwidth control. A value of 0 means no
+// limit. Values are stored internally in
+// bytes per second.
+//
+
+#define CLIENT_BITRATE_LIMIT 0
+#define SERVER_BITRATE_LIMIT 0
+
+//
+// Default values for cache control. We limit
+// the maximum size of a request to 262144 but
+// we need to consider the replies, whose size
+// may be up to 4MB.
+//
+
+#define MINIMUM_MESSAGE_SIZE 4
+#define MAXIMUM_MESSAGE_SIZE 4194304
+#define MAXIMUM_REQUEST_SIZE 262144
+
+#define CLIENT_TOTAL_STORAGE_SIZE 8388608
+#define SERVER_TOTAL_STORAGE_SIZE 8388608
+
+#define STORE_TIME_LIMIT 3600
+
+#define STORE_HITS_LOAD_BONUS 10
+#define STORE_HITS_ADD_BONUS 20
+#define STORE_HITS_LIMIT 100
+
+#define STORE_HITS_TOUCH 1
+#define STORE_HITS_UNTOUCH 2
+
+//
+// Default parameters for message splitting.
+//
+
+#define SPLIT_MODE 1
+#define SPLIT_TIMEOUT 50
+#define SPLIT_TOTAL_SIZE 128
+#define SPLIT_TOTAL_STORAGE_SIZE 1048576
+#define SPLIT_DATA_THRESHOLD 65536
+#define SPLIT_DATA_PACKET_LIMIT 24576
+
+//
+// Agent related parameters.
+//
+
+#define PACK_METHOD 63
+#define PACK_QUALITY 9
+#define HIDE_RENDER 0
+#define TAINT_REPLIES 1
+#define TAINT_THRESHOLD 8
+
+//
+// In current version only X server support is
+// implemented. Note that use of shared memory
+// is negotiated according to options provided
+// by the user.
+//
+
+#define SHMEM_CLIENT 0
+#define SHMEM_SERVER 1
+
+//
+// Default size of shared memory segments used
+// in MIT-SHM support.
+//
+
+#define SHMEM_CLIENT_SIZE 0
+#define SHMEM_SERVER_SIZE 2097152
+
+//
+// What do we do at the end of session? If this
+// flag is set, we launch a new client letting
+// the user run a new NX session.
+//
+
+#define ENABLE_RESTART_ON_SHUTDOWN 0
+
+//
+// Do we produce a core dump on fatal errors?
+//
+
+#define ENABLE_CORE_DUMP_ON_ABORT 0
+
+//
+// Reopen the log file if it exceeds this size.
+//
+
+#define FILE_SIZE_LIMIT 60000000
+
+//
+// Check periodically if we need to truncate the
+// log file. By default check every minute.
+//
+
+#define FILE_SIZE_CHECK_TIMEOUT 60000
+
+//
+// Protocol version compatibility values
+//
+
+const int Control::NX_MIN_PROTO_STEP = 10;
+const int Control::NX_MAX_PROTO_STEP = 10;
+const char* const Control::NXPROXY_COMPATIBILITY_VERSION = "3.5.0";
+
+//
+// Set defaults for control. They should be what
+// you get in case of 'local' connection.
+//
+
+Control::Control()
+{
+ ProxyMode = proxy_undefined;
+ ProxyStage = stage_undefined;
+ SessionMode = session_undefined;
+ FlushPolicy = policy_undefined;
+ LinkMode = link_undefined;
+
+ LinkEncrypted = LINK_ENCRYPTED;
+ FlushPriority = FLUSH_PRIORITY;
+
+ TokenSize = TOKEN_SIZE;
+ TokenLimit = TOKEN_LIMIT;
+
+ ChannelMask = CHANNEL_MASK;
+
+ InitTimeout = INIT_TIMEOUT;
+ PingTimeout = PING_TIMEOUT;
+ MotionTimeout = MOTION_TIMEOUT;
+ IdleTimeout = IDLE_TIMEOUT;
+
+ ChannelTimeout = CHANNEL_TIMEOUT;
+ ProxyTimeout = PROXY_TIMEOUT;
+ ShmemTimeout = SHMEM_TIMEOUT;
+
+ CleanupTimeout = CLEANUP_TIMEOUT;
+ KeeperTimeout = KEEPER_TIMEOUT;
+ LatencyTimeout = LATENCY_TIMEOUT;
+
+ FileSizeLimit = FILE_SIZE_LIMIT;
+ FileSizeCheckTimeout = FILE_SIZE_CHECK_TIMEOUT;
+
+ EnableRestartOnShutdown = ENABLE_RESTART_ON_SHUTDOWN;
+
+ KillDaemonOnShutdownLimit = KILL_LIMIT;
+
+ KillDaemonOnShutdown = new int[KillDaemonOnShutdownLimit];
+
+ for (int i = 0; i < KILL_LIMIT; i++)
+ {
+ KillDaemonOnShutdown[i] = -1;
+ }
+
+ KillDaemonOnShutdownNumber = 0;
+
+ EnableCoreDumpOnAbort = ENABLE_CORE_DUMP_ON_ABORT;
+
+ //
+ // Collect statistics by default.
+ //
+
+ EnableStatistics = 1;
+
+ //
+ // Memory restrictions if any.
+ //
+
+ LocalMemoryLevel = -1;
+
+ //
+ // Compression must be negotiated between proxies.
+ //
+
+ LocalDeltaCompression = -1;
+ RemoteDeltaCompression = -1;
+
+ LocalDataCompression = -1;
+ LocalStreamCompression = -1;
+
+ RemoteDataCompression = -1;
+ RemoteStreamCompression = -1;
+
+ LocalDataCompressionLevel = -1;
+ LocalDataCompressionThreshold = -1;
+ LocalStreamCompressionLevel = -1;
+
+ RemoteDataCompressionLevel = -1;
+ RemoteStreamCompressionLevel = -1;
+
+ //
+ // Transport buffers' allocation parameters.
+ //
+
+ TransportXBufferSize = TRANSPORT_X_BUFFER_SIZE;
+ TransportProxyBufferSize = TRANSPORT_PROXY_BUFFER_SIZE;
+ TransportGenericBufferSize = TRANSPORT_GENERIC_BUFFER_SIZE;
+
+ TransportXBufferThreshold = TRANSPORT_X_BUFFER_THRESHOLD;
+ TransportProxyBufferThreshold = TRANSPORT_PROXY_BUFFER_THRESHOLD;
+ TransportGenericBufferThreshold = TRANSPORT_GENERIC_BUFFER_THRESHOLD;
+
+ TransportMaximumBufferSize = TRANSPORT_MAXIMUM_BUFFER_SIZE;
+
+ //
+ // Flush the write buffer if it exceeds
+ // this size.
+ //
+
+ TransportFlushBufferSize = TRANSPORT_FLUSH_BUFFER_SIZE;
+
+ //
+ // Socket options.
+ //
+
+ OptionProxyKeepAlive = OPTION_PROXY_KEEP_ALIVE;
+ OptionProxyLowDelay = OPTION_PROXY_LOW_DELAY;
+ OptionProxyClientNoDelay = OPTION_PROXY_CLIENT_NO_DELAY;
+ OptionProxyServerNoDelay = OPTION_PROXY_SERVER_NO_DELAY;
+ OptionClientNoDelay = OPTION_CLIENT_NO_DELAY;
+ OptionServerNoDelay = OPTION_SERVER_NO_DELAY;
+
+ OptionProxyReceiveBuffer = OPTION_PROXY_RECEIVE_BUFFER;
+ OptionClientReceiveBuffer = OPTION_CLIENT_RECEIVE_BUFFER;
+ OptionServerReceiveBuffer = OPTION_SERVER_RECEIVE_BUFFER;
+
+ OptionProxySendBuffer = OPTION_PROXY_SEND_BUFFER;
+ OptionClientSendBuffer = OPTION_CLIENT_SEND_BUFFER;
+ OptionServerSendBuffer = OPTION_SERVER_SEND_BUFFER;
+
+ OptionProxyRetryAccept = OPTION_PROXY_RETRY_ACCEPT;
+ OptionProxyRetryConnect = OPTION_PROXY_RETRY_CONNECT;
+ OptionServerRetryConnect = OPTION_SERVER_RETRY_CONNECT;
+
+ //
+ // Base NX directories.
+ //
+
+ HomePath = NULL;
+ RootPath = NULL;
+ SystemPath = NULL;
+ TempPath = NULL;
+ ClientPath = NULL;
+
+ //
+ // Set defaults for handling persistent cache.
+ //
+
+ PersistentCachePath = NULL;
+ PersistentCacheName = NULL;
+
+ PersistentCacheThreshold = PERSISTENT_CACHE_THRESHOLD;
+
+ PersistentCacheEnableLoad = PERSISTENT_CACHE_ENABLE_LOAD;
+ PersistentCacheEnableSave = PERSISTENT_CACHE_ENABLE_SAVE;
+
+ PersistentCacheCheckOnShutdown = PERSISTENT_CACHE_CHECK_ON_SHUTDOWN;
+
+ PersistentCacheLoadPacked = PERSISTENT_CACHE_LOAD_PACKED;
+ PersistentCacheLoadRender = PERSISTENT_CACHE_LOAD_RENDER;
+
+ PersistentCacheDiskLimit = PERSISTENT_CACHE_DISK_LIMIT;
+
+ //
+ // Set defaults for image cache.
+ //
+
+ ImageCachePath = NULL;
+
+ ImageCacheEnableLoad = IMAGE_CACHE_ENABLE_LOAD;
+ ImageCacheEnableSave = IMAGE_CACHE_ENABLE_SAVE;
+
+ ImageCacheDiskLimit = IMAGE_CACHE_DISK_LIMIT;
+
+ //
+ // Set defaults for the read buffers.
+ //
+
+ ClientInitialReadSize = CLIENT_INITIAL_READ_SIZE;
+ ClientMaximumBufferSize = CLIENT_MAXIMUM_BUFFER_SIZE;
+
+ ServerInitialReadSize = SERVER_INITIAL_READ_SIZE;
+ ServerMaximumBufferSize = SERVER_MAXIMUM_BUFFER_SIZE;
+
+ ProxyInitialReadSize = PROXY_INITIAL_READ_SIZE;
+ ProxyMaximumBufferSize = PROXY_MAXIMUM_BUFFER_SIZE;
+
+ GenericInitialReadSize = GENERIC_INITIAL_READ_SIZE;
+ GenericMaximumBufferSize = GENERIC_MAXIMUM_BUFFER_SIZE;
+
+ ShortBitrateTimeFrame = SHORT_BITRATE_TIME_FRAME;
+ LongBitrateTimeFrame = LONG_BITRATE_TIME_FRAME;
+
+ //
+ // Bandwidth control.
+ //
+
+ LocalBitrateLimit = -1;
+
+ ClientBitrateLimit = CLIENT_BITRATE_LIMIT;
+ ServerBitrateLimit = SERVER_BITRATE_LIMIT;
+
+ //
+ // Default parameters for message handling.
+ //
+
+ ClientTotalStorageSize = CLIENT_TOTAL_STORAGE_SIZE;
+ ServerTotalStorageSize = SERVER_TOTAL_STORAGE_SIZE;
+
+ LocalTotalStorageSize = -1;
+ RemoteTotalStorageSize = -1;
+
+ StoreTimeLimit = STORE_TIME_LIMIT;
+
+ StoreHitsLoadBonus = STORE_HITS_LOAD_BONUS;
+ StoreHitsAddBonus = STORE_HITS_ADD_BONUS;
+ StoreHitsLimit = STORE_HITS_LIMIT;
+
+ StoreHitsTouch = STORE_HITS_TOUCH;
+ StoreHitsUntouch = STORE_HITS_UNTOUCH;
+
+ MinimumMessageSize = MINIMUM_MESSAGE_SIZE;
+ MaximumMessageSize = MAXIMUM_MESSAGE_SIZE;
+ MaximumRequestSize = MAXIMUM_REQUEST_SIZE;
+
+ SplitMode = SPLIT_MODE;
+ SplitTimeout = SPLIT_TIMEOUT;
+ SplitTotalSize = SPLIT_TOTAL_SIZE;
+ SplitTotalStorageSize = SPLIT_TOTAL_STORAGE_SIZE;
+ SplitDataThreshold = SPLIT_DATA_THRESHOLD;
+ SplitDataPacketLimit = SPLIT_DATA_PACKET_LIMIT;
+
+ PackMethod = PACK_METHOD;
+ PackQuality = PACK_QUALITY;
+ HideRender = HIDE_RENDER;
+ TaintReplies = TAINT_REPLIES;
+ TaintThreshold = TAINT_THRESHOLD;
+
+ ShmemClient = SHMEM_CLIENT;
+ ShmemServer = SHMEM_SERVER;
+
+ ShmemClientSize = SHMEM_CLIENT_SIZE;
+ ShmemServerSize = SHMEM_SERVER_SIZE;
+
+ //
+ // Get local version number from compile time
+ // settings. Version of remote proxy will be
+ // checked at connection time.
+ //
+
+ RemoteVersionMajor = -1;
+ RemoteVersionMinor = -1;
+ RemoteVersionPatch = -1;
+ RemoteVersionMaintenancePatch = -1;
+
+ CompatVersionMajor = -1;
+ CompatVersionMinor = -1;
+ CompatVersionPatch = -1;
+ CompatVersionMaintenancePatch = -1;
+
+ LocalVersionMajor = NXMajorVersion();
+ LocalVersionMinor = NXMinorVersion();
+ LocalVersionPatch = NXPatchVersion();
+ LocalVersionMaintenancePatch = NXMaintenancePatchVersion();
+
+ #ifdef TEST
+ *logofs << "Control: Major version is " << LocalVersionMajor
+ << " minor is " << LocalVersionMinor << " patch is "
+ << LocalVersionPatch << " Maintenance version is "
+ << LocalVersionMaintenancePatch << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Initialize local implemented methods later
+ // and negotiate remote methods at connection
+ // time.
+ //
+
+ LocalUnpackMethods = NULL;
+ RemoteUnpackMethods = NULL;
+
+ //
+ // Set to 1 those methods which are implemented.
+ //
+
+ setLocalUnpackMethods();
+
+ //
+ // Set the protocol version at the
+ // time the session is negotiated.
+ //
+
+ protoStep_ = 0;
+}
+
+Control::~Control()
+{
+ if (KillDaemonOnShutdown != NULL)
+ {
+ delete [] KillDaemonOnShutdown;
+ }
+
+ if (HomePath != NULL)
+ {
+ delete [] HomePath;
+ }
+
+ if (RootPath != NULL)
+ {
+ delete [] RootPath;
+ }
+
+ if (SystemPath != NULL)
+ {
+ delete [] SystemPath;
+ }
+
+ if (TempPath != NULL)
+ {
+ delete [] TempPath;
+ }
+
+ if (ClientPath != NULL)
+ {
+ delete [] ClientPath;
+ }
+
+ if (PersistentCachePath != NULL)
+ {
+ delete [] PersistentCachePath;
+ }
+
+ if (PersistentCacheName != NULL)
+ {
+ delete [] PersistentCacheName;
+ }
+
+ if (LocalUnpackMethods != NULL)
+ {
+ delete [] LocalUnpackMethods;
+ }
+
+ if (RemoteUnpackMethods != NULL)
+ {
+ delete [] RemoteUnpackMethods;
+ }
+
+ if (ImageCachePath != NULL)
+ {
+ delete [] ImageCachePath;
+ }
+}
+
+//
+// Set the protocol step based on the
+// remote version.
+//
+
+void Control::setProtoStep(int step)
+{
+ if (isValidProtoStep(step))
+ {
+ protoStep_ = step;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Control: PANIC! Invalid protocol step "
+ << "with value " << step << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+}
+
+int Control::getProtoStep()
+{
+ if (isValidProtoStep(protoStep_))
+ {
+ return protoStep_;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Control: PANIC! Can't identify the "
+ << "protocol step.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+}
+
+//
+// Set here the pack/unpack methods that are
+// implemented by this NX proxy.
+//
+
+void Control::setLocalUnpackMethods()
+{
+ LocalUnpackMethods = new unsigned char[PACK_METHOD_LIMIT];
+ RemoteUnpackMethods = new unsigned char[PACK_METHOD_LIMIT];
+
+ for (int i = 0; i < PACK_METHOD_LIMIT; i++)
+ {
+ LocalUnpackMethods[i] = 0;
+ RemoteUnpackMethods[i] = 0;
+ }
+
+ LocalUnpackMethods[NO_PACK] = 1;
+
+ LocalUnpackMethods[PACK_MASKED_8_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_64_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_256_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_512_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_4K_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_32K_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_64K_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_256K_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_2M_COLORS] = 1;
+ LocalUnpackMethods[PACK_MASKED_16M_COLORS] = 1;
+
+ LocalUnpackMethods[PACK_RAW_8_BITS] = 1;
+ LocalUnpackMethods[PACK_RAW_16_BITS] = 1;
+ LocalUnpackMethods[PACK_RAW_24_BITS] = 1;
+
+ LocalUnpackMethods[PACK_COLORMAP_256_COLORS] = 1;
+
+ LocalUnpackMethods[PACK_JPEG_8_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_64_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_256_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_512_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_4K_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_32K_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_64K_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_256K_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_2M_COLORS] = 1;
+ LocalUnpackMethods[PACK_JPEG_16M_COLORS] = 1;
+
+ LocalUnpackMethods[PACK_PNG_8_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_64_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_256_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_512_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_4K_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_32K_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_64K_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_256K_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_2M_COLORS] = 1;
+ LocalUnpackMethods[PACK_PNG_16M_COLORS] = 1;
+
+ LocalUnpackMethods[PACK_RGB_16M_COLORS] = 1;
+ LocalUnpackMethods[PACK_RLE_16M_COLORS] = 1;
+
+ LocalUnpackMethods[PACK_ALPHA] = 1;
+ LocalUnpackMethods[PACK_COLORMAP] = 1;
+
+ LocalUnpackMethods[PACK_BITMAP_16M_COLORS] = 1;
+}
diff --git a/nxcomp/src/Control.h b/nxcomp/src/Control.h
new file mode 100644
index 000000000..764fca2c1
--- /dev/null
+++ b/nxcomp/src/Control.h
@@ -0,0 +1,764 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Control_H
+#define Control_H
+
+#include "NXpack.h"
+
+#include "Misc.h"
+#include "Types.h"
+#include "Timestamp.h"
+#include "Statistics.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// This is the mode proxy is running.
+//
+
+typedef enum
+{
+ proxy_undefined = -1,
+ proxy_client,
+ proxy_server,
+ proxy_last_tag
+}
+T_proxy_mode;
+
+//
+// Handle advances in the connection
+// procedure.
+//
+
+typedef enum
+{
+ stage_undefined,
+ stage_initializing,
+ stage_connecting,
+ stage_connected,
+ stage_waiting_forwarder_version,
+ stage_waiting_forwarder_options,
+ stage_sending_forwarder_options,
+ stage_waiting_proxy_version,
+ stage_waiting_proxy_options,
+ stage_sending_proxy_options,
+ stage_waiting_proxy_caches,
+ stage_sending_proxy_caches,
+ stage_operational,
+ stage_terminating,
+ stage_terminated
+}
+T_proxy_stage;
+
+//
+// Hint about whether or not the proxy is
+// connected to a NX agen.
+//
+
+typedef enum
+{
+ session_undefined = -1,
+ session_agent,
+ session_shadow,
+ session_proxy,
+ session_last_tag
+}
+T_session_mode;
+
+//
+// Set how data will be written to the peer
+// socket.
+//
+
+typedef enum
+{
+ policy_undefined = -1,
+ policy_immediate,
+ policy_deferred
+}
+T_flush_policy;
+
+//
+// Link mode, after negotiation, will be set to
+// any of the values defined in the NXproto.h.
+//
+
+#define link_undefined -1;
+
+//
+// This class collects functioning parameters,
+// to be configurable at run-time. They are for
+// the most part regarding timeouts, transport
+// and message stores handling.
+//
+
+class Control
+{
+ public:
+
+ //
+ // Does proxy run in client mode or server mode?
+ // As soon as we'll have gone through parsing of
+ // the command line options the current mode will
+ // be propagated to the control class.
+ //
+
+ T_proxy_mode ProxyMode;
+
+ //
+ // Goes from initializing to operational.
+ //
+
+ T_proxy_stage ProxyStage;
+
+ //
+ // Hint about type of session currently running.
+ //
+
+ T_session_mode SessionMode;
+
+ //
+ // Either immediate or defferred flushes.
+ //
+
+ T_flush_policy FlushPolicy;
+
+ //
+ // If set, the channels will try to flush the
+ // encoded data whenever there is a prioritized
+ // message. Depending on the flush policy, this
+ // may determine an immediate flush or an event
+ // being generated telling to the agent that it
+ // should flush the proxy link.
+ //
+
+ int FlushPriority;
+
+ //
+ // Id corresponding to link speed negotiated
+ // between proxies.
+ //
+
+ int LinkMode;
+
+ //
+ // Set if the proxy is connected to a program
+ // providing the encryption of the point to
+ // point communication.
+ //
+
+ int LinkEncrypted;
+
+ //
+ // Maximum number of bytes sent for each token.
+ //
+
+ int TokenSize;
+
+ //
+ // Maximum number of tokens that can be spent
+ // by the client proxy before having to block
+ // waiting for a reply.
+ //
+
+ int TokenLimit;
+
+ //
+ // Bitmask used to determine the distribution
+ // of channel ids between the client and server
+ // proxies.
+ //
+
+ int ChannelMask;
+
+ //
+ // Kill session if control parameters cannot
+ // be negotiated before this timeout.
+ //
+
+ int InitTimeout;
+
+ //
+ // Enter the congestion state if the remote does
+ // not reply to the ping within the given amount
+ // of time.
+ //
+
+ int PingTimeout;
+
+ //
+ // Enqueue motion notify events in server channel.
+ //
+
+ int MotionTimeout;
+
+ //
+ // Force an update of the congestion counter if
+ // the proxy is idle for this time.
+ //
+
+ int IdleTimeout;
+
+ //
+ // Close the connection if can't write before
+ // this timeout.
+ //
+
+ int ChannelTimeout;
+
+ //
+ // Close connection if can't write before
+ // this timeout.
+ //
+
+ int ProxyTimeout;
+
+ //
+ // How many milliseconds to wait for the shared
+ // memory completion event to become available.
+ //
+
+ int ShmemTimeout;
+
+ //
+ // Wait for applications to complete at the time
+ // proxy is shut down.
+ //
+
+ int CleanupTimeout;
+
+ //
+ // Wait this amount of milliseconds before any
+ // iteration of the house-keeping process.
+ //
+
+ int KeeperTimeout;
+
+ //
+ // Adjust timeout calculations.
+ //
+
+ int LatencyTimeout;
+
+ //
+ // Maximum allowed size of log files.
+ //
+
+ int FileSizeLimit;
+ int FileSizeCheckTimeout;
+
+ //
+ // What do we do at the end of session? If
+ // this flag is set we launch a new client
+ // letting the user run a new NX session.
+ //
+
+ int EnableRestartOnShutdown;
+
+ //
+ // The client can request the proxy to kill
+ // a number of processes before exiting.
+ //
+
+ int *KillDaemonOnShutdown;
+ int KillDaemonOnShutdownNumber;
+ int KillDaemonOnShutdownLimit;
+
+ //
+ // Do we generate a core dump and exit in
+ // case of program errors?
+ //
+
+ int EnableCoreDumpOnAbort;
+
+ //
+ // Is statistic output enabled?
+ //
+
+ int EnableStatistics;
+
+ //
+ // Version number of local and remote proxy.
+ //
+
+ /*
+ * LocalVersionMaintenancePatch, RemoteVersionMaintenancePatch
+ * CompatVersionMaintenancePatch
+ *
+ * currently not used, for future compatibility checks
+ */
+ int LocalVersionMajor;
+ int LocalVersionMinor;
+ int LocalVersionPatch;
+ int LocalVersionMaintenancePatch;
+
+ int RemoteVersionMajor;
+ int RemoteVersionMinor;
+ int RemoteVersionPatch;
+ int RemoteVersionMaintenancePatch;
+
+ int CompatVersionMajor;
+ int CompatVersionMinor;
+ int CompatVersionPatch;
+ int CompatVersionMaintenancePatch;
+
+ //
+ // Compatibility version for the proxy
+ //
+
+ static const char* const NXPROXY_COMPATIBILITY_VERSION;
+
+ //
+ // Which unpack methods are implemented in proxy?
+ //
+
+ unsigned char *LocalUnpackMethods;
+ unsigned char *RemoteUnpackMethods;
+
+ //
+ // Memory restriction imposed by user.
+ //
+
+ int LocalMemoryLevel;
+
+ //
+ // Use or not differential compression
+ // and caching of X protocol messages.
+ //
+
+ int LocalDeltaCompression;
+ int RemoteDeltaCompression;
+
+ //
+ // Compression of images and replies.
+ //
+
+ int LocalDataCompression;
+ int LocalDataCompressionLevel;
+
+ int RemoteDataCompression;
+ int RemoteDataCompressionLevel;
+
+ //
+ // Minimum packet size to be compressed.
+ //
+
+ int LocalDataCompressionThreshold;
+
+ //
+ // Compress or not data flowing through the proxy
+ // link. Level should be one of the ZLIB level as
+ // Z_DEFAULT_COMPRESSION or Z_BEST_COMPRESSION.
+ //
+
+ int LocalStreamCompression;
+ int LocalStreamCompressionLevel;
+
+ int RemoteStreamCompression;
+ int RemoteStreamCompressionLevel;
+
+ //
+ // Size of read operations in read buffer classes.
+ //
+
+ int ClientInitialReadSize;
+ int ClientMaximumBufferSize;
+
+ int ServerInitialReadSize;
+ int ServerMaximumBufferSize;
+
+ int ProxyInitialReadSize;
+ int ProxyMaximumBufferSize;
+
+ int GenericInitialReadSize;
+ int GenericMaximumBufferSize;
+
+ //
+ // Set initial size and resize policy of
+ // transport buffers. If maximum size is
+ // exceeded, print a warning.
+ //
+
+ int TransportXBufferSize;
+ int TransportProxyBufferSize;
+ int TransportGenericBufferSize;
+
+ int TransportXBufferThreshold;
+ int TransportProxyBufferThreshold;
+ int TransportGenericBufferThreshold;
+
+ int TransportMaximumBufferSize;
+
+ //
+ // Flush the data produced for the channel
+ // connection if it exceeds this size.
+ //
+
+ int TransportFlushBufferSize;
+
+ //
+ // Socket options.
+ //
+
+ int OptionProxyKeepAlive;
+ int OptionProxyLowDelay;
+ int OptionProxyClientNoDelay;
+ int OptionProxyServerNoDelay;
+ int OptionClientNoDelay;
+ int OptionServerNoDelay;
+
+ int OptionProxyReceiveBuffer;
+ int OptionClientReceiveBuffer;
+ int OptionServerReceiveBuffer;
+
+ int OptionProxySendBuffer;
+ int OptionClientSendBuffer;
+ int OptionServerSendBuffer;
+
+ int OptionProxyRetryAccept;
+ int OptionProxyRetryConnect;
+ int OptionServerRetryConnect;
+
+ //
+ // Calculate current bitrate on proxy link
+ // using these observation periods. Value
+ // is in milliseconds.
+ //
+
+ int ShortBitrateTimeFrame;
+ int LongBitrateTimeFrame;
+
+ //
+ // Limit the bandwidth usage of the proxy
+ // link.
+ //
+
+ int LocalBitrateLimit;
+
+ int ClientBitrateLimit;
+ int ServerBitrateLimit;
+
+ //
+ // This is the limit imposed by user on
+ // total cache size.
+ //
+
+ int ClientTotalStorageSize;
+ int ServerTotalStorageSize;
+
+ int LocalTotalStorageSize;
+ int RemoteTotalStorageSize;
+
+ //
+ // Discard messages in store older than
+ // this amount of seconds.
+ //
+
+ int StoreTimeLimit;
+
+ //
+ // Any new message in store starts with
+ // this amount of hits.
+ //
+
+ int StoreHitsAddBonus;
+
+ //
+ // Unless it is loaded from persistent
+ // cache.
+ //
+
+ int StoreHitsLoadBonus;
+
+ //
+ // Stop increasing hits at this threshold.
+ //
+
+ int StoreHitsLimit;
+
+ //
+ // Give a special weight to messages put or
+ // taken from cache during startup time.
+ //
+
+ int StoreHitsStartup;
+
+ //
+ // Weight of touch and untoch operations.
+ //
+
+ int StoreHitsTouch;
+ int StoreHitsUntouch;
+
+ //
+ // Directives on size of messages to cache.
+ //
+
+ int MinimumMessageSize;
+ int MaximumMessageSize;
+
+ //
+ // Maximum size of a single X request.
+ //
+
+ int MaximumRequestSize;
+
+ //
+ // Currently selected streaming mode.
+ //
+
+ int SplitMode;
+
+ //
+ // Send new split data any given amount of
+ // milliseconds.
+ //
+
+ int SplitTimeout;
+
+ //
+ // Maximum number of distinct messages and
+ // maximum size in bytes of the temporary
+ // storage.
+ //
+
+ int SplitTotalSize;
+ int SplitTotalStorageSize;
+
+ //
+ // Don't split messages smaller that this
+ // threshold and send no more than the
+ // given amount of bytes in a single data
+ // shot when streaming the split messages.
+ //
+
+ int SplitDataThreshold;
+ int SplitDataPacketLimit;
+
+ //
+ // Agent related parameters. These values apply
+ // to the agent which, at startup, must query
+ // the user's settings.
+ //
+
+ int PackMethod;
+ int PackQuality;
+ int HideRender;
+ int TaintReplies;
+ int TaintThreshold;
+
+ //
+ // Do we allow shared memory image support in
+ // client and or server?
+ //
+
+ int ShmemClient;
+ int ShmemServer;
+
+ //
+ // Default size of shared memory segments used
+ // in MIT-SHM support.
+ //
+
+ int ShmemClientSize;
+ int ShmemServerSize;
+
+ //
+ // The user's home directory.
+ //
+
+ char *HomePath;
+
+ //
+ // The ".nx" directory, usually in
+ // the user's home.
+ //
+
+ char *RootPath;
+
+ //
+ // Usually the /usr/NX" directory.
+ //
+
+ char *SystemPath;
+
+ //
+ // Usually the "/tmp" directory.
+ //
+
+ char *TempPath;
+
+ //
+ // The complete path to the client.
+ //
+
+ char *ClientPath;
+
+ //
+ // String containing path of cache
+ // file selected for load or save.
+ //
+
+ char *PersistentCachePath;
+
+ //
+ // Name of selected cache file.
+ //
+
+ char *PersistentCacheName;
+
+ //
+ // Minimum size of cache in memory
+ // to proceed to its storage on disk.
+ //
+
+ int PersistentCacheThreshold;
+
+ //
+ // Is persistent cache enabled?
+ //
+
+ int PersistentCacheEnableLoad;
+ int PersistentCacheEnableSave;
+
+ //
+ // This is used just for test because
+ // it requires that client and server
+ // reside on the same machine.
+ //
+
+ int PersistentCacheCheckOnShutdown;
+
+ //
+ // Load packed image and render extension
+ // message stores. This currently depends
+ // on the type of session.
+ //
+
+ int PersistentCacheLoadPacked;
+ int PersistentCacheLoadRender;
+
+ //
+ // Maximum disk consumption of message
+ // caches on disk.
+ //
+
+ int PersistentCacheDiskLimit;
+
+ //
+ // String containing the base path
+ // of image cache files.
+ //
+
+ char *ImageCachePath;
+
+ //
+ // Is image cache enabled?
+ //
+
+ int ImageCacheEnableLoad;
+ int ImageCacheEnableSave;
+
+ //
+ // Maximum disk consumption of image
+ // caches on disk.
+ //
+
+ int ImageCacheDiskLimit;
+
+ //
+ // Only constructor, destructor
+ // and a few utility functions.
+ //
+
+ Control();
+
+ ~Control();
+
+ //
+ // Should not leverage control to find channel
+ // stores' size limits. As most of values in
+ // control, this info must be moved elsewhere.
+ //
+
+ int getUpperStorageSize() const
+ {
+ return (ClientTotalStorageSize >
+ ServerTotalStorageSize ?
+ ClientTotalStorageSize :
+ ServerTotalStorageSize);
+ }
+
+ int getLowerStorageSize() const
+ {
+ return (ClientTotalStorageSize <
+ ServerTotalStorageSize ?
+ ClientTotalStorageSize :
+ ServerTotalStorageSize);
+ }
+
+ void setProtoStep(int step);
+
+ int getProtoStep();
+
+ private:
+
+ //
+ // Look in Control.cpp.
+ //
+
+ void setLocalUnpackMethods();
+
+ //
+ // Manage the encoding according
+ // to the protocol version.
+ //
+
+ int protoStep_;
+
+ //
+ // Min and max values allowed for protocol step
+ // depending on protocol version compatibility
+ //
+
+ static const int NX_MIN_PROTO_STEP;
+ static const int NX_MAX_PROTO_STEP;
+
+ //
+ // Check the validity of protocol step
+ //
+
+ bool isValidProtoStep(int step)
+ {
+ return ((step >= NX_MIN_PROTO_STEP) && (step <= NX_MAX_PROTO_STEP));
+ }
+
+};
+
+#endif /* Control_H */
diff --git a/nxcomp/src/CopyArea.cpp b/nxcomp/src/CopyArea.cpp
new file mode 100644
index 000000000..c2a19c2df
--- /dev/null
+++ b/nxcomp/src/CopyArea.cpp
@@ -0,0 +1,199 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "CopyArea.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+int CopyAreaStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CopyAreaMessage *copyArea = (CopyAreaMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ copyArea -> src_drawable = GetULONG(buffer + 4, bigEndian);
+ copyArea -> dst_drawable = GetULONG(buffer + 8, bigEndian);
+ copyArea -> gcontext = GetULONG(buffer + 12, bigEndian);
+
+ copyArea -> src_x = GetUINT(buffer + 16, bigEndian);
+ copyArea -> src_y = GetUINT(buffer + 18, bigEndian);
+ copyArea -> dst_x = GetUINT(buffer + 20, bigEndian);
+ copyArea -> dst_y = GetUINT(buffer + 22, bigEndian);
+
+ copyArea -> width = GetUINT(buffer + 24, bigEndian);
+ copyArea -> height = GetUINT(buffer + 26, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CopyAreaStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CopyAreaMessage *copyArea = (CopyAreaMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(copyArea -> src_drawable, buffer + 4, bigEndian);
+ PutULONG(copyArea -> dst_drawable, buffer + 8, bigEndian);
+ PutULONG(copyArea -> gcontext, buffer + 12, bigEndian);
+
+ PutUINT(copyArea -> src_x, buffer + 16, bigEndian);
+ PutUINT(copyArea -> src_y, buffer + 18, bigEndian);
+ PutUINT(copyArea -> dst_x, buffer + 20, bigEndian);
+ PutUINT(copyArea -> dst_y, buffer + 22, bigEndian);
+
+ PutUINT(copyArea -> width, buffer + 24, bigEndian);
+ PutUINT(copyArea -> height, buffer + 26, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void CopyAreaStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ CopyAreaMessage *copyArea = (CopyAreaMessage *) message;
+
+ *logofs << name() << ": Identity src_drawable " << copyArea -> src_drawable
+ << ", dst_drawable " << copyArea -> dst_drawable << ", gcontext " << copyArea -> gcontext
+ << ", src_x " << copyArea -> src_x << ", src_y " << copyArea -> src_y
+ << ", dst_x " << copyArea -> dst_x << ", dst_y " << copyArea -> dst_y
+ << ", width " << copyArea -> width << ", height " << copyArea -> height
+ << ", size " << copyArea -> size_ << ".\n";
+
+ #endif
+}
+
+void CopyAreaStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 16, 12);
+}
+
+void CopyAreaStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ CopyAreaMessage *copyArea = (CopyAreaMessage *) message;
+ CopyAreaMessage *cachedCopyArea = (CopyAreaMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << copyArea -> src_drawable
+ << " as " << "src_drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(copyArea -> src_drawable, clientCache -> drawableCache);
+
+ cachedCopyArea -> src_drawable = copyArea -> src_drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << copyArea -> dst_drawable
+ << " as " << "dst_drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(copyArea -> dst_drawable, clientCache -> drawableCache);
+
+ cachedCopyArea -> dst_drawable = copyArea -> dst_drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << copyArea -> gcontext
+ << " as " << "gcontext" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(copyArea -> gcontext, clientCache -> gcCache);
+
+ cachedCopyArea -> gcontext = copyArea -> gcontext;
+}
+
+void CopyAreaStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ CopyAreaMessage *copyArea = (CopyAreaMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ copyArea -> src_drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << copyArea -> src_drawable
+ << " as " << "src_drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ copyArea -> dst_drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << copyArea -> dst_drawable
+ << " as " << "dst_drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ copyArea -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << copyArea -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/src/CopyArea.h b/nxcomp/src/CopyArea.h
new file mode 100644
index 000000000..6b2617875
--- /dev/null
+++ b/nxcomp/src/CopyArea.h
@@ -0,0 +1,192 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef CopyArea_H
+#define CopyArea_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define COPYAREA_ENABLE_CACHE 1
+#define COPYAREA_ENABLE_DATA 0
+#define COPYAREA_ENABLE_SPLIT 0
+#define COPYAREA_ENABLE_COMPRESS 0
+
+#define COPYAREA_DATA_LIMIT 0
+#define COPYAREA_DATA_OFFSET 28
+
+#define COPYAREA_CACHE_SLOTS 3000
+#define COPYAREA_CACHE_THRESHOLD 5
+#define COPYAREA_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class CopyAreaMessage : public Message
+{
+ friend class CopyAreaStore;
+
+ public:
+
+ CopyAreaMessage()
+ {
+ }
+
+ ~CopyAreaMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int src_drawable;
+ unsigned int dst_drawable;
+ unsigned int gcontext;
+ unsigned short src_x;
+ unsigned short src_y;
+ unsigned short dst_x;
+ unsigned short dst_y;
+ unsigned short width;
+ unsigned short height;
+};
+
+class CopyAreaStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ CopyAreaStore() : MessageStore()
+ {
+ enableCache = COPYAREA_ENABLE_CACHE;
+ enableData = COPYAREA_ENABLE_DATA;
+ enableSplit = COPYAREA_ENABLE_SPLIT;
+ enableCompress = COPYAREA_ENABLE_COMPRESS;
+
+ dataLimit = COPYAREA_DATA_LIMIT;
+ dataOffset = COPYAREA_DATA_OFFSET;
+
+ cacheSlots = COPYAREA_CACHE_SLOTS;
+ cacheThreshold = COPYAREA_CACHE_THRESHOLD;
+ cacheLowerThreshold = COPYAREA_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~CopyAreaStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "CopyArea";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_CopyArea;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(CopyAreaMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new CopyAreaMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new CopyAreaMessage((const CopyAreaMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (CopyAreaMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* CopyArea_H */
diff --git a/nxcomp/src/CreateGC.cpp b/nxcomp/src/CreateGC.cpp
new file mode 100644
index 000000000..34978de13
--- /dev/null
+++ b/nxcomp/src/CreateGC.cpp
@@ -0,0 +1,194 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "CreateGC.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int CreateGCStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CreateGCMessage *createGC = (CreateGCMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ createGC -> gcontext = GetULONG(buffer + 4, bigEndian);
+ createGC -> drawable = GetULONG(buffer + 8, bigEndian);
+ createGC -> value_mask = GetULONG(buffer + 12, bigEndian);
+
+ //
+ // Clear the unused bytes carried in the
+ // payload to increase the effectiveness
+ // of the caching algorithm.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Removing unused bytes from the "
+ << "data payload.\n" << logofs_flush;
+ #endif
+
+ createGC -> value_mask &= (1 << 23) - 1;
+
+ unsigned int mask = 0x1;
+ unsigned char *source = (unsigned char *) buffer + CREATEGC_DATA_OFFSET;
+ unsigned long value = 0;
+
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (createGC -> value_mask & mask)
+ {
+ value = GetULONG(source, bigEndian);
+
+ value &= (0xffffffff >> (32 - CREATEGC_FIELD_WIDTH[i]));
+
+ PutULONG(value, source, bigEndian);
+
+ source += 4;
+ }
+
+ mask <<= 1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreateGCStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CreateGCMessage *createGC = (CreateGCMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(createGC -> gcontext, buffer + 4, bigEndian);
+ PutULONG(createGC -> drawable, buffer + 8, bigEndian);
+ PutULONG(createGC -> value_mask, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void CreateGCStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ CreateGCMessage *createGC = (CreateGCMessage *) message;
+
+ *logofs << name() << ": Identity gcontext " << createGC -> gcontext << ", drawable "
+ << createGC -> drawable << ", value_mask " << createGC -> value_mask
+ << ", size " << createGC -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void CreateGCStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // This didn't include the drawable
+ // in previous versions.
+ //
+
+ md5_append(md5_state_, buffer + 8, 8);
+}
+
+void CreateGCStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ CreateGCMessage *createGC = (CreateGCMessage *) message;
+ CreateGCMessage *cachedCreateGC = (CreateGCMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ // Since ProtoStep7 (#issue 108)
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << createGC -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeNewXidValue(createGC -> gcontext, clientCache -> lastId,
+ clientCache -> lastIdCache, clientCache -> gcCache,
+ clientCache -> freeGCCache);
+
+ cachedCreateGC -> gcontext = createGC -> gcontext;
+}
+
+void CreateGCStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ CreateGCMessage *createGC = (CreateGCMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeNewXidValue(value, clientCache -> lastId,
+ clientCache -> lastIdCache, clientCache -> gcCache,
+ clientCache -> freeGCCache);
+
+ createGC -> gcontext = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << createGC -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/CreateGC.h b/nxcomp/src/CreateGC.h
new file mode 100644
index 000000000..03e27d685
--- /dev/null
+++ b/nxcomp/src/CreateGC.h
@@ -0,0 +1,186 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef CreateGC_H
+#define CreateGC_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CREATEGC_ENABLE_CACHE 1
+#define CREATEGC_ENABLE_DATA 0
+#define CREATEGC_ENABLE_SPLIT 0
+#define CREATEGC_ENABLE_COMPRESS 0
+
+#define CREATEGC_DATA_LIMIT 144
+#define CREATEGC_DATA_OFFSET 16
+
+#define CREATEGC_CACHE_SLOTS 2000
+#define CREATEGC_CACHE_THRESHOLD 2
+#define CREATEGC_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class CreateGCMessage : public Message
+{
+ friend class CreateGCStore;
+
+ public:
+
+ CreateGCMessage()
+ {
+ }
+
+ ~CreateGCMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int gcontext;
+ unsigned int drawable;
+ unsigned int value_mask;
+};
+
+class CreateGCStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ CreateGCStore() : MessageStore()
+ {
+ enableCache = CREATEGC_ENABLE_CACHE;
+ enableData = CREATEGC_ENABLE_DATA;
+ enableSplit = CREATEGC_ENABLE_SPLIT;
+ enableCompress = CREATEGC_ENABLE_COMPRESS;
+
+ dataLimit = CREATEGC_DATA_LIMIT;
+ dataOffset = CREATEGC_DATA_OFFSET;
+
+ cacheSlots = CREATEGC_CACHE_SLOTS;
+ cacheThreshold = CREATEGC_CACHE_THRESHOLD;
+ cacheLowerThreshold = CREATEGC_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~CreateGCStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "CreateGC";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_CreateGC;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(CreateGCMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new CreateGCMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new CreateGCMessage((const CreateGCMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (CreateGCMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* CreateGC_H */
diff --git a/nxcomp/src/CreatePixmap.cpp b/nxcomp/src/CreatePixmap.cpp
new file mode 100644
index 000000000..a60134cd5
--- /dev/null
+++ b/nxcomp/src/CreatePixmap.cpp
@@ -0,0 +1,280 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "CreatePixmap.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Constructors and destructors.
+//
+
+CreatePixmapStore::CreatePixmapStore()
+
+ : MessageStore()
+{
+ enableCache = CREATEPIXMAP_ENABLE_CACHE;
+ enableData = CREATEPIXMAP_ENABLE_DATA;
+ enableSplit = CREATEPIXMAP_ENABLE_SPLIT;
+ enableCompress = CREATEPIXMAP_ENABLE_COMPRESS;
+
+ dataLimit = CREATEPIXMAP_DATA_LIMIT;
+ dataOffset = CREATEPIXMAP_DATA_OFFSET;
+
+ cacheSlots = CREATEPIXMAP_CACHE_SLOTS;
+ cacheThreshold = CREATEPIXMAP_CACHE_THRESHOLD;
+ cacheLowerThreshold = CREATEPIXMAP_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+CreatePixmapStore::~CreatePixmapStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int CreatePixmapStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> depthCache);
+
+ encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> drawableCache,
+ clientCache -> freeDrawableCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> windowCache);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 12, bigEndian), 16,
+ clientCache -> createPixmapXCache, 8);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 14, bigEndian), 16,
+ clientCache -> createPixmapYCache, 8);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreatePixmapStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned char cValue;
+ unsigned int value;
+
+ size = 16;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+
+ *(buffer + 1) = cValue;
+
+ decodeBuffer.decodeNewXidValue(value,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> drawableCache,
+ clientCache -> freeDrawableCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> windowCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> createPixmapXCache, 8);
+
+ PutUINT(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> createPixmapYCache, 8);
+
+ PutUINT(value, buffer + 14, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreatePixmapStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message;
+
+ createPixmap -> depth = *(buffer + 1);
+
+ createPixmap -> id = GetULONG(buffer + 4, bigEndian);
+ createPixmap -> drawable = GetULONG(buffer + 8, bigEndian);
+
+ createPixmap -> width = GetUINT(buffer + 12, bigEndian);
+ createPixmap -> height = GetUINT(buffer + 14, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int CreatePixmapStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message;
+
+ *(buffer + 1) = createPixmap -> depth;
+
+ PutULONG(createPixmap -> id, buffer + 4, bigEndian);
+ PutULONG(createPixmap -> drawable, buffer + 8, bigEndian);
+
+ PutUINT(createPixmap -> width, buffer + 12, bigEndian);
+ PutUINT(createPixmap -> height, buffer + 14, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void CreatePixmapStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Dump of identity not implemented.\n"
+ << logofs_flush;
+ #endif
+
+ #endif
+}
+
+void CreatePixmapStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 8, 8);
+}
+
+void CreatePixmapStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message;
+ CreatePixmapMessage *cachedCreatePixmap = (CreatePixmapMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeNewXidValue(createPixmap -> id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> drawableCache,
+ clientCache -> freeDrawableCache);
+
+ cachedCreatePixmap -> id = createPixmap -> id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+void CreatePixmapStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ CreatePixmapMessage *createPixmap = (CreatePixmapMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeNewXidValue(createPixmap -> id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> drawableCache,
+ clientCache -> freeDrawableCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Size is "
+ << createPixmap -> size_ << " identity is "
+ << createPixmap -> i_size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/CreatePixmap.h b/nxcomp/src/CreatePixmap.h
new file mode 100644
index 000000000..0a3212dd9
--- /dev/null
+++ b/nxcomp/src/CreatePixmap.h
@@ -0,0 +1,162 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef CreatePixmap_H
+#define CreatePixmap_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define CREATEPIXMAP_ENABLE_CACHE 1
+#define CREATEPIXMAP_ENABLE_DATA 0
+#define CREATEPIXMAP_ENABLE_SPLIT 0
+#define CREATEPIXMAP_ENABLE_COMPRESS 0
+
+#define CREATEPIXMAP_DATA_LIMIT 16
+#define CREATEPIXMAP_DATA_OFFSET 16
+
+#define CREATEPIXMAP_CACHE_SLOTS 1000
+#define CREATEPIXMAP_CACHE_THRESHOLD 2
+#define CREATEPIXMAP_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class CreatePixmapMessage : public Message
+{
+ friend class CreatePixmapStore;
+
+ public:
+
+ CreatePixmapMessage()
+ {
+ }
+
+ ~CreatePixmapMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char depth;
+
+ unsigned int id;
+ unsigned int drawable;
+
+ unsigned short width;
+ unsigned short height;
+};
+
+class CreatePixmapStore : public MessageStore
+{
+ public:
+
+ CreatePixmapStore();
+
+ virtual ~CreatePixmapStore();
+
+ virtual const char *name() const
+ {
+ return "CreatePixmap";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_CreatePixmap;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(CreatePixmapMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new CreatePixmapMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new CreatePixmapMessage((const CreatePixmapMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (CreatePixmapMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* CreatePixmap_H */
diff --git a/nxcomp/src/DecodeBuffer.cpp b/nxcomp/src/DecodeBuffer.cpp
new file mode 100644
index 000000000..4c1530d9b
--- /dev/null
+++ b/nxcomp/src/DecodeBuffer.cpp
@@ -0,0 +1,639 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Misc.h"
+#include "Control.h"
+
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+DecodeBuffer::DecodeBuffer(const unsigned char *data, unsigned int length)
+
+ : buffer_(data), end_(buffer_ + length), nextSrc_(buffer_), srcMask_(0x80)
+{
+ // Since ProtoStep7 (#issue 108)
+ end_ = buffer_ + length - DECODE_BUFFER_POSTFIX_SIZE;
+}
+
+int DecodeBuffer::decodeValue(unsigned int &value, unsigned int numBits,
+ unsigned int blockSize, int endOkay)
+{
+ #ifdef DUMP
+ *logofs << "DecodeBuffer: Decoding " << numBits
+ << " bits value with block " << blockSize
+ << " and " << (nextSrc_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ unsigned int result = 0;
+ unsigned int destMask = 0x1;
+ unsigned int bitsRead = 0;
+
+ if (blockSize == 0)
+ blockSize = numBits;
+
+ unsigned char nextSrcChar = *nextSrc_;
+ unsigned int numBlocks = 1;
+
+ do
+ {
+ if (numBlocks == 4)
+ {
+ blockSize = numBits;
+ }
+
+ unsigned int bitsToRead = (blockSize > numBits - bitsRead ?
+ numBits - bitsRead : blockSize);
+ unsigned int count = 0;
+ unsigned char lastBit;
+
+ do
+ {
+ if (nextSrc_ >= end_)
+ {
+ if (!endOkay)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [A] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Label "context" is just used to identify
+ // the routine which detected the problem in
+ // present source file.
+ //
+
+ cerr << "Error" << ": Failure decoding data in context [A].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [B] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [B].\n";
+
+ HandleAbort();
+ }
+
+ lastBit = (nextSrcChar & srcMask_);
+
+ if (lastBit)
+ result |= destMask;
+
+ srcMask_ >>= 1;
+
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ nextSrcChar = *nextSrc_;
+ }
+
+ destMask <<= 1;
+ }
+ while (bitsToRead > ++count);
+
+ bitsRead += bitsToRead;
+
+ if (bitsRead < numBits)
+ {
+ if (nextSrc_ >= end_)
+ {
+ if (!endOkay)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [C] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [C].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [D] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [D].\n";
+
+ HandleAbort();
+ }
+
+ unsigned char moreData = (nextSrcChar & srcMask_);
+
+ srcMask_ >>= 1;
+
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ nextSrcChar = *nextSrc_;
+ }
+
+ if (!moreData)
+ {
+ if (lastBit)
+ {
+ do
+ {
+ result |= destMask;
+ destMask <<= 1;
+ }
+ while (numBits > ++bitsRead);
+ }
+ else
+ bitsRead = numBits;
+ }
+ }
+
+ blockSize >>= 1;
+
+ if (blockSize < 2)
+ blockSize = 2;
+
+ numBlocks++;
+ }
+ while (numBits > bitsRead);
+
+ value = result;
+
+ return 1;
+}
+
+int DecodeBuffer::decodeCachedValue(unsigned int &value, unsigned int numBits,
+ IntCache &cache, unsigned int blockSize,
+ int endOkay)
+{
+ #ifdef DUMP
+ *logofs << "DecodeBuffer: Decoding " << numBits
+ << " bits cached value with block " << blockSize
+ << " and " << (nextSrc_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ if (nextSrc_ >= end_)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [E] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [E].\n";
+
+ HandleAbort();
+ }
+
+ unsigned int index = 0;
+ unsigned char nextSrcChar = *nextSrc_;
+
+ while (!(nextSrcChar & srcMask_))
+ {
+ index++;
+ srcMask_ >>= 1;
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ if (nextSrc_ >= end_)
+ {
+ if (!endOkay)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [F] "
+ << "in decodeCachedValue() nextSrc_ = "
+ << (nextSrc_ - buffer_) << " end_ = "
+ << (end_ - buffer_) << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [F].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [G] "
+ << "in decodeValue() nextSrc_ = " << (nextSrc_ - buffer_)
+ << " end_ = " << (end_ - buffer_) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [G].\n";
+
+ HandleAbort();
+ }
+
+ nextSrcChar = *nextSrc_;
+ }
+ }
+
+ srcMask_ >>= 1;
+
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ }
+
+ if (index == 2)
+ {
+ // Since ProtoStep8 (#issue 108)
+ blockSize = cache.getBlockSize(blockSize);
+
+ if (decodeValue(value, numBits, blockSize, endOkay))
+ {
+ cache.insert(value, IntMask[numBits]);
+
+ return 1;
+ }
+
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [H] "
+ << "in decodeCacheValue() with no value found.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [H].\n";
+
+ HandleAbort();
+ }
+ else
+ {
+ if (index > 2)
+ {
+ index--;
+ }
+
+ if (index > cache.getSize())
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [I] "
+ << "in decodeCachedValue() index = " << index
+ << " cache size = " << cache.getSize() << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [I].\n";
+
+ HandleAbort();
+ }
+
+ value = cache.get(index);
+
+ return 1;
+ }
+}
+
+int DecodeBuffer::decodeCachedValue(unsigned char &value, unsigned int numBits,
+ CharCache &cache, unsigned int blockSize,
+ int endOkay)
+{
+ #ifdef DUMP
+ *logofs << "DecodeBuffer: Decoding " << numBits
+ << " bits char cached value with block " << blockSize
+ << " and " << nextSrc_ - buffer_ << " bytes read out of "
+ << end_ - buffer_ << ".\n" << logofs_flush;
+ #endif
+
+ if (nextSrc_ >= end_)
+ {
+ #ifdef TEST
+ *logofs << "DecodeBuffer: End of buffer reached in context [J] with "
+ << nextSrc_ - buffer_ << " bytes read out of "
+ << end_ - buffer_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ unsigned int index = 0;
+ unsigned char nextSrcChar = *nextSrc_;
+
+ while (!(nextSrcChar & srcMask_))
+ {
+ index++;
+ srcMask_ >>= 1;
+
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+
+ if (nextSrc_ >= end_)
+ {
+ if (!endOkay)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [K] "
+ << "in decodeCachedValue() nextSrc_ "
+ << (nextSrc_ - buffer_) << " end_ " << (end_ - buffer_)
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [K].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef TEST
+ *logofs << "DecodeBuffer: End of buffer reached in context [L] with "
+ << nextSrc_ - buffer_ << " bytes read out of "
+ << end_ - buffer_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ nextSrcChar = *nextSrc_;
+ }
+ }
+
+ srcMask_ >>= 1;
+
+ if (srcMask_ == 0)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ }
+
+ if (index == 2)
+ {
+ unsigned int temp;
+
+ if (decodeValue(temp, numBits, blockSize, endOkay))
+ {
+ value = (unsigned char) temp;
+
+ cache.insert(value);
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [M] "
+ << "in decodeValue() with index = 2.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [M].\n";
+
+ HandleAbort();
+ }
+ }
+ else
+ {
+ if (index > 2)
+ {
+ index--;
+ }
+
+ if (index > cache.getSize())
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [N] "
+ << "in decodeCachedValue() " << "index = " << index
+ << " cache size = " << cache.getSize() << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [N].\n";
+
+ HandleAbort();
+ }
+
+ value = cache.get(index);
+ }
+
+ return 1;
+}
+
+//
+// Simply returns a pointer to the correct spot in
+// the internal buffer. If the caller needs this
+// data to last beyond the lifetime of the internal
+// buffer, it must copy the data in its own memory.
+//
+
+const unsigned char *DecodeBuffer::decodeMemory(unsigned int numBytes)
+{
+ #ifdef DUMP
+ *logofs << "DecodeBuffer: Decoding " << numBytes
+ << " bytes of memory with " << (nextSrc_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ const unsigned char *result;
+
+ //
+ // Force ourselves to a byte boundary.
+ // Is up to application to ensure data
+ // is word alligned when needed.
+ //
+
+ if (srcMask_ != 0x80)
+ {
+ srcMask_ = 0x80;
+ nextSrc_++;
+ }
+
+ result = nextSrc_;
+
+ if (numBytes > DECODE_BUFFER_OVERFLOW_SIZE)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Can't decode a buffer of "
+ << numBytes << " bytes with limit set to "
+ << DECODE_BUFFER_OVERFLOW_SIZE << ".\n"
+ << logofs_flush;
+
+ *logofs << "DecodeBuffer: PANIC! Assuming failure decoding "
+ << "data in context [O].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Should never decode buffer of size "
+ << "greater than " << DECODE_BUFFER_OVERFLOW_SIZE
+ << " bytes.\n";
+
+ cerr << "Error" << ": Assuming failure decoding data in "
+ << "context [O].\n";
+
+ HandleAbort();
+ }
+ else if (end_ - nextSrc_ < (int) numBytes)
+ {
+ #ifdef PANIC
+ *logofs << "DecodeBuffer: PANIC! Assertion failed. Error [P] "
+ << "in decodeMemory() " << "with length " << numBytes
+ << " and " << (end_ - nextSrc_)
+ << " bytes remaining.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decoding data in context [P].\n";
+
+ HandleAbort();
+ }
+
+ nextSrc_ += numBytes;
+
+ return result;
+}
+
+void DecodeBuffer::decodeActionValue(unsigned char &value, unsigned short &position,
+ ActionCache &cache)
+{
+ unsigned int t;
+
+ decodeCachedValue(t, 15, *(cache.base_[cache.slot_]));
+
+ cache.last_ += t;
+ cache.last_ &= 0x7fff;
+
+ value = cache.last_ >> 13;
+
+ position = cache.last_ & 0x1fff;
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Decoded value "
+ << (unsigned) value << " and position "
+ << position << " with base " << cache.slot_
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Action block prediction is "
+ << (*(cache.base_[cache.slot_])).getBlockSize(15)
+ << ".\n" << logofs_flush;
+ #endif
+
+ cache.slot_ = (cache.last_ & 0xff);
+}
+
+void DecodeBuffer::decodeNewXidValue(unsigned int &value, unsigned int &lastId,
+ IntCache &lastIdCache, IntCache &cache,
+ FreeCache &freeCache)
+{
+ decodeCachedValue(value, 29, lastIdCache);
+
+ lastId += (value + 1);
+ lastId &= 0x1fffffff;
+
+ value = lastId;
+
+ cache.push(value, 0x1fffffff);
+
+ freeCache.push(value, 0x1fffffff);
+}
+
+void DecodeBuffer::decodeNewXidValue(unsigned int &value, unsigned int &lastId,
+ IntCache &lastIdCache, XidCache &cache,
+ FreeCache &freeCache)
+{
+ decodeCachedValue(value, 29, lastIdCache);
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Decoded new Xid difference "
+ << value << ".\n" << logofs_flush;
+ #endif
+
+ lastId += (value + 1);
+ lastId &= 0x1fffffff;
+
+ value = lastId;
+
+ unsigned int t = (value - cache.last_);
+
+ cache.last_ = value;
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Decoded new Xid " << value
+ << " with base " << cache.slot_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cache.slot_ = (value & 0xff);
+
+ cache.base_[cache.slot_] -> push(t, 0x1fffffff);
+
+ freeCache.push(value, 0x1fffffff);
+}
+
+void DecodeBuffer::decodeXidValue(unsigned int &value, XidCache &cache)
+{
+ unsigned int t;
+
+ decodeCachedValue(t, 29, *(cache.base_[cache.slot_]));
+
+ cache.last_ += t;
+ cache.last_ &= 0x1fffffff;
+
+ value = cache.last_;
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Decoded Xid " << value
+ << " with base " << cache.slot_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cache.slot_ = (value & 0xff);
+
+ #ifdef DEBUG
+ *logofs << "DecodeBuffer: Xid block prediction is "
+ << (*(cache.base_[cache.slot_])).getBlockSize(29)
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+void DecodeBuffer::decodeFreeXidValue(unsigned int &value, FreeCache &cache)
+{
+ decodeCachedValue(value, 29, cache);
+}
+
diff --git a/nxcomp/src/DecodeBuffer.h b/nxcomp/src/DecodeBuffer.h
new file mode 100644
index 000000000..f5f84c54f
--- /dev/null
+++ b/nxcomp/src/DecodeBuffer.h
@@ -0,0 +1,138 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef DecodeBuffer_H
+#define DecodeBuffer_H
+
+#include <string.h>
+
+#include "IntCache.h"
+#include "CharCache.h"
+#include "XidCache.h"
+#include "FreeCache.h"
+#include "OpcodeCache.h"
+#include "ActionCache.h"
+
+#define DECODE_BUFFER_OVERFLOW_SIZE 4194304
+
+#define DECODE_BUFFER_POSTFIX_SIZE 1
+
+class DecodeBuffer
+{
+ public:
+
+ DecodeBuffer(const unsigned char *data, unsigned int length);
+
+ ~DecodeBuffer()
+ {
+ }
+
+ int decodeValue(unsigned int &value, unsigned int numBits,
+ unsigned int blockSize = 0, int endOkay = 0);
+
+ int decodeCachedValue(unsigned int &value, unsigned int numBits,
+ IntCache &cache, unsigned int blockSize = 0,
+ int endOkay = 0);
+
+ int decodeCachedValue(unsigned char &value, unsigned int numBits,
+ CharCache &cache, unsigned int blockSize = 0,
+ int endOkay = 0);
+
+ void decodeDiffCachedValue(unsigned int &value, unsigned int &previous,
+ unsigned int numBits, IntCache &cache,
+ unsigned int blockSize = 0)
+ {
+ decodeCachedValue(value, numBits, cache, blockSize);
+
+ previous += (value + 1);
+ previous &= (0xffffffff >> (32 - numBits));
+
+ value = previous;
+ }
+
+ void decodeBoolValue(unsigned int &value)
+ {
+ decodeValue(value, 1);
+ }
+
+ int decodeOpcodeValue(unsigned char &value, OpcodeCache &cache, int endOkay = 0)
+ {
+ int result = decodeCachedValue(value, 8, cache.base_[cache.slot_], 8, endOkay);
+
+ if (result == 1)
+ {
+ cache.slot_ = value;
+ }
+
+ return result;
+ }
+
+ void decodeActionValue(unsigned char &value, unsigned short &position,
+ ActionCache &cache);
+
+ void decodeNewXidValue(unsigned int &value, unsigned int &lastId,
+ IntCache &lastIdCache, IntCache &cache,
+ FreeCache &freeCache);
+
+ void decodeNewXidValue(unsigned int &value, unsigned int &lastId,
+ IntCache &lastIdCache, XidCache &cache,
+ FreeCache &freeCache);
+
+ void decodeXidValue(unsigned int &value, XidCache &cache);
+
+ void decodeFreeXidValue(unsigned int &value, FreeCache &cache);
+
+ void decodeTextData(unsigned char *buffer, unsigned int numBytes)
+ {
+ decodeMemory(buffer, numBytes);
+ }
+
+ void decodeIntData(unsigned char *buffer, unsigned int numBytes)
+ {
+ decodeMemory(buffer, numBytes);
+ }
+
+ void decodeLongData(unsigned char *buffer, unsigned int numBytes)
+ {
+ decodeMemory(buffer, numBytes);
+ }
+
+ const unsigned char *decodeMemory(unsigned int numBytes);
+
+ void decodeMemory(unsigned char *buffer, unsigned int numBytes)
+ {
+ memcpy(buffer, decodeMemory(numBytes), numBytes);
+ }
+
+ private:
+
+ const unsigned char *buffer_;
+ const unsigned char *end_;
+ const unsigned char *nextSrc_;
+
+ unsigned char srcMask_;
+};
+
+#endif /* DecodeBuffer_H */
diff --git a/nxcomp/src/EncodeBuffer.cpp b/nxcomp/src/EncodeBuffer.cpp
new file mode 100644
index 000000000..e112113a8
--- /dev/null
+++ b/nxcomp/src/EncodeBuffer.cpp
@@ -0,0 +1,623 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Misc.h"
+#include "Control.h"
+
+#include "EncodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+#define ADVANCE_DEST \
+\
+if (destShift_ == 0) \
+{ \
+ destShift_ = 7; nextDest_++; *nextDest_ = 0; \
+} \
+else \
+{ \
+ destShift_--; \
+}
+
+EncodeBuffer::EncodeBuffer()
+{
+ size_ = ENCODE_BUFFER_DEFAULT_SIZE;
+
+ buffer_ = new unsigned char[size_ + ENCODE_BUFFER_PREFIX_SIZE +
+ ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE;
+ end_ = buffer_ + size_;
+
+ nextDest_ = buffer_;
+ *nextDest_ = 0;
+ destShift_ = 7;
+
+ lastBits_ = 0;
+
+ initialSize_ = ENCODE_BUFFER_DEFAULT_SIZE;
+ thresholdSize_ = ENCODE_BUFFER_DEFAULT_SIZE << 1;
+ maximumSize_ = ENCODE_BUFFER_DEFAULT_SIZE << 4;
+}
+
+EncodeBuffer::~EncodeBuffer()
+{
+ delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE);
+}
+
+void EncodeBuffer::setSize(unsigned int initialSize, unsigned int thresholdSize,
+ unsigned int maximumSize)
+{
+ initialSize_ = initialSize;
+ thresholdSize_ = thresholdSize;
+ maximumSize_ = maximumSize;
+
+ #ifdef TEST
+ *logofs << "EncodeBuffer: Set buffer sizes to "
+ << initialSize_ << "/" << thresholdSize_
+ << "/" << maximumSize_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+void EncodeBuffer::fullReset()
+{
+ if (size_ > initialSize_)
+ {
+ delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE);
+
+ size_ = initialSize_;
+
+ buffer_ = new unsigned char[size_ + ENCODE_BUFFER_PREFIX_SIZE +
+ ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE;
+ }
+
+ end_ = buffer_ + size_;
+
+ nextDest_ = buffer_;
+ *nextDest_ = 0;
+ destShift_ = 7;
+
+ lastBits_ = 0;
+}
+
+void EncodeBuffer::encodeValue(unsigned int value, unsigned int numBits,
+ unsigned int blockSize)
+{
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoding " << numBits
+ << " bits value with block " << blockSize
+ << " and " << (nextDest_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ value &= IntMask[numBits];
+
+ unsigned int srcMask = 0x1;
+ unsigned int bitsWritten = 0;
+
+ if (blockSize == 0)
+ blockSize = numBits;
+
+ if (end_ - nextDest_ < 8)
+ {
+ growBuffer();
+ }
+
+ unsigned int numBlocks = 1;
+
+ do
+ {
+ if (numBlocks == 4)
+ blockSize = numBits;
+
+ unsigned int bitsToWrite = (blockSize > numBits - bitsWritten ?
+ numBits - bitsWritten : blockSize);
+ unsigned int count = 0;
+ unsigned int lastBit;
+
+ do
+ {
+ lastBit = (value & srcMask);
+ if (lastBit)
+ *nextDest_ |= (1 << destShift_);
+ ADVANCE_DEST;
+ srcMask <<= 1;
+ }
+ while (bitsToWrite > ++count);
+
+ bitsWritten += bitsToWrite;
+
+ if (bitsWritten < numBits)
+ {
+ unsigned int tmpMask = srcMask;
+ unsigned int i = bitsWritten;
+
+ if (lastBit)
+ {
+ do
+ {
+ unsigned int nextBit = (value & tmpMask);
+
+ if (!nextBit)
+ break;
+
+ tmpMask <<= 1;
+ }
+ while (numBits > ++i);
+ }
+ else
+ {
+ do
+ {
+ unsigned int nextBit = (value & tmpMask);
+
+ if (nextBit)
+ break;
+
+ tmpMask <<= 1;
+ }
+ while (numBits > ++i);
+ }
+
+ if (i < numBits)
+ *nextDest_ |= (1 << destShift_);
+ else
+ bitsWritten = numBits;
+
+ ADVANCE_DEST;
+ }
+ blockSize >>= 1;
+
+ if (blockSize < 2)
+ blockSize = 2;
+
+ numBlocks++;
+ }
+ while (numBits > bitsWritten);
+}
+
+void EncodeBuffer::encodeCachedValue(unsigned int value, unsigned int numBits,
+ IntCache &cache, unsigned int blockSize)
+{
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoding " << numBits
+ << " bits cached value with block " << blockSize
+ << " and " << (nextDest_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ value &= IntMask[numBits];
+
+ if (end_ - nextDest_ < 8)
+ {
+ growBuffer();
+ }
+
+ blockSize = cache.getBlockSize(blockSize);
+
+ unsigned int index;
+ unsigned int sameDiff;
+
+ #ifdef DUMP
+
+ diffBits();
+
+ #endif
+
+ if (cache.lookup(value, index, IntMask[numBits], sameDiff))
+ {
+ if (index > 1)
+ index++;
+
+ while (destShift_ < index)
+ {
+ index -= destShift_;
+ index--;
+ destShift_ = 7;
+ nextDest_++;
+ *nextDest_ = 0;
+ }
+
+ destShift_ -= index;
+ *nextDest_ |= (1 << destShift_);
+ ADVANCE_DEST;
+
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoded cached int using "
+ << diffBits() << " bits out of " << numBits
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ ADVANCE_DEST;
+ ADVANCE_DEST;
+ *nextDest_ |= (1 << destShift_);
+ ADVANCE_DEST;
+
+ //
+ // The attempt is very seldom successful.
+ // Avoid to encode the additional bool.
+ //
+
+ // Since ProtoStep8 (#issue 108)
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoded missed int using "
+ << diffBits() << " bits out of " << numBits
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeValue(value, numBits, blockSize);
+ }
+}
+
+void EncodeBuffer::encodeCachedValue(unsigned char value, unsigned int numBits,
+ CharCache &cache, unsigned int blockSize)
+{
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoding " << numBits
+ << " bits char cached value with block " << blockSize
+ << " and " << (nextDest_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ value &= IntMask[numBits];
+
+ if (end_ - nextDest_ < 8)
+ {
+ growBuffer();
+ }
+
+ unsigned int index;
+
+ #ifdef DUMP
+
+ diffBits();
+
+ #endif
+
+ if (cache.lookup(value, index))
+ {
+ if (index > 1)
+ index++;
+
+ while (destShift_ < index)
+ {
+ index -= destShift_;
+ index--;
+ destShift_ = 7;
+ nextDest_++;
+ *nextDest_ = 0;
+ }
+
+ destShift_ -= index;
+ *nextDest_ |= (1 << destShift_);
+ ADVANCE_DEST;
+
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoded cached char using "
+ << diffBits() << " bits out of " << numBits
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ ADVANCE_DEST;
+ ADVANCE_DEST;
+ *nextDest_ |= (1 << destShift_);
+ ADVANCE_DEST;
+
+ encodeValue(value, numBits, blockSize);
+
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoded missed char using "
+ << diffBits() << " bits out of " << numBits
+ << ".\n" << logofs_flush;
+ #endif
+ }
+}
+
+void EncodeBuffer::encodeMemory(const unsigned char *buffer, unsigned int numBytes)
+{
+ #ifdef DUMP
+ *logofs << "EncodeBuffer: Encoding " << numBytes
+ << " bytes of memory with " << (nextDest_ - buffer_)
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ if (numBytes > ENCODE_BUFFER_OVERFLOW_SIZE)
+ {
+ #ifdef PANIC
+ *logofs << "EncodeBuffer: PANIC! Should never encode buffer "
+ << "of size greater than " << ENCODE_BUFFER_OVERFLOW_SIZE
+ << " bytes.\n" << logofs_flush;
+
+ *logofs << "EncodeBuffer: PANIC! Assuming failure encoding data "
+ << "in context [A].\n" << logofs_flush;
+ #endif
+
+ //
+ // Label "context" is just used to identify
+ // the routine which detected the problem in
+ // present source file.
+ //
+
+ cerr << "Error" << ": Should never encode buffer of size "
+ << "greater than " << ENCODE_BUFFER_OVERFLOW_SIZE
+ << " bytes.\n";
+
+ cerr << "Error" << ": Assuming failure encoding data "
+ << "in context [A].\n" ;
+
+ HandleAbort();
+ }
+
+ alignBuffer();
+
+ if (end_ - nextDest_ < (int) numBytes)
+ {
+ growBuffer(numBytes);
+ }
+
+ memcpy(nextDest_, buffer, numBytes);
+
+ nextDest_ += numBytes;
+
+ if (nextDest_ == end_)
+ {
+ growBuffer();
+ }
+ else if (nextDest_ > end_)
+ {
+ #ifdef PANIC
+ *logofs << "EncodeBuffer: PANIC! Assertion failed. Error [B] "
+ << "in encodeMemory() nextDest_ " << (nextDest_ - buffer)
+ << " end_ " << (end_ - buffer) << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Label "context" is just used to identify
+ // the routine which detected the problem in
+ // present source file.
+ //
+
+ cerr << "Error" << ": Failure encoding raw data "
+ << "in context [B].\n" ;
+
+ HandleAbort();
+ }
+
+ *nextDest_ = 0;
+}
+
+unsigned int EncodeBuffer::getLength() const
+{
+ unsigned int length = nextDest_ - buffer_;
+
+ if (destShift_ != 7)
+ {
+ length++;
+ }
+
+ // Since ProtoStep7 (#issue 108)
+ if (length > 0)
+ {
+ return length + ENCODE_BUFFER_POSTFIX_SIZE;
+ }
+
+ return length;
+}
+
+unsigned int EncodeBuffer::diffBits()
+{
+ unsigned int bits = ((nextDest_ - buffer_) << 3);
+
+ bits += (7 - destShift_);
+
+ unsigned int diff = bits - lastBits_;
+
+ lastBits_ = bits;
+
+ return diff;
+}
+
+void EncodeBuffer::growBuffer(unsigned int numBytes)
+{
+ if (numBytes == 0)
+ {
+ numBytes = initialSize_;
+ }
+
+ unsigned int bytesInBuffer = nextDest_ - buffer_;
+
+ unsigned int newSize = thresholdSize_;
+
+ while (newSize < bytesInBuffer + numBytes)
+ {
+ newSize <<= 1;
+
+ if (newSize > maximumSize_)
+ {
+ newSize = bytesInBuffer + numBytes + initialSize_;
+ }
+ }
+
+ unsigned char *newBuffer;
+
+ newBuffer = new unsigned char[newSize + ENCODE_BUFFER_PREFIX_SIZE +
+ ENCODE_BUFFER_POSTFIX_SIZE] + ENCODE_BUFFER_PREFIX_SIZE;
+
+ if (newBuffer == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "EncodeBuffer: PANIC! Error in context [C] "
+ << "growing buffer to accommodate " << numBytes
+ << " bytes .\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error in context [C] "
+ << "growing encode buffer to accommodate "
+ << numBytes << " bytes.\n";
+
+ HandleAbort();
+ }
+
+ #ifdef TEST
+ if (newSize >= maximumSize_)
+ {
+ *logofs << "EncodeBuffer: WARNING! Buffer grown to reach "
+ << "size of " << newSize << " bytes.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // Prefix should not contain any valid data.
+ // It is proxy that will fill it with control
+ // messages and data length at the time a new
+ // frame is written to socket.
+ //
+
+ memcpy(newBuffer, buffer_, bytesInBuffer + 1);
+
+ newBuffer[bytesInBuffer + 1] = 0;
+
+ delete [] (buffer_ - ENCODE_BUFFER_PREFIX_SIZE);
+
+ buffer_ = newBuffer;
+ size_ = newSize;
+ end_ = buffer_ + size_;
+
+ nextDest_ = buffer_ + bytesInBuffer;
+}
+
+void EncodeBuffer::alignBuffer()
+{
+ if (destShift_ != 7)
+ {
+ destShift_ = 7;
+ nextDest_++;
+
+ if (nextDest_ >= end_)
+ {
+ growBuffer();
+ }
+
+ *nextDest_ = 0;
+ }
+}
+
+void EncodeBuffer::encodeActionValue(unsigned char value, unsigned short position,
+ ActionCache &cache)
+{
+ unsigned int v = (value << 13) | position;
+
+ unsigned int t = (v - cache.last_);
+
+ encodeCachedValue(t, 15, *(cache.base_[cache.slot_]));
+
+ cache.last_ = v;
+
+ #ifdef DEBUG
+ *logofs << "EncodeBuffer: Encoded value "
+ << (unsigned) value << " and position "
+ << position << " with base " << cache.slot_
+ << ".\n" << logofs_flush;
+ #endif
+
+ cache.slot_ = (cache.last_ & 0xff);
+}
+
+void EncodeBuffer::encodeNewXidValue(unsigned int value, unsigned int &lastId,
+ IntCache &lastIdCache, IntCache &cache,
+ FreeCache &freeCache)
+{
+ encodeCachedValue((value - 1) - lastId, 29, lastIdCache);
+
+ lastId = value;
+
+ cache.push(value, 0x1fffffff);
+
+ freeCache.push(value, 0x1fffffff);
+}
+
+void EncodeBuffer::encodeNewXidValue(unsigned int value, unsigned int &lastId,
+ IntCache &lastIdCache, XidCache &cache,
+ FreeCache &freeCache)
+{
+ encodeCachedValue((value - 1) - lastId, 29, lastIdCache);
+
+ lastId = value;
+
+ unsigned int t = (value - cache.last_);
+
+ cache.last_ = value;
+
+ #ifdef DEBUG
+ *logofs << "EncodeBuffer: Encoded new Xid " << value
+ << " with base " << cache.slot_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cache.slot_ = (value & 0xff);
+
+ cache.base_[cache.slot_] -> push(t, 0x1fffffff);
+
+ freeCache.push(value, IntMask[29]);
+}
+
+void EncodeBuffer::encodeXidValue(unsigned int value, XidCache &cache)
+{
+ unsigned int t = (value - cache.last_);
+
+ encodeCachedValue(t, 29, *(cache.base_[cache.slot_]));
+
+ cache.last_ = value;
+
+ #ifdef DEBUG
+ *logofs << "EncodeBuffer: Encoded Xid " << value
+ << " with base " << cache.slot_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cache.slot_ = (value & 0xff);
+}
+
+void EncodeBuffer::encodeFreeXidValue(unsigned int value, FreeCache &cache)
+{
+ encodeCachedValue(value, 29, cache);
+}
diff --git a/nxcomp/src/EncodeBuffer.h b/nxcomp/src/EncodeBuffer.h
new file mode 100644
index 000000000..67f6ff093
--- /dev/null
+++ b/nxcomp/src/EncodeBuffer.h
@@ -0,0 +1,183 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef EncodeBuffer_H
+#define EncodeBuffer_H
+
+#include "IntCache.h"
+#include "CharCache.h"
+#include "XidCache.h"
+#include "FreeCache.h"
+#include "OpcodeCache.h"
+#include "ActionCache.h"
+
+#define ENCODE_BUFFER_DEFAULT_SIZE 16384
+
+//
+// This should match the maximum size of
+// a single message added to write buffer
+// (see WriteBuffer.h).
+//
+
+#define ENCODE_BUFFER_OVERFLOW_SIZE 4194304
+
+//
+// Adjust for the control messages and the
+// frame length added by the proxy.
+//
+
+#define ENCODE_BUFFER_PREFIX_SIZE 64
+
+//
+// The encode routines may write one byte
+// past the nominal end of the encode buffer.
+// This additional byte is included in the
+// payload. This is actually a harmless bug.
+//
+
+#define ENCODE_BUFFER_POSTFIX_SIZE 1
+
+class EncodeBuffer
+{
+ public:
+
+ EncodeBuffer();
+
+ ~EncodeBuffer();
+
+ void setSize(unsigned int initialSize, unsigned int thresholdSize,
+ unsigned int maximumSize);
+
+ void encodeValue(unsigned int value, unsigned int numBits,
+ unsigned int blockSize = 0);
+
+ void encodeCachedValue(unsigned int value, unsigned int numBits,
+ IntCache &cache, unsigned int blockSize = 0);
+
+ void encodeCachedValue(unsigned char value, unsigned int numBits,
+ CharCache &cache, unsigned int blockSize = 0);
+
+ void encodeDiffCachedValue(const unsigned int value, unsigned int &previous,
+ unsigned int numBits, IntCache &cache,
+ unsigned int blockSize = 0)
+ {
+ encodeCachedValue((value - 1) - previous, numBits, cache, blockSize);
+
+ previous = value;
+ }
+
+ void encodeBoolValue(unsigned int value)
+ {
+ encodeValue(value, 1);
+ }
+
+ void encodeOpcodeValue(unsigned char value, OpcodeCache &cache)
+ {
+ encodeCachedValue(value, 8, cache.base_[cache.slot_], 8);
+
+ cache.slot_ = value;
+ }
+
+ void encodeActionValue(unsigned char value, ActionCache &cache)
+ {
+ unsigned short position = 0;
+
+ encodeActionValue(value, position, cache);
+ }
+
+ void encodeActionValue(unsigned char value, unsigned short position,
+ ActionCache &cache);
+
+ void encodeNewXidValue(unsigned int value, unsigned int &lastId,
+ IntCache &lastIdCache, IntCache &cache,
+ FreeCache &freeCache);
+
+ void encodeNewXidValue(unsigned int value, unsigned int &lastId,
+ IntCache &lastIdCache, XidCache &cache,
+ FreeCache &freeCache);
+
+ void encodeXidValue(unsigned int value, XidCache &cache);
+
+ void encodeFreeXidValue(unsigned int value, FreeCache &cache);
+
+ void encodeTextData(const unsigned char *buffer, unsigned int numBytes)
+ {
+ encodeMemory(buffer, numBytes);
+ }
+
+ void encodeIntData(const unsigned char *buffer, unsigned int numBytes)
+ {
+ encodeMemory(buffer, numBytes);
+ }
+
+ void encodeLongData(const unsigned char *buffer, unsigned int numBytes)
+ {
+ encodeMemory(buffer, numBytes);
+ }
+
+ void encodeMemory(const unsigned char *buffer, unsigned int numBytes);
+
+ unsigned char *getData()
+ {
+ return buffer_;
+ }
+
+ unsigned int getLength() const;
+
+ unsigned int getBits() const
+ {
+ return ((nextDest_ - buffer_) << 3) + (7 - destShift_);
+ }
+
+ unsigned int diffBits();
+
+ void fullReset();
+
+ private:
+
+ void growBuffer(unsigned int numBytes = 0);
+
+ void alignBuffer();
+
+ unsigned int size_;
+ unsigned char *buffer_;
+
+ //
+ // This points to the first byte
+ // just beyond end of the buffer.
+ //
+
+ const unsigned char *end_;
+
+ unsigned char *nextDest_;
+ unsigned int destShift_;
+ unsigned int lastBits_;
+
+ unsigned int initialSize_;
+ unsigned int thresholdSize_;
+ unsigned int maximumSize_;
+};
+
+#endif /* EncodeBuffer_H */
diff --git a/nxcomp/src/FillPoly.cpp b/nxcomp/src/FillPoly.cpp
new file mode 100644
index 000000000..b5928d5cf
--- /dev/null
+++ b/nxcomp/src/FillPoly.cpp
@@ -0,0 +1,239 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "FillPoly.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int FillPolyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ FillPolyMessage *fillPoly = (FillPolyMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ fillPoly -> drawable = GetULONG(buffer + 4, bigEndian);
+ fillPoly -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ fillPoly -> shape = *(buffer + 12);
+ fillPoly -> mode = *(buffer + 13);
+
+ // Since ProtoStep8 (#issue 108)
+ if (size >= (unsigned int) dataOffset)
+ {
+ fillPoly -> x_origin = GetUINT(buffer + 16, bigEndian);
+ fillPoly -> y_origin = GetUINT(buffer + 18, bigEndian);
+ }
+ else
+ {
+ fillPoly -> x_origin = 0;
+ fillPoly -> y_origin = 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int FillPolyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ FillPolyMessage *fillPoly = (FillPolyMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(fillPoly -> drawable, buffer + 4, bigEndian);
+ PutULONG(fillPoly -> gcontext, buffer + 8, bigEndian);
+
+ *(buffer + 12) = fillPoly -> shape;
+ *(buffer + 13) = fillPoly -> mode;
+
+ // Since ProtoStep8 (#issue 108)
+ if (size >= (unsigned int) dataOffset)
+ {
+ PutUINT(fillPoly -> x_origin, buffer + 16, bigEndian);
+ PutUINT(fillPoly -> y_origin, buffer + 18, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void FillPolyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ FillPolyMessage *fillPoly = (FillPolyMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << fillPoly -> drawable
+ << ", gcontext " << fillPoly -> gcontext << ", shape "
+ << fillPoly -> shape << ", mode " << fillPoly -> mode
+ << fillPoly -> size_ << ", x_origin " << fillPoly -> x_origin
+ << ", y_origin " << fillPoly -> y_origin << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+void FillPolyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Fields shape, mode.
+ //
+
+ md5_append(md5_state_, buffer + 12, 2);
+}
+
+void FillPolyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ FillPolyMessage *fillPoly = (FillPolyMessage *) message;
+ FillPolyMessage *cachedFillPoly = (FillPolyMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << fillPoly -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(fillPoly -> drawable, clientCache -> drawableCache);
+
+ cachedFillPoly -> drawable = fillPoly -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << fillPoly -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(fillPoly -> gcontext, clientCache -> gcCache);
+
+ cachedFillPoly -> gcontext = fillPoly -> gcontext;
+
+ // Since ProtoStep8 (#issue 108)
+ if (fillPoly -> size_ >= dataOffset)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << fillPoly -> x_origin
+ << " as x_origin field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(fillPoly -> x_origin, 16,
+ *clientCache -> fillPolyXAbsCache[0], 8);
+
+ cachedFillPoly -> x_origin = fillPoly -> x_origin;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << fillPoly -> y_origin
+ << " as y_origin field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(fillPoly -> y_origin, 16,
+ *clientCache -> fillPolyYAbsCache[0], 8);
+
+ cachedFillPoly -> y_origin = fillPoly -> y_origin;
+ }
+}
+
+void FillPolyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ FillPolyMessage *fillPoly = (FillPolyMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(fillPoly -> drawable, clientCache -> drawableCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << fillPoly -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(fillPoly -> gcontext, clientCache -> gcCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << fillPoly -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ // Since ProtoStep8 (#issue 108)
+ if (fillPoly -> size_ >= dataOffset)
+ {
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> fillPolyXAbsCache[0], 8);
+
+ fillPoly -> x_origin = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << fillPoly -> x_origin
+ << " as x_origin field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> fillPolyYAbsCache[0], 8);
+
+ fillPoly -> y_origin = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << fillPoly -> y_origin
+ << " as y_origin field.\n" << logofs_flush;
+ #endif
+ }
+}
+
+
diff --git a/nxcomp/src/FillPoly.h b/nxcomp/src/FillPoly.h
new file mode 100644
index 000000000..4ceb96c09
--- /dev/null
+++ b/nxcomp/src/FillPoly.h
@@ -0,0 +1,200 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef FillPoly_H
+#define FillPoly_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define FILLPOLY_ENABLE_CACHE 1
+#define FILLPOLY_ENABLE_DATA 0
+#define FILLPOLY_ENABLE_SPLIT 0
+#define FILLPOLY_ENABLE_COMPRESS 0
+
+#define FILLPOLY_DATA_LIMIT 512
+
+#define FILLPOLY_CACHE_SLOTS 2000
+#define FILLPOLY_CACHE_THRESHOLD 3
+#define FILLPOLY_CACHE_LOWER_THRESHOLD 1
+
+#define FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 20
+
+//
+// The message class.
+//
+
+class FillPolyMessage : public Message
+{
+ friend class FillPolyStore;
+
+ public:
+
+ FillPolyMessage()
+ {
+ }
+
+ ~FillPolyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char shape;
+ unsigned char mode;
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned short x_origin;
+ unsigned short y_origin;
+};
+
+class FillPolyStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ FillPolyStore() : MessageStore()
+ {
+ enableCache = FILLPOLY_ENABLE_CACHE;
+ enableData = FILLPOLY_ENABLE_DATA;
+ enableSplit = FILLPOLY_ENABLE_SPLIT;
+ enableCompress = FILLPOLY_ENABLE_COMPRESS;
+
+ dataLimit = FILLPOLY_DATA_LIMIT;
+
+ // Since ProtoStep8 (#issue 108)
+ dataOffset = FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8;
+
+ cacheSlots = FILLPOLY_CACHE_SLOTS;
+ cacheThreshold = FILLPOLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = FILLPOLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~FillPolyStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "FillPoly";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_FillPoly;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(FillPolyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new FillPolyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new FillPolyMessage((const FillPolyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (FillPolyMessage *) message;
+ }
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size)
+ {
+ // Since ProtoStep8 (#issue 108)
+ return (size >= FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 ?
+ FILLPOLY_DATA_OFFSET_IF_PROTO_STEP_8 : size);
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* FillPoly_H */
diff --git a/nxcomp/src/Fork.cpp b/nxcomp/src/Fork.cpp
new file mode 100644
index 000000000..657c36134
--- /dev/null
+++ b/nxcomp/src/Fork.cpp
@@ -0,0 +1,110 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+
+#include "Fork.h"
+#include "Misc.h"
+#include "Timestamp.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Only on Cygwin, retry n times waiting a
+// given amount of milliseconds after each
+// attempt.
+//
+
+#define RETRY_LIMIT 30
+#define RETRY_TIMEOUT 1000
+
+int Fork()
+{
+ #ifdef __CYGWIN32__
+
+ int limit = RETRY_LIMIT;
+ int timeout = RETRY_TIMEOUT;
+
+ #else
+
+ int limit = 1;
+ int timeout = 0;
+
+ #endif
+
+ int pid = 0;
+
+ for (int i = 0; i < limit; i++)
+ {
+ #ifdef TEST
+ *logofs << "Fork: Trying at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // It could optionally try again only if the
+ // error code is 11, 'Resource temporarily
+ // unavailable'.
+ //
+
+ if ((pid = fork()) >= 0)
+ {
+ break;
+ }
+ else if (i < limit - 1)
+ {
+ #ifdef WARNING
+ *logofs << "Fork: WARNING! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'. Retrying...\n" << logofs_flush;
+ #endif
+
+ usleep(timeout * 1000);
+ }
+ }
+
+ #ifdef TEST
+
+ if (pid <= 0)
+ {
+ *logofs << "Fork: Returning at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ return pid;
+}
diff --git a/nxcomp/src/Fork.h b/nxcomp/src/Fork.h
new file mode 100644
index 000000000..94238ed90
--- /dev/null
+++ b/nxcomp/src/Fork.h
@@ -0,0 +1,31 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+//
+// Try again if the fork() fails, as it can happen
+// often on Cygwin.
+//
+
+extern int Fork();
diff --git a/nxcomp/src/FreeCache.h b/nxcomp/src/FreeCache.h
new file mode 100644
index 000000000..bf5c801e5
--- /dev/null
+++ b/nxcomp/src/FreeCache.h
@@ -0,0 +1,42 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef FreeCache_H
+#define FreeCache_H
+
+#include "IntCache.h"
+
+class FreeCache : public IntCache
+{
+ public:
+
+ FreeCache(unsigned int size)
+
+ : IntCache(size)
+ {
+ }
+};
+
+#endif /* FreeCache_H */
diff --git a/nxcomp/src/GenericChannel.cpp b/nxcomp/src/GenericChannel.cpp
new file mode 100644
index 000000000..877412cee
--- /dev/null
+++ b/nxcomp/src/GenericChannel.cpp
@@ -0,0 +1,495 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "GenericChannel.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "StaticCompressor.h"
+
+#include "Statistics.h"
+#include "Proxy.h"
+
+extern Proxy *proxy;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Log the important tracepoints related
+// to writing packets to the peer proxy.
+//
+
+#undef FLUSH
+
+//
+// Define this to log when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Here are the static members.
+//
+
+#ifdef REFERENCES
+
+int GenericChannel::references_ = 0;
+
+#endif
+
+GenericChannel::GenericChannel(Transport *transport, StaticCompressor *compressor)
+
+ : Channel(transport, compressor), readBuffer_(transport_, this)
+{
+ #ifdef REFERENCES
+ *logofs << "GenericChannel: Created new object at "
+ << this << " for FD#" << fd_ << " out of "
+ << ++references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+GenericChannel::~GenericChannel()
+{
+ #ifdef REFERENCES
+ *logofs << "GenericChannel: Deleted object at "
+ << this << " for FD#" << fd_ << " out of "
+ << --references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+//
+// Beginning of handleRead().
+//
+
+int GenericChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length)
+{
+ #ifdef TEST
+ *logofs << "handleRead: Called for FD#" << fd_
+ << " with " << encodeBuffer.getLength()
+ << " bytes already encoded.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Pointer to located message and
+ // its size in bytes.
+ //
+
+ const unsigned char *inputMessage;
+ unsigned int inputLength;
+
+ //
+ // Tag message as generic data in compression
+ // routine. Opcode is not actually transferred
+ // over the network.
+ //
+
+ unsigned char inputOpcode = X_NXInternalGenericData;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: Trying to read from FD#"
+ << fd_ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = readBuffer_.readMessage();
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Read result on FD#" << fd_
+ << " is " << result << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (result < 0)
+ {
+ //
+ // Let the proxy close the channel.
+ //
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ *logofs << "handleRead: PANIC! No data read from FD#"
+ << fd_ << " while encoding messages.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+
+ #endif
+
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleRead: Encoding messages for FD#" << fd_
+ << " with " << readBuffer_.getLength() << " bytes "
+ << "in the buffer.\n" << logofs_flush;
+ #endif
+
+ //
+ // Divide the available data in multiple
+ // messages and encode them one by one.
+ //
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL)
+ {
+ encodeBuffer.encodeValue(inputLength, 32, 14);
+
+ if (isCompressed() == 1)
+ {
+ unsigned int compressedDataSize = 0;
+ unsigned char *compressedData = NULL;
+
+ if (handleCompress(encodeBuffer, inputOpcode, 0,
+ inputMessage, inputLength, compressedData,
+ compressedDataSize) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ encodeBuffer.encodeMemory(inputMessage, inputLength);
+ }
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleRead: Handled generic data for FD#" << fd_
+ << ". " << inputLength << " bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ addProtocolBits(inputLength << 3, bits);
+
+ if (isPrioritized() == 1)
+ {
+ priority_++;
+ }
+
+ } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL) ...
+
+ //
+ // All data has been read from the read buffer.
+ // We still need to mark the end of the encode
+ // buffer just before sending the frame. This
+ // allows us to accommodate multiple reads in
+ // a single frame.
+ //
+
+ if (priority_ > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of " << priority_ << " prioritized "
+ << "messages for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncPriority() < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset the priority flag.
+ //
+
+ priority_ = 0;
+ }
+
+ //
+ // Flush if we produced enough data.
+ //
+
+ if (proxy -> canAsyncFlush() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of enough data or timeout on the "
+ << "proxy link.\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncFlush() < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (transport_ -> pending() != 0 ||
+ readBuffer_.checkMessage() != 0)
+ {
+ *logofs << "handleRead: PANIC! Buffer for X descriptor FD#"
+ << fd_ << " has " << transport_ -> pending()
+ << " bytes to read.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Reset the read buffer.
+ //
+
+ readBuffer_.fullReset();
+
+ return 1;
+}
+
+//
+// End of handleRead().
+//
+
+//
+// Beginning of handleWrite().
+//
+
+int GenericChannel::handleWrite(const unsigned char *message, unsigned int length)
+{
+ #ifdef TEST
+ *logofs << "handleWrite: Called for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Create the buffer from which to
+ // decode messages.
+ //
+
+ DecodeBuffer decodeBuffer(message, length);
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleWrite: Decoding messages for FD#" << fd_
+ << " with " << length << " bytes in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char *outputMessage;
+ unsigned int outputLength;
+
+ //
+ // Tag message as generic data
+ // in decompression.
+ //
+
+ unsigned char outputOpcode = X_NXInternalGenericData;
+
+ for (;;)
+ {
+ decodeBuffer.decodeValue(outputLength, 32, 14);
+
+ if (outputLength == 0)
+ {
+ break;
+ }
+
+ if (isCompressed() == 1)
+ {
+ if (writeBuffer_.getAvailable() < outputLength ||
+ (int) outputLength >= control -> TransportFlushBufferSize)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Using scratch buffer for "
+ << "generic data with size " << outputLength << " and "
+ << writeBuffer_.getLength() << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+
+ outputMessage = writeBuffer_.addScratchMessage(outputLength);
+ }
+ else
+ {
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ }
+
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, outputOpcode, 0,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Using scratch buffer for "
+ << "generic data with size " << outputLength << " and "
+ << writeBuffer_.getLength() << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+
+ writeBuffer_.addScratchMessage((unsigned char *)
+ decodeBuffer.decodeMemory(outputLength), outputLength);
+ }
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Handled generic data for FD#" << fd_
+ << ". " << outputLength << " bytes out.\n"
+ << logofs_flush;
+ #endif
+
+ handleFlush(flush_if_needed);
+ }
+
+ //
+ // Write any remaining data to socket.
+ //
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+//
+// End of handleWrite().
+//
+
+//
+// Other members.
+//
+
+int GenericChannel::handleCompletion(EncodeBuffer &encodeBuffer)
+{
+ //
+ // Add the bits telling to the remote
+ // that all data in the frame has been
+ // encoded.
+ //
+
+ if (encodeBuffer.getLength() > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleCompletion: Writing completion bits with "
+ << encodeBuffer.getLength() << " bytes encoded "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(0, 32, 14);
+
+ return 1;
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "handleCompletion: PANIC! No completion to write "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+ #endif
+
+ return 0;
+}
+
+int GenericChannel::handleConfiguration()
+{
+ #ifdef TEST
+ *logofs << "GenericChannel: Setting new buffer parameters.\n"
+ << logofs_flush;
+ #endif
+
+ readBuffer_.setSize(control -> GenericInitialReadSize,
+ control -> GenericMaximumBufferSize);
+
+ writeBuffer_.setSize(control -> TransportGenericBufferSize,
+ control -> TransportGenericBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ transport_ -> setSize(control -> TransportGenericBufferSize,
+ control -> TransportGenericBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ return 1;
+}
+
+int GenericChannel::handleFinish()
+{
+ #ifdef TEST
+ *logofs << "GenericChannel: Finishing channel for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ priority_ = 0;
+
+ finish_ = 1;
+
+ transport_ -> fullReset();
+
+ return 1;
+}
+
+int GenericChannel::setReferences()
+{
+ #ifdef TEST
+ *logofs << "GenericChannel: Initializing the static "
+ << "members for the generic channels.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef REFERENCES
+
+ references_ = 0;
+
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/GenericChannel.h b/nxcomp/src/GenericChannel.h
new file mode 100644
index 000000000..3df18f444
--- /dev/null
+++ b/nxcomp/src/GenericChannel.h
@@ -0,0 +1,440 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef GenericChannel_H
+#define GenericChannel_H
+
+#include "Channel.h"
+
+#include "Statistics.h"
+
+#include "GenericReadBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to log a line when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// This class implements the client
+// side compression of X protocol.
+//
+
+class GenericChannel : public Channel
+{
+ public:
+
+ GenericChannel(Transport *transport, StaticCompressor *compressor);
+
+ virtual ~GenericChannel();
+
+ virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length);
+
+ virtual int handleWrite(const unsigned char *message, unsigned int length);
+
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
+ T_store_action action, int position, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+ {
+ return 0;
+ }
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store,
+ T_store_action action, int position, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+ {
+ return 0;
+ }
+
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split)
+ {
+ return 0;
+ }
+
+ virtual int handleSplitEvent(DecodeBuffer &decodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleMotion(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleCompletion(EncodeBuffer &encodeBuffer);
+
+ virtual int handleConfiguration();
+
+ virtual int handleFinish();
+
+ virtual int handleAsyncEvents()
+ {
+ return 0;
+ }
+
+ virtual int needSplit() const
+ {
+ return 0;
+ }
+
+ virtual int needMotion() const
+ {
+ return 0;
+ }
+
+ virtual T_channel_type getType() const = 0;
+
+ //
+ // Initialize the static members.
+ //
+
+ static int setReferences();
+
+ protected:
+
+ //
+ // Generic channels are considered to be
+ // in congestion state as soon as the
+ // socket is blocked for write.
+ //
+
+ virtual int isCongested()
+ {
+ return (transport_ -> blocked() == 1);
+ }
+
+ virtual int isReliable()
+ {
+ return 0;
+ }
+
+ //
+ // Model generic channels' encoding and
+ // decoding policy.
+ //
+
+ virtual int isCompressed() = 0;
+
+ //
+ // Return true if the channel contains
+ // time sensitive data.
+ //
+
+ virtual int isPrioritized() = 0;
+
+ //
+ // Record the protocol bits for the
+ // specific service.
+ //
+
+ virtual void addProtocolBits(unsigned int bitsIn, unsigned int bitsOut) = 0;
+
+ //
+ // Channel's own read buffer.
+ //
+
+ GenericReadBuffer readBuffer_;
+
+ private:
+
+ //
+ // Keep track of object's creation
+ // and deletion.
+ //
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+class CupsChannel : public GenericChannel
+{
+ public:
+
+ CupsChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~CupsChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_cups;
+ }
+
+ virtual int isCompressed()
+ {
+ // Since ProtoStep8 (#issue 108)
+ return 0;
+ }
+
+ virtual int isPrioritized()
+ {
+ return 0;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addCupsBits(bitsIn, bitsOut);
+ }
+};
+
+class SmbChannel : public GenericChannel
+{
+ public:
+
+ SmbChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~SmbChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_smb;
+ }
+
+ virtual int isCompressed()
+ {
+ // Since ProtoStep8 (#issue 108)
+ return 0;
+ }
+
+ virtual int isPrioritized()
+ {
+ return 0;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addSmbBits(bitsIn, bitsOut);
+ }
+};
+
+class MediaChannel : public GenericChannel
+{
+ public:
+
+ MediaChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~MediaChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_media;
+ }
+
+ //
+ // Don't try to compress the media data.
+ //
+
+ virtual int isCompressed()
+ {
+ return 0;
+ }
+
+ //
+ // Reduce the latency of media channels
+ // by setting them as prioritized, even
+ // if this will take away bandwidth from
+ // the X channels.
+ //
+
+ virtual int isPrioritized()
+ {
+ return 1;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addMediaBits(bitsIn, bitsOut);
+ }
+};
+
+class HttpChannel : public GenericChannel
+{
+ public:
+
+ HttpChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~HttpChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_http;
+ }
+
+ virtual int isCompressed()
+ {
+ // Since ProtoStep8 (#issue 108)
+ return 0;
+ }
+
+ virtual int isPrioritized()
+ {
+ return 0;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addHttpBits(bitsIn, bitsOut);
+ }
+};
+
+class FontChannel : public GenericChannel
+{
+ public:
+
+ FontChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~FontChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_font;
+ }
+
+ virtual int isCompressed()
+ {
+ // Since ProtoStep8 (#issue 108)
+ return 0;
+ }
+
+ virtual int isPrioritized()
+ {
+ return 1;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addFontBits(bitsIn, bitsOut);
+ }
+};
+
+class SlaveChannel : public GenericChannel
+{
+ public:
+
+ SlaveChannel(Transport *transport, StaticCompressor *compressor)
+
+ : GenericChannel(transport, compressor)
+ {
+ }
+
+ virtual ~SlaveChannel()
+ {
+ }
+
+ protected:
+
+ virtual T_channel_type getType() const
+ {
+ return channel_slave;
+ }
+
+ virtual int isCompressed()
+ {
+ return 0;
+ }
+
+ virtual int isPrioritized()
+ {
+ return 0;
+ }
+
+ virtual void addProtocolBits(unsigned int bitsIn,
+ unsigned int bitsOut)
+ {
+ statistics -> addSlaveBits(bitsIn, bitsOut);
+ }
+};
+
+#endif /* GenericChannel_H */
diff --git a/nxcomp/src/GenericReadBuffer.cpp b/nxcomp/src/GenericReadBuffer.cpp
new file mode 100644
index 000000000..78217157e
--- /dev/null
+++ b/nxcomp/src/GenericReadBuffer.cpp
@@ -0,0 +1,82 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "GenericReadBuffer.h"
+
+#include "GenericChannel.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+unsigned int GenericReadBuffer::suggestedLength(unsigned int pendingLength)
+{
+ //
+ // Always read the initial read size.
+ //
+
+ return 0;
+}
+
+int GenericReadBuffer::locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength)
+{
+ //
+ // We don't care about the endianess
+ // in generic channels.
+ //
+
+ unsigned int size = end - start;
+
+ #ifdef TEST
+ *logofs << "GenericReadBuffer: Locating message for FD#"
+ << transport_ -> fd() << " with " << size
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ if (size == 0)
+ {
+ remaining_ = 1;
+
+ return 0;
+ }
+
+ dataLength = size;
+
+ controlLength = 0;
+ trailerLength = 0;
+
+ remaining_ = 0;
+
+ return 1;
+}
diff --git a/nxcomp/src/GenericReadBuffer.h b/nxcomp/src/GenericReadBuffer.h
new file mode 100644
index 000000000..5ea4d939d
--- /dev/null
+++ b/nxcomp/src/GenericReadBuffer.h
@@ -0,0 +1,61 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef GenericReadBuffer_H
+#define GenericReadBuffer_H
+
+#include "ReadBuffer.h"
+#include "Control.h"
+
+class GenericChannel;
+
+class GenericReadBuffer : public ReadBuffer
+{
+ public:
+
+ GenericReadBuffer(Transport *transport, GenericChannel *channel)
+
+ : ReadBuffer(transport), channel_(channel)
+ {
+ }
+
+ virtual ~GenericReadBuffer()
+ {
+ }
+
+ protected:
+
+ virtual unsigned int suggestedLength(unsigned int pendingLength);
+
+ virtual int locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength);
+
+ GenericChannel *channel_;
+};
+
+#endif /* GenericReadBuffer_H */
diff --git a/nxcomp/src/GenericReply.cpp b/nxcomp/src/GenericReply.cpp
new file mode 100644
index 000000000..b56e579e8
--- /dev/null
+++ b/nxcomp/src/GenericReply.cpp
@@ -0,0 +1,302 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "GenericReply.h"
+
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+GenericReplyStore::GenericReplyStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = GENERICREPLY_ENABLE_CACHE;
+ enableData = GENERICREPLY_ENABLE_DATA;
+ enableSplit = GENERICREPLY_ENABLE_SPLIT;
+
+ // Since ProtoStep7 (#issue 108)
+ enableCompress = GENERICREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = GENERICREPLY_DATA_LIMIT;
+ dataOffset = GENERICREPLY_DATA_OFFSET;
+
+ cacheSlots = GENERICREPLY_CACHE_SLOTS;
+ cacheThreshold = GENERICREPLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = GENERICREPLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+GenericReplyStore::~GenericReplyStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GenericReplyStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(GetULONG(buffer + 4, bigEndian), 32, 15);
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ serverCache -> genericReplyCharCache);
+
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + i * 4 + 8, bigEndian), 32,
+ *serverCache -> genericReplyIntCache[i]);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericReplyStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeValue(size, 32, 15);
+
+ size = 32 + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeCachedValue(*(buffer + 1), 8,
+ serverCache -> genericReplyCharCache);
+
+ unsigned int value;
+
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 32,
+ *serverCache -> genericReplyIntCache[i]);
+
+ PutULONG(value, buffer + i * 4 + 8, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericReplyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GenericReplyMessage *genericReply = (GenericReplyMessage *) message;
+
+ genericReply -> byte_data = *(buffer + 1);
+
+ for (int i = 0; i < 12; i++)
+ {
+ genericReply -> short_data[i] = GetUINT(buffer + i * 2 + 8, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericReplyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GenericReplyMessage *genericReply = (GenericReplyMessage *) message;
+
+ *(buffer + 1) = genericReply -> byte_data;
+
+ for (int i = 0; i < 12; i++)
+ {
+ PutUINT(genericReply -> short_data[i], buffer + i * 2 + 8, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GenericReplyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GenericReplyMessage *genericReply = (GenericReplyMessage *) message;
+
+ *logofs << name() << ": Identity byte_data "
+ << (unsigned) genericReply -> byte_data;
+
+ for (int i = 0; i < 12; i++)
+ {
+ *logofs << ", short_data[" << i << "]"
+ << (unsigned) genericReply -> short_data[i];
+ }
+
+ *logofs << ", size " << genericReply -> size_ << ".\n";
+
+ #endif
+}
+
+void GenericReplyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void GenericReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ GenericReplyMessage *genericReply = (GenericReplyMessage *) message;
+ GenericReplyMessage *cachedGenericReply = (GenericReplyMessage *) cachedMessage;
+
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value "
+ << (unsigned int) genericReply -> byte_data
+ << " as byte_data field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(genericReply -> byte_data, 8,
+ serverCache -> genericReplyCharCache);
+
+ cachedGenericReply -> byte_data = genericReply -> byte_data;
+
+ for (unsigned int i = 0; i < 12; i++)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << genericReply -> short_data[i]
+ << " as short_data[" << i << "] field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(genericReply -> short_data[i], 16,
+ *serverCache -> genericReplyIntCache[i]);
+
+ cachedGenericReply -> short_data[i] = genericReply -> short_data[i];
+ }
+}
+
+void GenericReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ //
+ // Decode the variant part.
+ //
+
+ GenericReplyMessage *genericReply = (GenericReplyMessage *) message;
+
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(genericReply -> byte_data, 8,
+ serverCache -> genericReplyCharCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value "
+ << (unsigned int) genericReply -> byte_data
+ << " as byte_data field.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+
+ for (unsigned int i = 0; i < 12; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *serverCache -> genericReplyIntCache[i]);
+
+ genericReply -> short_data[i] = (unsigned short) value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << genericReply -> short_data[i]
+ << " as short_data[" << i << "] field.\n" << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/src/GenericReply.h b/nxcomp/src/GenericReply.h
new file mode 100644
index 000000000..e899b8467
--- /dev/null
+++ b/nxcomp/src/GenericReply.h
@@ -0,0 +1,161 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef GenericReply_H
+#define GenericReply_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GENERICREPLY_ENABLE_CACHE 1
+#define GENERICREPLY_ENABLE_DATA 1
+#define GENERICREPLY_ENABLE_SPLIT 0
+
+#define GENERICREPLY_DATA_LIMIT 1048576 - 32
+#define GENERICREPLY_DATA_OFFSET 32
+
+#define GENERICREPLY_CACHE_SLOTS 400
+#define GENERICREPLY_CACHE_THRESHOLD 5
+#define GENERICREPLY_CACHE_LOWER_THRESHOLD 1
+
+#define GENERICREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class GenericReplyMessage : public Message
+{
+ friend class GenericReplyStore;
+
+ public:
+
+ GenericReplyMessage()
+ {
+ }
+
+ ~GenericReplyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute the
+ // 'identity' part of the message. Starting
+ // from protocol level 3 we use short data
+ // to increase cache efficiency.
+ //
+
+ private:
+
+ unsigned char byte_data;
+ unsigned int int_data[6];
+ unsigned short short_data[12];
+};
+
+class GenericReplyStore : public MessageStore
+{
+ public:
+
+ GenericReplyStore(StaticCompressor *compressor);
+
+ virtual ~GenericReplyStore();
+
+ virtual const char *name() const
+ {
+ return "GenericReply";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXInternalGenericReply;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GenericReplyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new GenericReplyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GenericReplyMessage((const GenericReplyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GenericReplyMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GenericReply_H */
diff --git a/nxcomp/src/GenericRequest.cpp b/nxcomp/src/GenericRequest.cpp
new file mode 100644
index 000000000..c569d6a24
--- /dev/null
+++ b/nxcomp/src/GenericRequest.cpp
@@ -0,0 +1,334 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "GenericRequest.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+GenericRequestStore::GenericRequestStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = GENERICREQUEST_ENABLE_CACHE;
+ enableData = GENERICREQUEST_ENABLE_DATA;
+ enableSplit = GENERICREQUEST_ENABLE_SPLIT;
+
+ // Since ProtoStep7 (#issue 108)
+ enableCompress = GENERICREQUEST_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = GENERICREQUEST_DATA_LIMIT;
+ dataOffset = GENERICREQUEST_DATA_OFFSET;
+
+ cacheSlots = GENERICREQUEST_CACHE_SLOTS;
+ cacheThreshold = GENERICREQUEST_CACHE_THRESHOLD;
+ cacheLowerThreshold = GENERICREQUEST_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+GenericRequestStore::~GenericRequestStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GenericRequestStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(size >> 2, 16, 10);
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> genericRequestOpcodeCache);
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding data[" << i << "] "
+ << "at position " << i * 2 + 4 << " with value "
+ << GetUINT(buffer + (i * 2) + 4, bigEndian)
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + (i * 2) + 4, bigEndian), 16,
+ *clientCache -> genericRequestDataCache[i]);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericRequestStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeValue(size, 16, 10);
+
+ size <<= 2;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeCachedValue(*(buffer + 1), 8,
+ clientCache -> genericRequestOpcodeCache);
+
+ unsigned int value;
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> genericRequestDataCache[i]);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding data[" << i << "] "
+ << "at position " << i * 2 + 4 << " with value "
+ << value << ".\n" << logofs_flush;
+ #endif
+
+ PutUINT(value, buffer + 4 + (i * 2), bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericRequestStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GenericRequestMessage *genericRequest = (GenericRequestMessage *) message;
+
+ genericRequest -> opcode = *(buffer + 1);
+
+ for (unsigned int i = 0; i < 8; i++)
+ {
+ if ((i * 2 + 4) < size)
+ {
+ genericRequest -> data[i] = GetUINT(buffer + i * 2 + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed data[" << i << "] "
+ << "with value " << genericRequest -> data[i]
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ genericRequest -> data[i] = 0;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GenericRequestStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GenericRequestMessage *genericRequest = (GenericRequestMessage *) message;
+
+ *(buffer + 1) = genericRequest -> opcode;
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed data[" << i << "] "
+ << "with value " << genericRequest -> data[i]
+ << ".\n" << logofs_flush;
+ #endif
+
+ PutUINT(genericRequest -> data[i], buffer + i * 2 + 4, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GenericRequestStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GenericRequestMessage *genericRequest = (GenericRequestMessage *) message;
+
+ *logofs << name() << ": Identity opcode " << (unsigned) genericRequest -> opcode;
+
+ for (int i = 0; i < 8; i++)
+ {
+ *logofs << ", data[" << i << "] " << genericRequest -> data[i];
+ }
+
+ *logofs << ", size " << genericRequest -> size_ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void GenericRequestStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // As data offset can be beyond the real end of
+ // the message, we need to include the message's
+ // size or we will match any message whose size
+ // is less or equal to the data offset.
+ //
+
+ md5_append(md5_state_, buffer + 2, 2);
+}
+
+void GenericRequestStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ GenericRequestMessage *genericRequest = (GenericRequestMessage *) message;
+ GenericRequestMessage *cachedGenericRequest = (GenericRequestMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Updating value "
+ << (unsigned) genericRequest -> opcode
+ << " as opcode field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((unsigned int) genericRequest -> opcode, 8,
+ clientCache -> genericRequestOpcodeCache);
+
+ cachedGenericRequest -> opcode = genericRequest -> opcode;
+
+ for (int i = 0; i < 8 && (i * 2 + 4) < genericRequest -> size_; i++)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Updating data[" << i << "] "
+ << "with value " << genericRequest -> data[i]
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((unsigned int) genericRequest -> data[i], 16,
+ *clientCache -> genericRequestDataCache[i]);
+
+ cachedGenericRequest -> data[i] = genericRequest -> data[i];
+ }
+}
+
+void GenericRequestStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ GenericRequestMessage *genericRequest = (GenericRequestMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(genericRequest -> opcode, 8,
+ clientCache -> genericRequestOpcodeCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Updated value "
+ << (unsigned) genericRequest -> opcode
+ << " as opcode field.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+
+ for (int i = 0; i < 8 && (i * 2 + 4) < genericRequest -> size_; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> genericRequestDataCache[i]);
+
+ genericRequest -> data[i] = (unsigned short) value;
+
+ #ifdef TEST
+ *logofs << name() << ": Updated data[" << i << "] "
+ << "with value " << genericRequest -> data[i]
+ << ".\n" << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/src/GenericRequest.h b/nxcomp/src/GenericRequest.h
new file mode 100644
index 000000000..13ffea718
--- /dev/null
+++ b/nxcomp/src/GenericRequest.h
@@ -0,0 +1,160 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef GenericRequest_H
+#define GenericRequest_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GENERICREQUEST_ENABLE_CACHE 1
+#define GENERICREQUEST_ENABLE_DATA 1
+#define GENERICREQUEST_ENABLE_SPLIT 0
+
+#define GENERICREQUEST_DATA_LIMIT 262144 - 20
+#define GENERICREQUEST_DATA_OFFSET 20
+
+#define GENERICREQUEST_CACHE_SLOTS 400
+#define GENERICREQUEST_CACHE_THRESHOLD 5
+#define GENERICREQUEST_CACHE_LOWER_THRESHOLD 1
+
+#define GENERICREQUEST_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class GenericRequestMessage : public Message
+{
+ friend class GenericRequestStore;
+
+ public:
+
+ GenericRequestMessage()
+ {
+ }
+
+ ~GenericRequestMessage()
+ {
+ }
+
+ //
+ // Note that we consider for this message a data offset
+ // of 4 (or 20 starting from protocol 3). Bytes from 9
+ // to 20, if present, are taken as part of identity and
+ // encoded through an array of int caches.
+ //
+
+ private:
+
+ unsigned char opcode;
+ unsigned short data[8];
+};
+
+class GenericRequestStore : public MessageStore
+{
+ public:
+
+ GenericRequestStore(StaticCompressor *compressor);
+
+ virtual ~GenericRequestStore();
+
+ virtual const char *name() const
+ {
+ return "GenericRequest";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXInternalGenericRequest;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GenericRequestMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new GenericRequestMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GenericRequestMessage((const GenericRequestMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GenericRequestMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GenericRequest_H */
diff --git a/nxcomp/src/GetImage.cpp b/nxcomp/src/GetImage.cpp
new file mode 100644
index 000000000..20b6a07d1
--- /dev/null
+++ b/nxcomp/src/GetImage.cpp
@@ -0,0 +1,177 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "GetImage.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GetImageStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetImageMessage *getImage = (GetImageMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ getImage -> format = *(buffer + 1);
+
+ #ifdef TEST
+ if (getImage -> format != 1 && getImage -> format != 2)
+ {
+ *logofs << name() << ": WARNING! Dirty value " << getImage -> format
+ << " for field format.\n" << logofs_flush;
+ }
+ #endif
+
+ getImage -> drawable = GetULONG(buffer + 4, bigEndian);
+
+ getImage -> x = GetUINT(buffer + 8, bigEndian);
+ getImage -> y = GetUINT(buffer + 10, bigEndian);
+ getImage -> width = GetUINT(buffer + 12, bigEndian);
+ getImage -> height = GetUINT(buffer + 14, bigEndian);
+
+ getImage -> plane_mask = GetULONG(buffer + 16, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetImageStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetImageMessage *getImage = (GetImageMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = getImage -> format;
+
+ PutULONG(getImage -> drawable, buffer + 4, bigEndian);
+
+ PutUINT(getImage -> x, buffer + 8, bigEndian);
+ PutUINT(getImage -> y, buffer + 10, bigEndian);
+ PutUINT(getImage -> width, buffer + 12, bigEndian);
+ PutUINT(getImage -> height, buffer + 14, bigEndian);
+
+ PutULONG(getImage -> plane_mask, buffer + 16, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GetImageStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GetImageMessage *getImage = (GetImageMessage *) message;
+
+ *logofs << name() << ": Identity format " << (unsigned) getImage -> format
+ << ", drawable " << getImage -> drawable << ", x " << getImage -> x
+ << ", y " << getImage -> y << ", width " << getImage -> width
+ << ", height " << getImage -> height << ", plane_mask "
+ << getImage -> plane_mask << ", size " << getImage -> size_
+ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void GetImageStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 8, 2);
+ md5_append(md5_state_, buffer + 10, 2);
+ md5_append(md5_state_, buffer + 12, 2);
+ md5_append(md5_state_, buffer + 14, 2);
+ md5_append(md5_state_, buffer + 16, 4);
+}
+
+void GetImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ GetImageMessage *getImage = (GetImageMessage *) message;
+ GetImageMessage *cachedGetImage = (GetImageMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << getImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(getImage -> drawable, clientCache -> drawableCache);
+
+ cachedGetImage -> drawable = getImage -> drawable;
+}
+
+void GetImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ GetImageMessage *getImage = (GetImageMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ getImage -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << getImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+}
+
diff --git a/nxcomp/src/GetImage.h b/nxcomp/src/GetImage.h
new file mode 100644
index 000000000..1c3558d14
--- /dev/null
+++ b/nxcomp/src/GetImage.h
@@ -0,0 +1,190 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef GetImage_H
+#define GetImage_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GETIMAGE_ENABLE_CACHE 1
+#define GETIMAGE_ENABLE_DATA 0
+#define GETIMAGE_ENABLE_SPLIT 0
+#define GETIMAGE_ENABLE_COMPRESS 0
+
+#define GETIMAGE_DATA_LIMIT 0
+#define GETIMAGE_DATA_OFFSET 20
+
+#define GETIMAGE_CACHE_SLOTS 200
+#define GETIMAGE_CACHE_THRESHOLD 1
+#define GETIMAGE_CACHE_LOWER_THRESHOLD 0
+
+//
+// The message class.
+//
+
+class GetImageMessage : public Message
+{
+ friend class GetImageStore;
+
+ public:
+
+ GetImageMessage()
+ {
+ }
+
+ ~GetImageMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char format;
+ unsigned int drawable;
+ unsigned short int x;
+ unsigned short int y;
+ unsigned short int width;
+ unsigned short int height;
+ unsigned int plane_mask;
+};
+
+class GetImageStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ GetImageStore() : MessageStore()
+ {
+ enableCache = GETIMAGE_ENABLE_CACHE;
+ enableData = GETIMAGE_ENABLE_DATA;
+ enableSplit = GETIMAGE_ENABLE_SPLIT;
+ enableCompress = GETIMAGE_ENABLE_COMPRESS;
+
+ dataLimit = GETIMAGE_DATA_LIMIT;
+ dataOffset = GETIMAGE_DATA_OFFSET;
+
+ cacheSlots = GETIMAGE_CACHE_SLOTS;
+ cacheThreshold = GETIMAGE_CACHE_THRESHOLD;
+ cacheLowerThreshold = GETIMAGE_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~GetImageStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "GetImage";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_GetImage;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GetImageMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new GetImageMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GetImageMessage((const GetImageMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GetImageMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GetImage_H */
diff --git a/nxcomp/src/GetImageReply.cpp b/nxcomp/src/GetImageReply.cpp
new file mode 100644
index 000000000..5100804ab
--- /dev/null
+++ b/nxcomp/src/GetImageReply.cpp
@@ -0,0 +1,194 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "GetImageReply.h"
+
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+GetImageReplyStore::GetImageReplyStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = GETIMAGEREPLY_ENABLE_CACHE;
+ enableData = GETIMAGEREPLY_ENABLE_DATA;
+ enableSplit = GETIMAGEREPLY_ENABLE_SPLIT;
+
+ // Since ProtoStep7 (#issue 108)
+ enableCompress = GETIMAGEREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = GETIMAGEREPLY_DATA_LIMIT;
+ dataOffset = GETIMAGEREPLY_DATA_OFFSET;
+
+ cacheSlots = GETIMAGEREPLY_CACHE_SLOTS;
+ cacheThreshold = GETIMAGEREPLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = GETIMAGEREPLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+GetImageReplyStore::~GetImageReplyStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GetImageReplyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ getImageReply -> depth = *(buffer + 1);
+
+ getImageReply -> visual = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetImageReplyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = getImageReply -> depth;
+
+ PutULONG(getImageReply -> visual, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GetImageReplyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message;
+
+ *logofs << name() << ": Identity depth " << (unsigned) getImageReply -> depth
+ << ", visual " << getImageReply -> visual << ", size "
+ << getImageReply -> size_ << ".\n";
+
+ #endif
+}
+
+void GetImageReplyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Field depth.
+ //
+
+ md5_append(md5_state_, buffer + 1, 1);
+}
+
+void GetImageReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message;
+
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << getImageReply -> visual
+ << " as visual field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(getImageReply -> visual, 29,
+ serverCache -> visualCache);
+}
+
+void GetImageReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ //
+ // Decode the variant part.
+ //
+
+ GetImageReplyMessage *getImageReply = (GetImageReplyMessage *) message;
+
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(getImageReply -> visual, 29,
+ serverCache -> visualCache);
+}
diff --git a/nxcomp/src/GetImageReply.h b/nxcomp/src/GetImageReply.h
new file mode 100644
index 000000000..d4f7c4267
--- /dev/null
+++ b/nxcomp/src/GetImageReply.h
@@ -0,0 +1,150 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef GetImageReply_H
+#define GetImageReply_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GETIMAGEREPLY_ENABLE_CACHE 1
+#define GETIMAGEREPLY_ENABLE_DATA 1
+#define GETIMAGEREPLY_ENABLE_SPLIT 0
+
+#define GETIMAGEREPLY_DATA_LIMIT 1048576 - 32
+#define GETIMAGEREPLY_DATA_OFFSET 32
+
+#define GETIMAGEREPLY_CACHE_SLOTS 1000
+#define GETIMAGEREPLY_CACHE_THRESHOLD 20
+#define GETIMAGEREPLY_CACHE_LOWER_THRESHOLD 2
+
+#define GETIMAGEREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class GetImageReplyMessage : public Message
+{
+ friend class GetImageReplyStore;
+
+ public:
+
+ GetImageReplyMessage()
+ {
+ }
+
+ ~GetImageReplyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char depth;
+ unsigned int visual;
+};
+
+class GetImageReplyStore : public MessageStore
+{
+ public:
+
+ GetImageReplyStore(StaticCompressor *compressor);
+
+ virtual ~GetImageReplyStore();
+
+ virtual const char *name() const
+ {
+ return "GetImageReply";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_GetImage;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GetImageReplyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new GetImageReplyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GetImageReplyMessage((const GetImageReplyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GetImageReplyMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GetImageReply_H */
diff --git a/nxcomp/src/GetProperty.cpp b/nxcomp/src/GetProperty.cpp
new file mode 100644
index 000000000..1c5e77ccc
--- /dev/null
+++ b/nxcomp/src/GetProperty.cpp
@@ -0,0 +1,119 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "GetProperty.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GetPropertyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetPropertyMessage *getProperty = (GetPropertyMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ getProperty -> property_delete = *(buffer + 1);
+
+ getProperty -> window = GetULONG(buffer + 4, bigEndian);
+ getProperty -> property = GetULONG(buffer + 8, bigEndian);
+ getProperty -> type = GetULONG(buffer + 12, bigEndian);
+ getProperty -> long_offset = GetULONG(buffer + 16, bigEndian);
+ getProperty -> long_length = GetULONG(buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetPropertyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+
+ GetPropertyMessage *getProperty = (GetPropertyMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = getProperty -> property_delete;
+
+ PutULONG(getProperty -> window, buffer + 4, bigEndian);
+ PutULONG(getProperty -> property, buffer + 8, bigEndian);
+ PutULONG(getProperty -> type, buffer + 12, bigEndian);
+ PutULONG(getProperty -> long_offset, buffer + 16, bigEndian);
+ PutULONG(getProperty -> long_length, buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GetPropertyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GetPropertyMessage *getProperty = (GetPropertyMessage *) message;
+
+ *logofs << name() << ": Identity property_delete " << (unsigned int) getProperty -> property_delete
+ << ", window " << getProperty -> window << ", property " << getProperty -> property
+ << ", type " << getProperty -> type << ", long-offset " << getProperty -> long_offset
+ << ", long-length " << getProperty -> long_length << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void GetPropertyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 4, 20);
+}
diff --git a/nxcomp/src/GetProperty.h b/nxcomp/src/GetProperty.h
new file mode 100644
index 000000000..46727280b
--- /dev/null
+++ b/nxcomp/src/GetProperty.h
@@ -0,0 +1,183 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef GetProperty_H
+#define GetProperty_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GETPROPERTY_ENABLE_CACHE 1
+#define GETPROPERTY_ENABLE_DATA 0
+#define GETPROPERTY_ENABLE_SPLIT 0
+#define GETPROPERTY_ENABLE_COMPRESS 0
+
+#define GETPROPERTY_DATA_LIMIT 0
+#define GETPROPERTY_DATA_OFFSET 24
+
+#define GETPROPERTY_CACHE_SLOTS 2000
+#define GETPROPERTY_CACHE_THRESHOLD 2
+#define GETPROPERTY_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class GetPropertyMessage : public Message
+{
+ friend class GetPropertyStore;
+
+ public:
+
+ GetPropertyMessage()
+ {
+ }
+
+ ~GetPropertyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char property_delete;
+ unsigned long window;
+ unsigned long property;
+ unsigned long type;
+ unsigned long long_offset;
+ unsigned long long_length;
+};
+
+class GetPropertyStore : public MessageStore
+{
+
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ GetPropertyStore() : MessageStore()
+ {
+ enableCache = GETPROPERTY_ENABLE_CACHE;
+ enableData = GETPROPERTY_ENABLE_DATA;
+ enableSplit = GETPROPERTY_ENABLE_SPLIT;
+ enableCompress = GETPROPERTY_ENABLE_COMPRESS;
+
+ dataLimit = GETPROPERTY_DATA_LIMIT;
+ dataOffset = GETPROPERTY_DATA_OFFSET;
+
+ cacheSlots = GETPROPERTY_CACHE_SLOTS;
+ cacheThreshold = GETPROPERTY_CACHE_THRESHOLD;
+ cacheLowerThreshold = GETPROPERTY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~GetPropertyStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "GetProperty";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_GetProperty;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GetPropertyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new GetPropertyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GetPropertyMessage((const GetPropertyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GetPropertyMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GetProperty_H */
diff --git a/nxcomp/src/GetPropertyReply.cpp b/nxcomp/src/GetPropertyReply.cpp
new file mode 100644
index 000000000..bf6879c5f
--- /dev/null
+++ b/nxcomp/src/GetPropertyReply.cpp
@@ -0,0 +1,304 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "GetPropertyReply.h"
+
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+GetPropertyReplyStore::GetPropertyReplyStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = GETPROPERTYREPLY_ENABLE_CACHE;
+ enableData = GETPROPERTYREPLY_ENABLE_DATA;
+ enableSplit = GETPROPERTYREPLY_ENABLE_SPLIT;
+
+ // Since ProtoStep7 (#issue 108)
+ enableCompress = GETPROPERTYREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = GETPROPERTYREPLY_DATA_LIMIT;
+ dataOffset = GETPROPERTYREPLY_DATA_OFFSET;
+
+ cacheSlots = GETPROPERTYREPLY_CACHE_SLOTS;
+ cacheThreshold = GETPROPERTYREPLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = GETPROPERTYREPLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+GetPropertyReplyStore::~GetPropertyReplyStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int GetPropertyReplyStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char format = (unsigned int) *(buffer + 1);
+
+ encodeBuffer.encodeCachedValue(format, 8,
+ serverCache -> getPropertyFormatCache);
+
+ unsigned int numBytes = GetULONG(buffer + 16, bigEndian);
+
+ encodeBuffer.encodeValue(numBytes, 32, 9);
+
+ if (format == 16)
+ {
+ numBytes <<= 1;
+ }
+ else if (format == 32)
+ {
+ numBytes <<= 2;
+ }
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 29,
+ serverCache -> getPropertyTypeCache, 9);
+
+ encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetPropertyReplyStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ServerCache *serverCache = (ServerCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char format;
+
+ decodeBuffer.decodeCachedValue(format, 8,
+ serverCache -> getPropertyFormatCache);
+
+ unsigned int length;
+
+ decodeBuffer.decodeValue(length, 32, 9);
+
+ unsigned int numBytes = length;
+
+ if (format == 16)
+ {
+ numBytes <<= 1;
+ }
+ else if (format == 32)
+ {
+ numBytes <<= 2;
+ }
+
+ size = 32 + RoundUp4(numBytes);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ *(buffer + 1) = format;
+
+ PutULONG(length, buffer + 16, bigEndian);
+
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ serverCache -> getPropertyTypeCache, 9);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetPropertyReplyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message;
+
+ getPropertyReply -> format = *(buffer + 1);
+
+ getPropertyReply -> type = GetULONG(buffer + 8, bigEndian);
+ getPropertyReply -> after = GetULONG(buffer + 12, bigEndian);
+ getPropertyReply -> items = GetULONG(buffer + 16, bigEndian);
+
+ //
+ // Cleanup the padding bytes.
+ //
+
+ unsigned int uiLengthInBytes;
+ unsigned int uiFormat;
+
+ if ((int) size > GETPROPERTYREPLY_DATA_OFFSET)
+ {
+ uiLengthInBytes = getPropertyReply -> items;
+
+ uiFormat = *(buffer + 1);
+
+ #ifdef DEBUG
+ *logofs << name() << ": length " << uiLengthInBytes
+ << ", format " << uiFormat << ", size "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ if (uiFormat == 16)
+ {
+ uiLengthInBytes <<= 1;
+ }
+ else if (uiFormat == 32)
+ {
+ uiLengthInBytes <<= 2;
+ }
+
+ unsigned char *end = ((unsigned char *) buffer) + size;
+ unsigned char *pad = ((unsigned char *) buffer) + GETPROPERTYREPLY_DATA_OFFSET + uiLengthInBytes;
+
+ CleanData((unsigned char *) pad, end - pad);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int GetPropertyReplyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message;
+
+ *(buffer + 1) = getPropertyReply -> format;
+
+ PutULONG(getPropertyReply -> type, buffer + 8, bigEndian);
+ PutULONG(getPropertyReply -> after, buffer + 12, bigEndian);
+ PutULONG(getPropertyReply -> items, buffer + 16, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void GetPropertyReplyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ GetPropertyReplyMessage *getPropertyReply = (GetPropertyReplyMessage *) message;
+
+ *logofs << name() << ": Identity format "
+ << (unsigned) getPropertyReply -> format << ", type "
+ << getPropertyReply -> type << ", after " << getPropertyReply -> after
+ << ", items " << getPropertyReply -> items << ", size "
+ << getPropertyReply -> size_ << ".\n";
+
+ #endif
+}
+
+void GetPropertyReplyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Fields format, type, after, items.
+ //
+
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 8, 12);
+}
+
+void GetPropertyReplyStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+}
+
+void GetPropertyReplyStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+}
diff --git a/nxcomp/src/GetPropertyReply.h b/nxcomp/src/GetPropertyReply.h
new file mode 100644
index 000000000..01634b408
--- /dev/null
+++ b/nxcomp/src/GetPropertyReply.h
@@ -0,0 +1,160 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef GetPropertyReply_H
+#define GetPropertyReply_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define GETPROPERTYREPLY_ENABLE_CACHE 1
+#define GETPROPERTYREPLY_ENABLE_DATA 1
+#define GETPROPERTYREPLY_ENABLE_SPLIT 0
+
+#define GETPROPERTYREPLY_DATA_LIMIT 1048576 - 32
+#define GETPROPERTYREPLY_DATA_OFFSET 32
+
+#define GETPROPERTYREPLY_CACHE_SLOTS 400
+#define GETPROPERTYREPLY_CACHE_THRESHOLD 5
+#define GETPROPERTYREPLY_CACHE_LOWER_THRESHOLD 1
+
+#define GETPROPERTYREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class GetPropertyReplyMessage : public Message
+{
+ friend class GetPropertyReplyStore;
+
+ public:
+
+ GetPropertyReplyMessage()
+ {
+ }
+
+ ~GetPropertyReplyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char format;
+ unsigned int type;
+ unsigned int after;
+ unsigned int items;
+};
+
+class GetPropertyReplyStore : public MessageStore
+{
+ public:
+
+ GetPropertyReplyStore(StaticCompressor *compressor);
+
+ virtual ~GetPropertyReplyStore();
+
+ virtual const char *name() const
+ {
+ return "GetPropertyReply";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_GetProperty;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(GetPropertyReplyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new GetPropertyReplyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new GetPropertyReplyMessage((const GetPropertyReplyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (GetPropertyReplyMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* GetPropertyReply_H */
diff --git a/nxcomp/src/ImageText16.cpp b/nxcomp/src/ImageText16.cpp
new file mode 100644
index 000000000..af057635f
--- /dev/null
+++ b/nxcomp/src/ImageText16.cpp
@@ -0,0 +1,231 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ImageText16.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ImageText16Store::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ImageText16Message *imageText16 = (ImageText16Message *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ imageText16 -> len = *(buffer + 1);
+
+ imageText16 -> drawable = GetULONG(buffer + 4, bigEndian);
+ imageText16 -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ imageText16 -> x = GetUINT(buffer + 12, bigEndian);
+ imageText16 -> y = GetUINT(buffer + 14, bigEndian);
+
+ if ((int) size > dataOffset)
+ {
+ int pad = (size - dataOffset) - (imageText16 -> len * 2);
+
+ if (pad > 0)
+ {
+ CleanData((unsigned char *) buffer + size - pad, pad);
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ImageText16Store::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ImageText16Message *imageText16 = (ImageText16Message *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = imageText16 -> len;
+
+ PutULONG(imageText16 -> drawable, buffer + 4, bigEndian);
+ PutULONG(imageText16 -> gcontext, buffer + 8, bigEndian);
+
+ PutUINT(imageText16 -> x, buffer + 12, bigEndian);
+ PutUINT(imageText16 -> y, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ImageText16Store::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ImageText16Message *imageText16 = (ImageText16Message *) message;
+
+ *logofs << name() << ": Identity len " << (unsigned int) imageText16 -> len
+ << " drawable " << imageText16 -> drawable << ", gcontext "
+ << imageText16 -> gcontext << ", x " << imageText16 -> x << ", y "
+ << imageText16 -> y << ", size " << imageText16 -> size_ << ".\n";
+
+ #endif
+}
+
+void ImageText16Store::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+}
+
+void ImageText16Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ ImageText16Message *imageText16 = (ImageText16Message *) message;
+ ImageText16Message *cachedImageText16 = (ImageText16Message *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText16 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(imageText16 -> drawable, clientCache -> drawableCache);
+
+ cachedImageText16 -> drawable = imageText16 -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText16 -> gcontext
+ << " as " << "gcontext" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(imageText16 -> gcontext, clientCache -> gcCache);
+
+ cachedImageText16 -> gcontext = imageText16 -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText16 -> x
+ << " as " << "x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = imageText16 -> x - cachedImageText16 -> x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> imageTextCacheX);
+
+ cachedImageText16 -> x = imageText16 -> x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText16 -> y
+ << " as " << "y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = imageText16 -> y - cachedImageText16 -> y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> imageTextCacheY);
+
+ cachedImageText16 -> y = imageText16 -> y;
+}
+
+void ImageText16Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ ImageText16Message *imageText16 = (ImageText16Message *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ imageText16 -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText16 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ imageText16 -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText16 -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> imageTextCacheX);
+
+ imageText16 -> x += value;
+ imageText16 -> x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText16 -> x
+ << " as x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> imageTextCacheY);
+
+ imageText16 -> y += value;
+ imageText16 -> y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText16 -> y
+ << " as y field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/src/ImageText16.h b/nxcomp/src/ImageText16.h
new file mode 100644
index 000000000..98462ab35
--- /dev/null
+++ b/nxcomp/src/ImageText16.h
@@ -0,0 +1,190 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ImageText16_H
+#define ImageText16_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define IMAGETEXT16_ENABLE_CACHE 1
+#define IMAGETEXT16_ENABLE_DATA 0
+#define IMAGETEXT16_ENABLE_SPLIT 0
+#define IMAGETEXT16_ENABLE_COMPRESS 0
+
+#define IMAGETEXT16_DATA_LIMIT 512
+#define IMAGETEXT16_DATA_OFFSET 16
+
+#define IMAGETEXT16_CACHE_SLOTS 3000
+#define IMAGETEXT16_CACHE_THRESHOLD 5
+#define IMAGETEXT16_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ImageText16Message : public Message
+{
+ friend class ImageText16Store;
+
+ public:
+
+ ImageText16Message()
+ {
+ }
+
+ ~ImageText16Message()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char len;
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned short x;
+ unsigned short y;
+};
+
+class ImageText16Store : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ImageText16Store() : MessageStore()
+ {
+ enableCache = IMAGETEXT16_ENABLE_CACHE;
+ enableData = IMAGETEXT16_ENABLE_DATA;
+ enableSplit = IMAGETEXT16_ENABLE_SPLIT;
+ enableCompress = IMAGETEXT16_ENABLE_COMPRESS;
+
+ dataLimit = IMAGETEXT16_DATA_LIMIT;
+ dataOffset = IMAGETEXT16_DATA_OFFSET;
+
+ cacheSlots = IMAGETEXT16_CACHE_SLOTS;
+ cacheThreshold = IMAGETEXT16_CACHE_THRESHOLD;
+ cacheLowerThreshold = IMAGETEXT16_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ImageText16Store()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ImageText16";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ImageText16;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ImageText16Message);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ImageText16Message();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ImageText16Message((const ImageText16Message &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ImageText16Message *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ImageText16_H */
diff --git a/nxcomp/src/ImageText8.cpp b/nxcomp/src/ImageText8.cpp
new file mode 100644
index 000000000..61fcef825
--- /dev/null
+++ b/nxcomp/src/ImageText8.cpp
@@ -0,0 +1,231 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ImageText8.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ImageText8Store::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ImageText8Message *imageText8 = (ImageText8Message *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ imageText8 -> len = *(buffer + 1);
+
+ imageText8 -> drawable = GetULONG(buffer + 4, bigEndian);
+ imageText8 -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ imageText8 -> x = GetUINT(buffer + 12, bigEndian);
+ imageText8 -> y = GetUINT(buffer + 14, bigEndian);
+
+ if ((int) size > dataOffset)
+ {
+ int pad = (size - dataOffset) - imageText8 -> len;
+
+ if (pad > 0)
+ {
+ CleanData((unsigned char *) buffer + size - pad, pad);
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ImageText8Store::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ImageText8Message *imageText8 = (ImageText8Message *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = imageText8 -> len;
+
+ PutULONG(imageText8 -> drawable, buffer + 4, bigEndian);
+ PutULONG(imageText8 -> gcontext, buffer + 8, bigEndian);
+
+ PutUINT(imageText8 -> x, buffer + 12, bigEndian);
+ PutUINT(imageText8 -> y, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ImageText8Store::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ImageText8Message *imageText8 = (ImageText8Message *) message;
+
+ *logofs << name() << ": Identity len " << (unsigned int) imageText8 -> len
+ << " drawable " << imageText8 -> drawable << ", gcontext "
+ << imageText8 -> gcontext << ", x " << imageText8 -> x << ", y "
+ << imageText8 -> y << ", size " << imageText8 -> size_ << ".\n";
+
+ #endif
+}
+
+void ImageText8Store::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+}
+
+void ImageText8Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ ImageText8Message *imageText8 = (ImageText8Message *) message;
+ ImageText8Message *cachedImageText8 = (ImageText8Message *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText8 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(imageText8 -> drawable, clientCache -> drawableCache);
+
+ cachedImageText8 -> drawable = imageText8 -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText8 -> gcontext
+ << " as " << "gcontext" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(imageText8 -> gcontext, clientCache -> gcCache);
+
+ cachedImageText8 -> gcontext = imageText8 -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText8 -> x
+ << " as " << "x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = imageText8 -> x - cachedImageText8 -> x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> imageTextCacheX);
+
+ cachedImageText8 -> x = imageText8 -> x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << imageText8 -> y
+ << " as " << "y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = imageText8 -> y - cachedImageText8 -> y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> imageTextCacheY);
+
+ cachedImageText8 -> y = imageText8 -> y;
+}
+
+void ImageText8Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ ImageText8Message *imageText8 = (ImageText8Message *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ imageText8 -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText8 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ imageText8 -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText8 -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> imageTextCacheX);
+
+ imageText8 -> x += value;
+ imageText8 -> x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText8 -> x
+ << " as x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> imageTextCacheY);
+
+ imageText8 -> y += value;
+ imageText8 -> y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << imageText8 -> y
+ << " as y field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/src/ImageText8.h b/nxcomp/src/ImageText8.h
new file mode 100644
index 000000000..aa9ccb5d9
--- /dev/null
+++ b/nxcomp/src/ImageText8.h
@@ -0,0 +1,190 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ImageText8_H
+#define ImageText8_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define IMAGETEXT8_ENABLE_CACHE 1
+#define IMAGETEXT8_ENABLE_DATA 0
+#define IMAGETEXT8_ENABLE_SPLIT 0
+#define IMAGETEXT8_ENABLE_COMPRESS 0
+
+#define IMAGETEXT8_DATA_LIMIT 256
+#define IMAGETEXT8_DATA_OFFSET 16
+
+#define IMAGETEXT8_CACHE_SLOTS 3000
+#define IMAGETEXT8_CACHE_THRESHOLD 5
+#define IMAGETEXT8_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class ImageText8Message : public Message
+{
+ friend class ImageText8Store;
+
+ public:
+
+ ImageText8Message()
+ {
+ }
+
+ ~ImageText8Message()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char len;
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned short x;
+ unsigned short y;
+};
+
+class ImageText8Store : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ImageText8Store() : MessageStore()
+ {
+ enableCache = IMAGETEXT8_ENABLE_CACHE;
+ enableData = IMAGETEXT8_ENABLE_DATA;
+ enableSplit = IMAGETEXT8_ENABLE_SPLIT;
+ enableCompress = IMAGETEXT8_ENABLE_COMPRESS;
+
+ dataLimit = IMAGETEXT8_DATA_LIMIT;
+ dataOffset = IMAGETEXT8_DATA_OFFSET;
+
+ cacheSlots = IMAGETEXT8_CACHE_SLOTS;
+ cacheThreshold = IMAGETEXT8_CACHE_THRESHOLD;
+ cacheLowerThreshold = IMAGETEXT8_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~ImageText8Store()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "ImageText8";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ImageText8;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ImageText8Message);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ImageText8Message();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ImageText8Message((const ImageText8Message &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ImageText8Message *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ImageText8_H */
diff --git a/nxcomp/src/IntCache.cpp b/nxcomp/src/IntCache.cpp
new file mode 100644
index 000000000..c72b81200
--- /dev/null
+++ b/nxcomp/src/IntCache.cpp
@@ -0,0 +1,230 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "Misc.h"
+#include "IntCache.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+IntCache::IntCache(unsigned int size)
+
+ : size_(size), length_(0), buffer_(new unsigned int[size]),
+ lastDiff_(0), lastValueInserted_(0), predictedBlockSize_(0)
+{
+}
+
+int IntCache::lookup(unsigned int &value, unsigned int &index,
+ unsigned int mask, unsigned int &sameDiff)
+{
+ for (unsigned int i = 0; i < length_; i++)
+ {
+ if (value == buffer_[i])
+ {
+ index = i;
+ if (i)
+ {
+ unsigned int target = (i >> 1);
+ do
+ {
+ buffer_[i] = buffer_[i - 1];
+ i--;
+ }
+ while (i > target);
+ buffer_[target] = value;
+ }
+ return 1;
+ }
+ }
+ unsigned int insertionPoint;
+ if (2 >= length_)
+ insertionPoint = length_;
+ else
+ insertionPoint = 2;
+ unsigned int start;
+ if (length_ >= size_)
+ start = size_ - 1;
+ else
+ {
+ start = length_;
+ length_++;
+ }
+ for (unsigned int k = start; k > insertionPoint; k--)
+ buffer_[k] = buffer_[k - 1];
+ buffer_[insertionPoint] = value;
+ unsigned int diff = value - lastValueInserted_;
+
+ lastValueInserted_ = (value & mask);
+ value = (diff & mask);
+ sameDiff = (value == lastDiff_);
+ if (!sameDiff)
+ {
+ lastDiff_ = value;
+
+ unsigned int lastChangeIndex = 0;
+ unsigned int lastBitIsOne = (lastDiff_ & 0x1);
+ unsigned int j = 1;
+ for (unsigned int nextMask = 0x2; nextMask & mask; nextMask <<= 1)
+ {
+ unsigned int nextBitIsOne = (lastDiff_ & nextMask);
+ if (nextBitIsOne)
+ {
+ if (!lastBitIsOne)
+ {
+ lastChangeIndex = j;
+ lastBitIsOne = nextBitIsOne;
+ }
+ }
+ else
+ {
+ if (lastBitIsOne)
+ {
+ lastChangeIndex = j;
+ lastBitIsOne = nextBitIsOne;
+ }
+ }
+ j++;
+ }
+ predictedBlockSize_ = lastChangeIndex + 1;
+ if (predictedBlockSize_ < 2)
+ predictedBlockSize_ = 2;
+ }
+ return 0;
+}
+
+void IntCache::insert(unsigned int &value, unsigned int mask)
+{
+ unsigned int insertionPoint;
+ if (2 >= length_)
+ insertionPoint = length_;
+ else
+ insertionPoint = 2;
+ unsigned int start;
+ if (length_ >= size_)
+ start = size_ - 1;
+ else
+ {
+ start = length_;
+ length_++;
+ }
+ for (unsigned int k = start; k > insertionPoint; k--)
+ buffer_[k] = buffer_[k - 1];
+ if (lastDiff_ != value)
+ {
+ lastDiff_ = value;
+ unsigned int lastChangeIndex = 0;
+ unsigned int lastBitIsOne = (lastDiff_ & 0x1);
+ unsigned int j = 1;
+ for (unsigned int nextMask = 0x2; nextMask & mask; nextMask <<= 1)
+ {
+ unsigned int nextBitIsOne = (lastDiff_ & nextMask);
+ if (nextBitIsOne)
+ {
+ if (!lastBitIsOne)
+ {
+ lastChangeIndex = j;
+ lastBitIsOne = nextBitIsOne;
+ }
+ }
+ else
+ {
+ if (lastBitIsOne)
+ {
+ lastChangeIndex = j;
+ lastBitIsOne = nextBitIsOne;
+ }
+ }
+ j++;
+ }
+ predictedBlockSize_ = lastChangeIndex + 1;
+ if (predictedBlockSize_ < 2)
+ predictedBlockSize_ = 2;
+ }
+ lastValueInserted_ += value;
+ lastValueInserted_ &= mask;
+ buffer_[insertionPoint] = lastValueInserted_;
+ value = lastValueInserted_;
+}
+
+void IntCache::push(unsigned int &value, unsigned int mask)
+{
+ //
+ // Using a memmove() appears to be slower.
+ //
+ // memmove((char *) &buffer_[1], (char *) &buffer_[0],
+ // sizeof(unsigned int) * (size_ - 1));
+ //
+ // if (length_ < size_)
+ // {
+ // length_++;
+ // }
+ //
+
+ unsigned int start;
+
+ if (length_ >= size_)
+ {
+ start = size_ - 1;
+ }
+ else
+ {
+ start = length_;
+
+ length_++;
+ }
+
+ for (unsigned int k = start; k > 0; k--)
+ {
+ buffer_[k] = buffer_[k - 1];
+ }
+
+ value &= mask;
+
+ buffer_[0] = value;
+}
+
+void IntCache::dump()
+{
+ #ifdef DUMP
+
+ *logofs << "IntCache: Dumping content of cache at "
+ << (void *) this << ":\n" << logofs_flush;
+
+ for (unsigned int i = 0; i < length_; i++)
+ {
+ *logofs << "IntCache: [" << i << "][" << buffer_[i] << "]\n";
+ }
+
+ #endif
+}
diff --git a/nxcomp/src/IntCache.h b/nxcomp/src/IntCache.h
new file mode 100644
index 000000000..69c522325
--- /dev/null
+++ b/nxcomp/src/IntCache.h
@@ -0,0 +1,119 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef IntCache_H
+#define IntCache_H
+
+class IntCache
+{
+ public:
+
+ IntCache(unsigned int size);
+
+ ~IntCache()
+ {
+ delete [] buffer_;
+ }
+
+ unsigned int getSize() const
+ {
+ return length_;
+ }
+
+ int lookup(unsigned int &value, unsigned int &index,
+ unsigned int mask, unsigned int &sameDiff);
+
+ //
+ // This can be inlined as it is only
+ // called by decodeCachedValue().
+ //
+
+ unsigned int get(unsigned int index)
+ {
+ unsigned int result = buffer_[index];
+
+ if (index != 0)
+ {
+ //
+ // Using a memmove() appears to be slower.
+ //
+ // unsigned int target = index >> 1;
+ //
+ // memmove((char *) &buffer_[target + 1], (char *) &buffer_[target],
+ // sizeof(unsigned int) * (index - target));
+ //
+ // buffer_[target] = result;
+ //
+
+ unsigned int i = index;
+
+ unsigned int target = (i >> 1);
+
+ do
+ {
+ buffer_[i] = buffer_[i - 1];
+
+ i--;
+ }
+ while (i > target);
+
+ buffer_[target] = result;
+ }
+
+ return result;
+ }
+
+ void insert(unsigned int &value, unsigned int mask);
+
+ void push(unsigned int &value, unsigned int mask);
+
+ void dump();
+
+ unsigned int getLastDiff(unsigned int mask) const
+ {
+ return lastDiff_;
+ }
+
+ unsigned int getBlockSize(unsigned int bits) const
+ {
+ if (bits > 0)
+ {
+ return (predictedBlockSize_ < bits ? predictedBlockSize_ : bits);
+ }
+
+ return predictedBlockSize_;
+ }
+
+ private:
+
+ unsigned int size_;
+ unsigned int length_;
+ unsigned int *buffer_;
+ unsigned int lastDiff_;
+ unsigned int lastValueInserted_;
+ unsigned int predictedBlockSize_;
+};
+
+#endif /* IntCache_H */
diff --git a/nxcomp/src/InternAtom.cpp b/nxcomp/src/InternAtom.cpp
new file mode 100644
index 000000000..9d0ab1b6d
--- /dev/null
+++ b/nxcomp/src/InternAtom.cpp
@@ -0,0 +1,131 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "InternAtom.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int InternAtomStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ InternAtomMessage *internAtom = (InternAtomMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ internAtom -> only_if_exists = *(buffer + 1);
+ internAtom -> name_length = GetUINT(buffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Clean up padding bytes.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ unsigned char *end = ((unsigned char *) buffer) + size;
+
+ for (unsigned char *pad = ((unsigned char *) buffer) + 8 +
+ internAtom -> name_length; pad < end; pad++)
+ {
+ *pad = 0;
+ }
+ }
+
+ return 1;
+}
+
+int InternAtomStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ InternAtomMessage *internAtom = (InternAtomMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = internAtom -> only_if_exists;
+
+ PutUINT(internAtom -> name_length, buffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void InternAtomStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ InternAtomMessage *internAtom = (InternAtomMessage *) message;
+
+ *logofs << name() << ": Identity only_if_exists "
+ << (unsigned int) internAtom -> only_if_exists
+ << ", name_length " << internAtom -> name_length
+ << ", name '";
+
+ for (int i = 0; i < internAtom -> name_length; i++)
+ {
+ *logofs << internAtom -> data_[i];
+ }
+
+ *logofs << "'.\n" << logofs_flush;
+
+ #endif
+}
+
+void InternAtomStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 4, 2);
+}
diff --git a/nxcomp/src/InternAtom.h b/nxcomp/src/InternAtom.h
new file mode 100644
index 000000000..6e69eca24
--- /dev/null
+++ b/nxcomp/src/InternAtom.h
@@ -0,0 +1,178 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef InternAtom_H
+#define InternAtom_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define INTERNATOM_ENABLE_CACHE 1
+#define INTERNATOM_ENABLE_DATA 0
+#define INTERNATOM_ENABLE_SPLIT 0
+#define INTERNATOM_ENABLE_COMPRESS 0
+
+#define INTERNATOM_DATA_LIMIT 80
+#define INTERNATOM_DATA_OFFSET 8
+
+#define INTERNATOM_CACHE_SLOTS 2000
+#define INTERNATOM_CACHE_THRESHOLD 2
+#define INTERNATOM_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class InternAtomMessage : public Message
+{
+ friend class InternAtomStore;
+
+ public:
+
+ InternAtomMessage()
+ {
+ }
+
+ ~InternAtomMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char only_if_exists;
+ unsigned short name_length;
+};
+
+class InternAtomStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ InternAtomStore() : MessageStore()
+ {
+ enableCache = INTERNATOM_ENABLE_CACHE;
+ enableData = INTERNATOM_ENABLE_DATA;
+ enableSplit = INTERNATOM_ENABLE_SPLIT;
+ enableCompress = INTERNATOM_ENABLE_COMPRESS;
+
+ dataLimit = INTERNATOM_DATA_LIMIT;
+ dataOffset = INTERNATOM_DATA_OFFSET;
+
+ cacheSlots = INTERNATOM_CACHE_SLOTS;
+ cacheThreshold = INTERNATOM_CACHE_THRESHOLD;
+ cacheLowerThreshold = INTERNATOM_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~InternAtomStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "InternAtom";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_InternAtom;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(InternAtomMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new InternAtomMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new InternAtomMessage((const InternAtomMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (InternAtomMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* InternAtom_H */
diff --git a/nxcomp/src/Jpeg.cpp b/nxcomp/src/Jpeg.cpp
new file mode 100644
index 000000000..2e8044049
--- /dev/null
+++ b/nxcomp/src/Jpeg.cpp
@@ -0,0 +1,890 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <X11/Xmd.h>
+
+#ifdef ANDROID
+#include <strings.h>
+#endif
+#include <unistd.h>
+#include <setjmp.h>
+#include <zlib.h>
+
+#ifdef __cplusplus
+
+extern "C"
+{
+ #include <stdio.h>
+ #include <jpeglib.h>
+}
+
+#else
+
+#include <stdio.h>
+#include <jpeglib.h>
+
+#endif
+
+#include "Misc.h"
+#include "Jpeg.h"
+#include "Unpack.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#define RGB24_TO_PIXEL(bpp,r,g,b) \
+ ((((CARD##bpp)(r) & 0xff) * srcRedMax + 127) / 255 \
+ << srcRedShift | \
+ (((CARD##bpp)(g) & 0xff) * srcGreenMax + 127) / 255 \
+ << srcGreenShift | \
+ (((CARD##bpp)(b) & 0xff) * srcBlueMax + 127) / 255 \
+ << srcBlueShift)
+
+#define RGB24_TO_PIXEL32(r,g,b) \
+ (((CARD32)(r) & 0xff) << srcRedShift | \
+ ((CARD32)(g) & 0xff) << srcGreenShift | \
+ ((CARD32)(b) & 0xff) << srcBlueShift)
+
+//
+// Functions from Unpack.cpp
+//
+
+extern int Unpack32To32(const T_colormask *colormask, const unsigned int *data,
+ unsigned int *out, unsigned int *end);
+
+extern int Unpack24To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+extern int Unpack16To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+//
+// Local functions used for the jpeg decompression.
+//
+
+static void JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData, int compressedLen);
+static void JpegInitSource(j_decompress_ptr cinfo);
+static void JpegTermSource(j_decompress_ptr cinfo);
+static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes);
+
+static boolean JpegFillInputBuffer(j_decompress_ptr cinfo);
+
+static int DecompressJpeg16(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder);
+
+static int DecompressJpeg24(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder);
+
+static int DecompressJpeg32(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder);
+
+void UnpackJpegErrorHandler(j_common_ptr cinfo) __attribute__((noreturn));
+
+//
+// Colormap stuff.
+//
+
+CARD16 srcRedMax, srcGreenMax, srcBlueMax;
+CARD8 srcRedShift, srcGreenShift, srcBlueShift;
+
+//
+// Error handler.
+//
+
+static bool jpegError;
+
+jmp_buf UnpackJpegContext;
+
+void UnpackJpegErrorHandler(j_common_ptr cinfo)
+{
+ #ifdef PANIC
+ *logofs << "UnpackJpegErrorHandler: PANIC! Detected error in JPEG decompression.\n"
+ << logofs_flush;
+
+ *logofs << "UnpackJpegErrorHandler: PANIC! Trying to revert to the previous context.\n"
+ << logofs_flush;
+ #endif
+
+ jpegError = 1;
+
+ longjmp(UnpackJpegContext, 1);
+}
+
+//
+// Attributes used for the jpeg decompression.
+//
+
+static struct jpeg_source_mgr jpegSrcManager;
+static JOCTET *jpegBufferPtr;
+static size_t jpegBufferLen;
+
+static char *tmpBuf;
+static int tmpBufSize = 0;
+
+int UnpackJpeg(T_geometry *geometry, unsigned char method, unsigned char *srcData,
+ int srcSize, int dstBpp, int dstWidth, int dstHeight,
+ unsigned char *dstData, int dstSize)
+{
+ int byteOrder = geometry -> image_byte_order;
+
+ //
+ // Check if data is coming from a failed unsplit.
+ //
+
+ if (srcSize < 2 || (srcData[0] == SPLIT_PATTERN &&
+ srcData[1] == SPLIT_PATTERN))
+ {
+ #ifdef WARNING
+ *logofs << "UnpackJpeg: WARNING! Skipping unpack of dummy data.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "UnpackJpeg: Decompression. Source size "
+ << srcSize << " bits per plane " << dstBpp
+ << ".\n" << logofs_flush;
+ #endif
+
+ srcRedShift = ffs(geometry -> red_mask) - 1;
+ srcGreenShift = ffs(geometry -> green_mask) - 1;
+ srcBlueShift = ffs(geometry -> blue_mask) - 1;
+
+ #ifdef DEBUG
+ *logofs << "UnpackJpeg: Red shift " << (int) srcRedShift
+ << " green shift " << (int) srcGreenShift << " blue shift "
+ << (int) srcBlueShift << ".\n" << logofs_flush;
+ #endif
+
+ srcRedMax = geometry -> red_mask >> srcRedShift;
+ srcGreenMax = geometry -> green_mask >> srcGreenShift;
+ srcBlueMax = geometry -> blue_mask >> srcBlueShift;
+
+ #ifdef DEBUG
+ *logofs << "UnpackJpeg: Red mask " << (void *) geometry -> red_mask
+ << " green mask " << (void *) geometry -> green_mask
+ << " blue mask " << (void *) geometry -> blue_mask
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "UnpackJpeg: Red max " << srcRedMax << " green max "
+ << srcGreenMax << " blue max " << srcBlueMax
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Make enough space in the temporary
+ // buffer to have one complete row of
+ // the image with 3 bytes for a pixel.
+ //
+
+ tmpBufSize = dstWidth * 3;
+ tmpBuf = new char[tmpBufSize];
+
+ if (tmpBuf == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackJpeg: PANIC! Cannot allocate "
+ << dstWidth * 3 << " bytes for Jpeg "
+ << "decompressed data.\n" << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ return -1;
+ }
+
+ int result = 1;
+
+ switch(dstBpp)
+ {
+ case 8:
+ {
+ //
+ // Simply move the data from srcData to dstData
+ // taking into consideration the correct padding.
+ //
+
+ int row;
+
+ unsigned char * dstBuff = dstData;
+ unsigned char * srcBuff = srcData;
+
+ for (row = 0; row < dstHeight; row++)
+ {
+ memcpy(dstBuff, srcBuff, dstWidth);
+
+ dstBuff += RoundUp4(dstWidth);
+ srcBuff += dstWidth;
+ }
+
+ break;
+ }
+ case 16:
+ {
+ result = DecompressJpeg16(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ case 24:
+ {
+ result = DecompressJpeg24(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ case 32:
+ {
+ result = DecompressJpeg32(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "UnpackJpeg: PANIC! Failed to decode Jpeg image. "
+ << " Unsupported Bpp value " << dstBpp
+ << " for the Jpeg compression"
+ << ".\n" << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ result = -1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << "UnpackJpeg: Decompression finished with result "
+ << result << ".\n" << logofs_flush;
+ #endif
+
+ if (result == -1)
+ {
+ delete [] tmpBuf;
+
+ #ifdef PANIC
+ *logofs << "UnpackJpeg: PANIC! Failed to decode Jpeg image using "
+ << dstBpp << " Bpp destination.\n" << logofs_flush;
+ #endif
+
+ return result;
+ }
+
+ //
+ // Apply the correction for the brightness.
+ //
+
+ int maskMethod;
+
+ switch(method)
+ {
+ case PACK_JPEG_8_COLORS:
+ {
+ maskMethod = MASK_8_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_64_COLORS:
+ {
+ maskMethod = MASK_64_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_256_COLORS:
+ {
+ maskMethod = MASK_256_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_512_COLORS:
+ {
+ maskMethod = MASK_512_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_4K_COLORS:
+ {
+ maskMethod = MASK_4K_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_32K_COLORS:
+ {
+ maskMethod = MASK_32K_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_64K_COLORS:
+ {
+ maskMethod = MASK_64K_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_256K_COLORS:
+ {
+ maskMethod = MASK_256K_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_2M_COLORS:
+ {
+ maskMethod = MASK_2M_COLORS;
+
+ break;
+ }
+ case PACK_JPEG_16M_COLORS:
+ {
+ maskMethod = MASK_16M_COLORS;
+
+ break;
+ }
+ default:
+ {
+ delete [] tmpBuf;
+
+ return -1;
+ }
+ }
+
+ const T_colormask *colorMask = MethodColorMask(maskMethod);
+
+ unsigned char *dstBuff = dstData;
+
+ switch (dstBpp)
+ {
+ case 16:
+ {
+ Unpack16To16(colorMask, dstBuff, dstBuff, dstBuff + dstSize);
+
+ break;
+ }
+ case 24:
+ {
+ break;
+ }
+ case 32:
+ {
+ Unpack32To32(colorMask, (unsigned int *) dstBuff, (unsigned int *) dstBuff,
+ (unsigned int *) (dstBuff + dstSize));
+ break;
+ }
+ default:
+ {
+ delete [] tmpBuf;
+
+ return -1;
+ }
+ }
+
+ delete [] tmpBuf;
+
+ return 1;
+}
+
+//
+// Functions that actually do the Jpeg decompression.
+//
+
+int DecompressJpeg16(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ unsigned char *data;
+ JSAMPROW rowPointer[1];
+
+ unsigned int dx = 0;
+ unsigned int dy = 0;
+
+ #ifdef DEBUG
+ *logofs << "DecompressJpeg16: Decompressing with length "
+ << compressedLen << " width " << w << " height "
+ << h << ".\n" << logofs_flush;
+ #endif
+
+ jpegError = 0;
+
+ cinfo.err = jpeg_std_error(&jerr);
+
+ jerr.error_exit = UnpackJpegErrorHandler;
+
+ if (setjmp(UnpackJpegContext) == 1)
+ {
+ #ifdef TEST
+ *logofs << "DecompressJpeg16: Out of the long jump with error '"
+ << jpegError << "'.\n" << logofs_flush;
+ #endif
+
+ goto AbortDecompressJpeg16;
+ }
+
+ jpeg_create_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg16;
+
+ JpegSetSrcManager(&cinfo, compressedData, compressedLen);
+
+ jpeg_read_header(&cinfo, TRUE);
+
+ if (jpegError) goto AbortDecompressJpeg16;
+
+ cinfo.out_color_space = JCS_RGB;
+
+ jpeg_start_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg16;
+
+ if (cinfo.output_width != w ||
+ cinfo.output_height != h ||
+ cinfo.output_components != 3)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg16: PANIC! Wrong JPEG data received.\n"
+ << logofs_flush;
+ #endif
+
+ jpeg_destroy_decompress(&cinfo);
+
+ return -1;
+ }
+
+ //
+ // PixelPtr points to dstBuf which is
+ // already padded correctly for the final
+ // image to put
+ //
+
+ data = dstBuf;
+
+ rowPointer[0] = (JSAMPROW) tmpBuf;
+
+ unsigned long pixel;
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, rowPointer, 1);
+
+ if (jpegError) goto AbortDecompressJpeg16;
+
+ for (dx = 0; dx < w; dx++)
+ {
+ pixel = RGB24_TO_PIXEL(16, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1],
+ tmpBuf[dx * 3 + 2]);
+
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ data[0] = (unsigned char) (pixel & 0xff);
+ data[1] = (unsigned char) ((pixel >> 8) & 0xff);
+ }
+ else
+ {
+ data[1] = (unsigned char) (pixel & 0xff);
+ data[0] = (unsigned char) ((pixel >> 8) & 0xff);
+ }
+
+ data += 2;
+ }
+
+ //
+ // Move data at the beginning of the
+ // next line.
+ //
+
+ data = data + (RoundUp4(w * 2) - w * 2);
+
+ dy++;
+ }
+
+ AbortDecompressJpeg16:
+
+ if (jpegError == 0)
+ {
+ jpeg_finish_decompress(&cinfo);
+ }
+
+ jpeg_destroy_decompress(&cinfo);
+
+ if (jpegError == 1)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg16: Failed to decompress JPEG image.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "DecompressJpeg16: Decompression finished with "
+ << dy << " lines handled.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int DecompressJpeg24(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ CARD8 *pixelPtr = NULL;
+ JSAMPROW rowPointer[1];
+
+ unsigned int dx = 0;
+ unsigned int dy = 0;
+
+ #ifdef TEST
+ *logofs << "DecompressJpeg24: Decompressing with length "
+ << compressedLen << " width " << w << " height "
+ << h << ".\n" << logofs_flush;
+ #endif
+
+ jpegError = 0;
+
+ cinfo.err = jpeg_std_error(&jerr);
+
+ jerr.error_exit = UnpackJpegErrorHandler;
+
+ if (setjmp(UnpackJpegContext) == 1)
+ {
+ #ifdef TEST
+ *logofs << "DecompressJpeg24: Out of the long jump with error '"
+ << jpegError << "'.\n" << logofs_flush;
+ #endif
+
+ goto AbortDecompressJpeg24;
+ }
+
+ jpeg_create_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg24;
+
+ JpegSetSrcManager(&cinfo, compressedData, compressedLen);
+
+ jpeg_read_header(&cinfo, TRUE);
+
+ if (jpegError) goto AbortDecompressJpeg24;
+
+ cinfo.out_color_space = JCS_RGB;
+
+ jpeg_start_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg24;
+
+ if (cinfo.output_width != w ||
+ cinfo.output_height != h ||
+ cinfo.output_components != 3)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg24: PANIC! Wrong JPEG data received.\n"
+ << logofs_flush;
+ #endif
+
+ jpeg_destroy_decompress(&cinfo);
+
+ return -1;
+ }
+
+ //
+ // PixelPtr points to dstBuf which is
+ // already padded correctly for the final
+ // image to put.
+ //
+
+ pixelPtr = (CARD8 *) dstBuf;
+
+ rowPointer[0] = (JSAMPROW) tmpBuf;
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, rowPointer, 1);
+
+ if (jpegError) goto AbortDecompressJpeg24;
+
+ for (dx = 0; dx < w; dx++)
+ {
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ pixelPtr[0] = tmpBuf[dx * 3];
+ pixelPtr[1] = tmpBuf[dx * 3 + 1];
+ pixelPtr[2] = tmpBuf[dx * 3 + 2];
+ }
+ else
+ {
+ pixelPtr[2] = tmpBuf[dx * 3];
+ pixelPtr[1] = tmpBuf[dx * 3 + 1];
+ pixelPtr[0] = tmpBuf[dx * 3 + 2];
+ }
+
+ pixelPtr += 3;
+ }
+
+ //
+ // Go to the next line.
+ //
+
+ pixelPtr = (CARD8 *) (((char *) pixelPtr) + (RoundUp4(w * 3) - w * 3));
+
+ dy++;
+ }
+
+ AbortDecompressJpeg24:
+
+ if (jpegError == 0)
+ {
+ jpeg_finish_decompress(&cinfo);
+ }
+
+ jpeg_destroy_decompress(&cinfo);
+
+ if (jpegError == 1)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg24: Failed to decompress JPEG image.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "DecompressJpeg24: Decompression finished with "
+ << dy << " lines handled.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int DecompressJpeg32(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ unsigned char *data;
+ JSAMPROW rowPointer[1];
+
+ unsigned int dx = 0;
+ unsigned int dy = 0;
+
+ #ifdef TEST
+ *logofs << "DecompressJpeg32: Decompressing with length "
+ << compressedLen << " width " << w << " height "
+ << h << ".\n" << logofs_flush;
+ #endif
+
+ jpegError = 0;
+
+ cinfo.err = jpeg_std_error(&jerr);
+
+ jerr.error_exit = UnpackJpegErrorHandler;
+
+ if (setjmp(UnpackJpegContext) == 1)
+ {
+ #ifdef TEST
+ *logofs << "DecompressJpeg32: Out of the long jump with error '"
+ << jpegError << "'.\n" << logofs_flush;
+ #endif
+
+ goto AbortDecompressJpeg32;
+ }
+
+ jpeg_create_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg32;
+
+ JpegSetSrcManager(&cinfo, compressedData, compressedLen);
+
+ jpeg_read_header(&cinfo, TRUE);
+
+ if (jpegError) goto AbortDecompressJpeg32;
+
+ cinfo.out_color_space = JCS_RGB;
+
+ jpeg_start_decompress(&cinfo);
+
+ if (jpegError) goto AbortDecompressJpeg32;
+
+ if (cinfo.output_width != w ||
+ cinfo.output_height != h ||
+ cinfo.output_components != 3)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg32 : PANIC! Wrong JPEG data received.\n"
+ << logofs_flush;
+ #endif
+
+ jpeg_destroy_decompress(&cinfo);
+
+ return -1;
+ }
+
+ //
+ // PixelPtr points to dstBuf which is
+ // already padded correctly for the final
+ // image to put
+ //
+
+ data = dstBuf;
+
+ rowPointer[0] = (JSAMPROW) tmpBuf;
+
+ unsigned long pixel;
+
+ int i;
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ jpeg_read_scanlines(&cinfo, rowPointer, 1);
+
+ if (jpegError) goto AbortDecompressJpeg32;
+
+ for (dx = 0; dx < w; dx++)
+ {
+ pixel = RGB24_TO_PIXEL(32, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1],
+ tmpBuf[dx * 3 + 2]);
+
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ data[i] = (unsigned char)(pixel & 0xff);
+ pixel >>= 8;
+ }
+ }
+ else
+ {
+ for (i = 3; i >= 0; i--)
+ {
+ data[i] = (unsigned char) (pixel & 0xff);
+ pixel >>= 8;
+ }
+ }
+
+ data += 4;
+ }
+
+ dy++;
+ }
+
+ AbortDecompressJpeg32:
+
+ if (jpegError == 0)
+ {
+ jpeg_finish_decompress(&cinfo);
+ }
+
+ jpeg_destroy_decompress(&cinfo);
+
+ if (jpegError == 1)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressJpeg32: Failed to decompress JPEG image.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "DecompressJpeg32: Decompression finished with "
+ << dy << " lines handled.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+static void JpegInitSource(j_decompress_ptr cinfo)
+{
+ jpegError = 0;
+}
+
+static boolean JpegFillInputBuffer(j_decompress_ptr cinfo)
+{
+ jpegError = 1;
+
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+ jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr;
+
+ return TRUE;
+}
+
+static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes)
+{
+ if (num_bytes < 0 || (unsigned long) num_bytes > jpegSrcManager.bytes_in_buffer)
+ {
+ jpegError = 1;
+
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+ jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr;
+ }
+ else
+ {
+ jpegSrcManager.next_input_byte += (size_t) num_bytes;
+ jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+static void JpegTermSource(j_decompress_ptr cinfo)
+{
+}
+
+static void JpegSetSrcManager(j_decompress_ptr cinfo,
+ CARD8 *compressedData,
+ int compressedLen)
+{
+ jpegBufferPtr = (JOCTET *) compressedData;
+ jpegBufferLen = (size_t) compressedLen;
+
+ jpegSrcManager.init_source = JpegInitSource;
+ jpegSrcManager.fill_input_buffer = JpegFillInputBuffer;
+ jpegSrcManager.skip_input_data = JpegSkipInputData;
+ jpegSrcManager.resync_to_restart = jpeg_resync_to_restart;
+ jpegSrcManager.term_source = JpegTermSource;
+ jpegSrcManager.next_input_byte = jpegBufferPtr;
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+
+ cinfo->src = &jpegSrcManager;
+}
diff --git a/nxcomp/src/Jpeg.h b/nxcomp/src/Jpeg.h
new file mode 100644
index 000000000..58a5bffef
--- /dev/null
+++ b/nxcomp/src/Jpeg.h
@@ -0,0 +1,36 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Jpeg_H
+#define Jpeg_H
+
+#include "Misc.h"
+#include "Unpack.h"
+
+int UnpackJpeg(T_geometry *geometry, unsigned char method, unsigned char *srcData,
+ int srcSize, int dstBpp, int dstWidth, int dstHeight,
+ unsigned char *dstData, int dstSize);
+
+#endif /* Jpeg_H */
diff --git a/nxcomp/src/Keeper.cpp b/nxcomp/src/Keeper.cpp
new file mode 100644
index 000000000..4babbe8a6
--- /dev/null
+++ b/nxcomp/src/Keeper.cpp
@@ -0,0 +1,612 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include "Keeper.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Remove the directory if it's empty
+// since more than 30 * 24 h.
+//
+
+#define EMPTY_DIR_TIME 2592000
+
+//
+// Sleep once any ONCE entries.
+//
+
+#define ONCE 2
+
+//
+// Define this to trace messages while
+// they are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// This is used for reference count.
+//
+
+#ifdef REFERENCES
+
+int File::references_ = 0;
+
+#endif
+
+bool T_older::operator () (File *a, File *b) const
+{
+ return a -> compare(b);
+}
+
+File::File()
+{
+ name_ = NULL;
+
+ size_ = 0;
+ time_ = 0;
+
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "Keeper: Created new File at "
+ << this << " out of " << references_
+ << " allocated references.\n"
+ << logofs_flush;
+
+ #endif
+}
+
+//
+// TODO: This class can leak industrial amounts of memory.
+// I'm 100% sure that the desctructor is called and that
+// also the string pointed in the File structure is dele-
+// ted. Everything is logged, but still the memory is not
+// freed. This is less a problem on Windows, where the me-
+// mory occupation remains almost constant. Obviously the
+// problem lies in the STL allocators of the GNU libstc++.
+//
+
+File::~File()
+{
+ #ifdef TEST
+ *logofs << "Keeper: Deleting name for File at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ delete [] name_;
+
+ #ifdef REFERENCES
+
+ *logofs << "Keeper: Deleted File at "
+ << this << " out of " << references_
+ << " allocated references.\n"
+ << logofs_flush;
+
+ references_--;
+
+ #endif
+}
+
+bool File::compare(File *b) const
+{
+ if (this -> time_ == b -> time_)
+ {
+ return (this -> size_ < b -> size_);
+ }
+
+ return (this -> time_ < b -> time_);
+}
+
+Keeper::Keeper(int caches, int images, const char *root,
+ int sleep, int parent)
+{
+ caches_ = caches;
+ images_ = images;
+ sleep_ = sleep;
+ parent_ = parent;
+
+ root_ = new char[strlen(root) + 1];
+
+ strcpy(root_, root);
+
+ total_ = 0;
+ signal_ = 0;
+
+ files_ = new T_files;
+}
+
+Keeper::~Keeper()
+{
+ empty();
+
+ delete files_;
+
+ delete [] root_;
+}
+
+int Keeper::cleanupCaches()
+{
+ #ifdef TEST
+ *logofs << "Keeper: Looking for cache directories in '"
+ << root_ << "'.\n" << logofs_flush;
+ #endif
+
+ DIR *rootDir = opendir(root_);
+
+ if (rootDir != NULL)
+ {
+ dirent *dirEntry;
+
+ struct stat fileStat;
+
+ int baseSize = strlen(root_);
+
+ int s = 0;
+
+ while (((dirEntry = readdir(rootDir)) != NULL))
+ {
+ if (s++ % ONCE == 0) usleep(sleep_ * 1000);
+
+ if (signal_ != 0) break;
+
+ if (strcmp(dirEntry -> d_name, "cache") == 0 ||
+ strncmp(dirEntry -> d_name, "cache-", 6) == 0)
+ {
+ char *dirName = new char[baseSize + strlen(dirEntry -> d_name) + 2];
+
+ if (dirName == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't check directory entry '"
+ << dirEntry -> d_name << "'.\n" << logofs_flush;
+ #endif
+
+ delete [] dirName;
+
+ continue;
+ }
+
+ strcpy(dirName, root_);
+ strcpy(dirName + baseSize, "/");
+ strcpy(dirName + baseSize + 1, dirEntry -> d_name);
+
+ #ifdef TEST
+ *logofs << "Keeper: Checking directory '" << dirName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (stat(dirName, &fileStat) == 0 &&
+ S_ISDIR(fileStat.st_mode) != 0)
+ {
+ //
+ // Add to repository all the "C-" and
+ // "S-" files in the given directory.
+ //
+
+ collect(dirName);
+ }
+
+ delete [] dirName;
+ }
+ }
+
+ closedir(rootDir);
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't open NX root directory '"
+ << root_ << "'. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't open NX root directory '"
+ << root_ << "'. Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+ }
+
+ //
+ // Remove older files.
+ //
+
+ cleanup(caches_);
+
+ //
+ // Empty the repository.
+ //
+
+ empty();
+
+ return 1;
+}
+
+int Keeper::cleanupImages()
+{
+ #ifdef TEST
+ *logofs << "Keeper: Looking for image directory in '"
+ << root_ << "'.\n" << logofs_flush;
+ #endif
+
+ char *imagesPath = new char[strlen(root_) + strlen("/images") + 1];
+
+ if (imagesPath == NULL)
+ {
+ return -1;
+ }
+
+ strcpy(imagesPath, root_);
+ strcat(imagesPath, "/images");
+
+ //
+ // Check if the cache directory does exist.
+ //
+
+ struct stat dirStat;
+
+ if (stat(imagesPath, &dirStat) == -1)
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't stat NX images cache directory '"
+ << imagesPath << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't stat NX images cache directory '"
+ << imagesPath << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ delete [] imagesPath;
+
+ return -1;
+ }
+
+ //
+ // Check any of the 16 directories in the
+ // images root path.
+ //
+
+ char *digitPath = new char[strlen(imagesPath) + 5];
+
+ strcpy(digitPath, imagesPath);
+
+ for (char digit = 0; digit < 16; digit++)
+ {
+ //
+ // Give up if we received a signal or
+ // our parent is gone.
+ //
+
+ if (signal_ != 0)
+ {
+ #ifdef TEST
+ *logofs << "Keeper: Signal detected. Aborting.\n"
+ << logofs_flush;
+ #endif
+
+ goto KeeperCleanupImagesAbort;
+ }
+ else if (parent_ != getppid() || parent_ == 1)
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Parent process appears "
+ << "to be dead. Returning.\n"
+ << logofs_flush;
+ #endif
+
+ goto KeeperCleanupImagesAbort;
+
+ return 0;
+ }
+
+ sprintf(digitPath + strlen(imagesPath), "/I-%01X", digit);
+
+ //
+ // Add to the repository all the files
+ // in the given directory.
+ //
+
+ collect(digitPath);
+ }
+
+ delete [] imagesPath;
+ delete [] digitPath;
+
+ //
+ // Remove the oldest files.
+ //
+
+ cleanup(images_);
+
+ //
+ // Empty the repository.
+ //
+
+ empty();
+
+ return 1;
+
+KeeperCleanupImagesAbort:
+
+ delete [] imagesPath;
+ delete [] digitPath;
+
+ empty();
+
+ return 0;
+}
+
+int Keeper::collect(const char *path)
+{
+ #ifdef TEST
+ *logofs << "Keeper: Looking for files in directory '"
+ << path << "'.\n" << logofs_flush;
+ #endif
+
+ DIR *cacheDir = opendir(path);
+
+ if (cacheDir != NULL)
+ {
+ File *file;
+
+ dirent *dirEntry;
+
+ struct stat fileStat;
+
+ int baseSize = strlen(path);
+ int fileSize = baseSize + 3 + MD5_LENGTH * 2 + 1;
+
+ int n = 0;
+ int s = 0;
+
+ while (((dirEntry = readdir(cacheDir)) != NULL))
+ {
+ if (s++ % ONCE == 0) usleep(sleep_ * 1000);
+
+ if (signal_ != 0) break;
+
+ if (strcmp(dirEntry -> d_name, ".") == 0 ||
+ strcmp(dirEntry -> d_name, "..") == 0)
+ {
+ continue;
+ }
+
+ n++;
+
+ if (strlen(dirEntry -> d_name) == (MD5_LENGTH * 2 + 2) &&
+ (strncmp(dirEntry -> d_name, "I-", 2) == 0 ||
+ strncmp(dirEntry -> d_name, "S-", 2) == 0 ||
+ strncmp(dirEntry -> d_name, "C-", 2) == 0))
+ {
+ file = new File();
+
+ char *fileName = new char[fileSize];
+
+ if (file == NULL || fileName == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't add file '"
+ << dirEntry -> d_name << "' to repository.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] fileName;
+
+ delete file;
+
+ continue;
+ }
+
+ strcpy(fileName, path);
+ strcpy(fileName + baseSize, "/");
+ strcpy(fileName + baseSize + 1, dirEntry -> d_name);
+
+ file -> name_ = fileName;
+
+ #ifdef DEBUG
+ *logofs << "Keeper: Adding file '" << file -> name_
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (stat(file -> name_, &fileStat) == -1)
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't stat NX file '"
+ << file -> name_ << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete file;
+
+ continue;
+ }
+
+ file -> size_ = fileStat.st_size;
+ file -> time_ = fileStat.st_mtime;
+
+ files_ -> insert(T_files::value_type(file));
+
+ total_ += file -> size_;
+ }
+ }
+
+ closedir(cacheDir);
+
+ if (n == 0)
+ {
+ time_t now = time(NULL);
+
+ if (now > 0 && stat(path, &fileStat) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Keeper: Empty NX subdirectory '" << path
+ << "' unused since " << now - fileStat.st_mtime
+ << " S.\n" << logofs_flush;
+ #endif
+
+ if (now - fileStat.st_mtime > EMPTY_DIR_TIME)
+ {
+ #ifdef TEST
+ *logofs << "Keeper: Removing empty NX subdirectory '"
+ << path << "'.\n" << logofs_flush;
+ #endif
+
+ rmdir(path);
+ }
+ }
+ }
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Keeper: WARNING! Can't open NX subdirectory '"
+ << path << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't open NX subdirectory '"
+ << path << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+
+ return 1;
+}
+
+int Keeper::cleanup(int threshold)
+{
+ #ifdef TEST
+ *logofs << "Keeper: Deleting the oldest files with "
+ << files_ -> size() << " elements threshold "
+ << threshold << " and size " << total_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // At least some versions of the standard
+ // library don't allow erasing an element
+ // while looping. This is not the most ef-
+ // ficient way to release the objects, but
+ // it's better than making a copy of the
+ // container.
+ //
+
+ while (total_ > threshold && files_ -> size() > 0)
+ {
+ T_files::iterator i = files_ -> begin();
+
+ File *file = *i;
+
+ #ifdef TEST
+ *logofs << "Keeper: Removing '" << file -> name_
+ << "' with time " << file -> time_ << " and size "
+ << file -> size_ << ".\n" << logofs_flush;
+ #endif
+
+ unlink(file -> name_);
+
+ total_ -= file -> size_;
+
+ #ifdef DEBUG
+ *logofs << "Keeper: Going to delete the file at "
+ << file << " while cleaning up.\n"
+ << logofs_flush;
+ #endif
+
+ delete file;
+
+ #ifdef DEBUG
+ *logofs << "Keeper: Going to erase the element "
+ << "while cleaning up.\n"
+ << logofs_flush;
+ #endif
+
+ files_ -> erase(i);
+ }
+
+ #ifdef TEST
+ *logofs << "Keeper: Bytes in repository are "
+ << total_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void Keeper::empty()
+{
+ #ifdef TEST
+ *logofs << "Keeper: Getting rid of files in repository.\n"
+ << logofs_flush;
+ #endif
+
+ while (files_ -> size() > 0)
+ {
+ T_files::iterator i = files_ -> begin();
+
+ File *file = *i;
+
+ #ifdef DEBUG
+ *logofs << "Keeper: Going to delete the file at "
+ << file << " while emptying.\n"
+ << logofs_flush;
+ #endif
+
+ delete file;
+
+ #ifdef DEBUG
+ *logofs << "Keeper: Going to erase the element "
+ << "while emptying.\n"
+ << logofs_flush;
+ #endif
+
+ files_ -> erase(i);
+ }
+
+ total_ = 0;
+
+ #ifdef TEST
+ *logofs << "Keeper: Bytes in repository are "
+ << total_ << ".\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/Keeper.h b/nxcomp/src/Keeper.h
new file mode 100644
index 000000000..b440beceb
--- /dev/null
+++ b/nxcomp/src/Keeper.h
@@ -0,0 +1,199 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Keeper_H
+#define Keeper_H
+
+#include "Misc.h"
+#include "Types.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to check how many file
+// nodes are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+class Keeper;
+
+class File
+{
+ friend class Keeper;
+
+ public:
+
+ File();
+
+ ~File();
+
+ //
+ // Allow sort by time and size. If time
+ // is the same, keep the bigger element.
+ //
+
+ bool compare(File *b) const;
+
+ private:
+
+ char *name_;
+
+ int size_;
+ time_t time_;
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+class Keeper
+{
+ public:
+
+ Keeper(int caches, int images, const char *root,
+ int sleep, int parent);
+
+ ~Keeper();
+
+ //
+ // Call this just once.
+ //
+
+ int cleanupCaches();
+
+ //
+ // Call this at any given interval.
+ //
+
+ int cleanupImages();
+
+ //
+ // Call this if it's time to exit.
+ //
+
+ void setSignal(int signal)
+ {
+ signal_ = signal;
+ }
+
+ int getSignal()
+ {
+ return signal_;
+ }
+
+ int getParent()
+ {
+ return parent_;
+ }
+
+ private:
+
+ //
+ // Get a list of files in directory.
+ //
+
+ int collect(const char *path);
+
+ //
+ // Sort the collected files according to
+ // last modification time and delete the
+ // older ones until disk size is below
+ // the threshold.
+ //
+
+ int cleanup(int threshold);
+
+ //
+ // Empty the files repository.
+ //
+
+ void empty();
+
+ //
+ // Size in bytes of total allowed
+ // storage for persistent caches.
+ //
+
+ int caches_;
+
+ //
+ // Size in bytes of total allowed
+ // storage for images cache.
+ //
+
+ int images_;
+
+ //
+ // Path of the NX root directory.
+ //
+
+ char *root_;
+
+ //
+ // The little delay to be introduced
+ // before reading a new entry.
+ //
+
+ int sleep_;
+
+ //
+ // Total size of files in repository.
+ //
+
+ int total_;
+
+ //
+ // The parent process, so we can exit
+ // if it is gone.
+ //
+
+ int parent_;
+
+ //
+ // Set if we need to give up because
+ // of a signal.
+ //
+
+ int signal_;
+
+ //
+ // Repository where to collect files.
+ //
+
+ T_files *files_;
+};
+
+#endif /* Keeper_H */
+
diff --git a/nxcomp/src/List.cpp b/nxcomp/src/List.cpp
new file mode 100644
index 000000000..b2db7151c
--- /dev/null
+++ b/nxcomp/src/List.cpp
@@ -0,0 +1,112 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "List.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to know how many instances
+// are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+#ifdef REFERENCES
+
+int List::references_ = 0;
+
+#endif
+
+List::List()
+{
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "List: Created new List at "
+ << this << " out of " << references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+List::~List()
+{
+ #ifdef REFERENCES
+
+ references_--;
+
+ *logofs << "List: Deleted List at "
+ << this << " out of " << references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+void List::remove(int value)
+{
+ for (T_list::iterator i = list_.begin(); i != list_.end(); i++)
+ {
+ if (*i == value)
+ {
+ list_.erase(i);
+
+ return;
+ }
+ }
+
+ #ifdef PANIC
+ *logofs << "List: PANIC! Should not try to remove "
+ << "an element not found in the list.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Should not try to remove "
+ << "an element not found in the list.\n";
+
+ HandleAbort();
+}
+
+void List::rotate()
+{
+ if (list_.size() > 1)
+ {
+ int value = *(list_.begin());
+
+ list_.pop_front();
+
+ list_.push_back(value);
+ }
+}
diff --git a/nxcomp/src/List.h b/nxcomp/src/List.h
new file mode 100644
index 000000000..31c80f835
--- /dev/null
+++ b/nxcomp/src/List.h
@@ -0,0 +1,95 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef List_H
+#define List_H
+
+#include "Misc.h"
+#include "Types.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to log when lists are
+// allocated and deallocated.
+//
+
+#undef REFERENCES
+
+class List
+{
+ public:
+
+ List();
+
+ ~List();
+
+ int getSize()
+ {
+ return list_.size();
+ }
+
+ T_list &getList()
+ {
+ return list_;
+ }
+
+ T_list copyList()
+ {
+ return list_;
+ }
+
+ void add(int value)
+ {
+ list_.push_back(value);
+ }
+
+ void remove(int value);
+
+ void rotate();
+
+ private:
+
+ //
+ // The list container.
+ //
+
+ T_list list_;
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+#endif /* List_H */
diff --git a/nxcomp/src/ListFontsReply.cpp b/nxcomp/src/ListFontsReply.cpp
new file mode 100644
index 000000000..16be522a1
--- /dev/null
+++ b/nxcomp/src/ListFontsReply.cpp
@@ -0,0 +1,213 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ListFontsReply.h"
+
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef DUMP
+#undef TEST
+#undef DEBUG
+
+ListFontsReplyStore::ListFontsReplyStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = LISTFONTSREPLY_ENABLE_CACHE;
+ enableData = LISTFONTSREPLY_ENABLE_DATA;
+ enableSplit = LISTFONTSREPLY_ENABLE_SPLIT;
+
+ // Since ProtoStep7 (#issue 108)
+ enableCompress = LISTFONTSREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = LISTFONTSREPLY_DATA_LIMIT;
+ dataOffset = LISTFONTSREPLY_DATA_OFFSET;
+
+ cacheSlots = LISTFONTSREPLY_CACHE_SLOTS;
+ cacheThreshold = LISTFONTSREPLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = LISTFONTSREPLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+ListFontsReplyStore::~ListFontsReplyStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ListFontsReplyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ listFontsReply -> number_of_names = GetUINT(buffer + 8, bigEndian);
+
+ //
+ // Clean up padding bytes.
+ //
+
+ if ((int) size > dataOffset)
+ {
+ unsigned int current;
+ unsigned int length;
+ unsigned int nstringInNames;
+
+ unsigned char *end = NULL;
+ unsigned char *pad = NULL;
+
+ #ifdef DUMP
+
+ *logofs << "\n" << logofs_flush;
+
+ *logofs << "Number of STRING8 " << listFontsReply -> number_of_names << ".\n" << logofs_flush;
+
+ *logofs << "Size " << size << ".\n" << logofs_flush;
+
+ DumpHexData(buffer, size);
+
+ *logofs << "\n" << logofs_flush;
+
+ #endif
+
+ length = LISTFONTSREPLY_DATA_OFFSET;
+
+ for (nstringInNames = 0;
+ nstringInNames < listFontsReply -> number_of_names &&
+ listFontsReply -> number_of_names > 0;
+ nstringInNames++)
+ {
+ //
+ // Start with offset LISTFONTSREPLY_DATA_OFFSET 32.
+ //
+
+ current = buffer[length];
+
+ length += current + 1;
+
+ #ifdef DUMP
+ *logofs << "\nString number : " << nstringInNames << " Current length : "
+ << current << "\n" << logofs_flush;
+ #endif
+ }
+
+ #ifdef DUMP
+ *logofs << "\nFinal length " << length << "\n" << logofs_flush;
+ #endif
+
+ end = ((unsigned char *) buffer) + size;
+
+ for (pad = ((unsigned char *) buffer) + length; pad < end; pad++)
+ {
+ *pad = 0;
+
+ #ifdef DUMP
+ *logofs << "\nPadding ." << "\n" << logofs_flush;
+ #endif
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ListFontsReplyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutUINT(listFontsReply -> number_of_names, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ListFontsReplyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ListFontsReplyMessage *listFontsReply = (ListFontsReplyMessage *) message;
+
+ *logofs << name() << ": Identity number_of_names "
+ << listFontsReply -> number_of_names << ", size "
+ << listFontsReply -> size_ << ".\n";
+
+ #endif
+}
+
+void ListFontsReplyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Field number_of_names.
+ //
+
+ md5_append(md5_state_, buffer + 8, 2);
+}
diff --git a/nxcomp/src/ListFontsReply.h b/nxcomp/src/ListFontsReply.h
new file mode 100644
index 000000000..c731878e0
--- /dev/null
+++ b/nxcomp/src/ListFontsReply.h
@@ -0,0 +1,146 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ListFontsReply_H
+#define ListFontsReply_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define LISTFONTSREPLY_ENABLE_CACHE 1
+#define LISTFONTSREPLY_ENABLE_DATA 1
+#define LISTFONTSREPLY_ENABLE_SPLIT 0
+
+#define LISTFONTSREPLY_DATA_LIMIT 1048576 - 32
+#define LISTFONTSREPLY_DATA_OFFSET 32
+
+#define LISTFONTSREPLY_CACHE_SLOTS 200
+#define LISTFONTSREPLY_CACHE_THRESHOLD 20
+#define LISTFONTSREPLY_CACHE_LOWER_THRESHOLD 5
+
+#define LISTFONTSREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class ListFontsReplyMessage : public Message
+{
+ friend class ListFontsReplyStore;
+
+ public:
+
+ ListFontsReplyMessage()
+ {
+ }
+
+ ~ListFontsReplyMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned short int number_of_names;
+};
+
+class ListFontsReplyStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ ListFontsReplyStore(StaticCompressor *compressor);
+
+ virtual ~ListFontsReplyStore();
+
+ virtual const char *name() const
+ {
+ return "ListFontsReply";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_ListFonts;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ListFontsReplyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new ListFontsReplyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ListFontsReplyMessage((const ListFontsReplyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ListFontsReplyMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* ListFontsReply_H */
diff --git a/nxcomp/src/Loop.cpp b/nxcomp/src/Loop.cpp
new file mode 100644
index 000000000..b6c82a2b4
--- /dev/null
+++ b/nxcomp/src/Loop.cpp
@@ -0,0 +1,16685 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <setjmp.h>
+
+#include <math.h>
+#include <ctype.h>
+#include <string.h>
+#include <dirent.h>
+#include <pwd.h>
+
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/utsname.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "Misc.h"
+
+#ifdef __sun
+#include <strings.h>
+#endif
+
+//
+// MacOSX 10.4 defines socklen_t. This is
+// intended to ensure compatibility with
+// older versions.
+//
+
+#ifdef __APPLE__
+#include <AvailabilityMacros.h>
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_3
+typedef int socklen_t;
+#endif
+#endif
+
+#ifdef _AIX
+#include <strings.h>
+#include <sys/select.h>
+#endif
+
+#ifndef __CYGWIN32__
+#include <netinet/tcp.h>
+#endif
+
+//
+// NX include files.
+//
+
+#include "NX.h"
+#include "NXalert.h"
+
+#include "Misc.h"
+#include "Control.h"
+#include "Socket.h"
+#include "Statistics.h"
+#include "Auth.h"
+#include "Keeper.h"
+#include "Agent.h"
+
+#include "ClientProxy.h"
+#include "ServerProxy.h"
+
+#include "Message.h"
+#include "ChannelEndPoint.h"
+
+//
+// System specific defines.
+//
+
+
+//
+// HP-UX hides this define.
+//
+
+#if defined(hpux) && !defined(RLIM_INFINITY)
+
+#define RLIM_INFINITY 0x7fffffff
+
+#endif
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Enable log output in signal handler.
+// This is likely to hang the proxy at
+// random, at least on Linux.
+//
+
+#undef UNSAFE
+
+//
+// Let all logs go to the standard error.
+// This is useful to interleave the Xlib
+// log output with the proxy output in a
+// single file.
+//
+
+#undef MIXED
+
+//
+// Define this to check if the client and
+// server caches match at shutdown. This
+// is a test facility as it requires that
+// both proxies are running on the same
+// host.
+//
+
+#undef MATCH
+
+//
+// If defined, reduce the size of the log
+// file and be sure it never exceeds the
+// limit.
+//
+
+#undef QUOTA
+
+//
+// If defined, force very strict limits for
+// the proxy tokens and force the proxy to
+// enter often in congestion state.
+//
+
+#undef STRICT
+
+//
+// Print a line in the log if the time we
+// spent inside the select or handling the
+// messages exceeded a given time value.
+//
+
+#undef TIME
+
+//
+// This can be useful when testing the forwarding
+// of the SSHD connection by nxssh to the agent.
+// The debug output will go to a well known file
+// that will be opened also by nxssh when BINDER
+// is enabled there.
+//
+
+#undef BINDER
+
+//
+// Define this to override the limits on
+// the core dump size.
+//
+
+#define COREDUMPS
+
+//
+// Upper limit of pre-allocated buffers
+// for string parameters.
+//
+
+#define DEFAULT_STRING_LENGTH 256
+
+//
+// Maximum length of remote options data
+// passed by peer proxy at startup.
+//
+
+#define DEFAULT_REMOTE_OPTIONS_LENGTH 512
+
+//
+// Maximum length of NX display string.
+//
+
+#define DEFAULT_DISPLAY_OPTIONS_LENGTH 1024
+
+//
+// Maximum number of cache file names to
+// send to the server side.
+//
+
+#define DEFAULT_REMOTE_CACHE_ENTRIES 100
+
+//
+// Maximum length of remote options string
+// that can be received from the peer proxy.
+//
+
+#define MAXIMUM_REMOTE_OPTIONS_LENGTH 4096
+
+//
+// Macro is true if we determined our proxy
+// mode.
+//
+
+#define WE_SET_PROXY_MODE (control -> ProxyMode != proxy_undefined)
+
+//
+// Macro is true if our side is the one that
+// should connect to remote.
+//
+
+#define WE_INITIATE_CONNECTION (connectSocket.enabled())
+
+//
+// Is true if we must provide our credentials
+// to the remote peer.
+//
+
+#define WE_PROVIDE_CREDENTIALS (control -> ProxyMode == proxy_server)
+
+//
+// Is true if we listen for a local forwarder
+// that will tunnel the traffic through a SSH
+// or HTTP link.
+//
+
+#define WE_LISTEN_FORWARDER (control -> ProxyMode == proxy_server && \
+ listenSocket.enabled())
+
+//
+// You must define FLUSH in Misc.h if
+// you want an immediate flush of the
+// log output.
+//
+
+ostream *logofs = NULL;
+
+//
+// Other stream destriptors used for
+// logging.
+//
+
+ostream *statofs = NULL;
+ostream *errofs = NULL;
+
+//
+// Save standard error's rdbuf here
+// and restore it when exiting.
+//
+
+static streambuf *errsbuf = NULL;
+
+//
+// Allow faults to be recovered by
+// jumping back into the main loop.
+//
+
+jmp_buf context;
+
+//
+// Provide operational parameters.
+//
+
+Control *control = NULL;
+
+//
+// Collect and print statistics.
+//
+
+Statistics *statistics = NULL;
+
+//
+// Keep data for X11 authentication.
+//
+
+Auth *auth = NULL;
+
+//
+// This class makes the hard work.
+//
+
+Proxy *proxy = NULL;
+
+//
+// Used to handle memory-to-memory
+// transport to the X agent.
+//
+
+Agent *agent = NULL;
+
+//
+// The image cache house-keeping class.
+//
+
+Keeper *keeper = NULL;
+
+//
+// Callback set by the child process
+// to be notified about signals.
+//
+
+int (*signalHandler)(int) = NULL;
+
+//
+// Signal handling functions (that are not already mentioned in Misc.h).
+//
+
+void InstallSignals();
+
+static void RestoreSignals();
+static void HandleSignal(int signal);
+
+//
+// Signal handling utilities.
+//
+
+static void InstallSignal(int signal, int action);
+static void RestoreSignal(int signal);
+
+static int HandleChildren();
+
+int HandleChild(int child);
+static int CheckChild(int pid, int status);
+static int WaitChild(int child, const char *label, int force);
+
+int CheckParent(const char *name, const char *type, int parent);
+
+void RegisterChild(int child);
+
+static int CheckAbort();
+
+//
+// Timer handling utilities (that are not already mentioned in Misc.h).
+//
+
+static void HandleTimer(int signal);
+
+//
+// Kill or check a running child.
+//
+
+static int KillProcess(int pid, const char *label, int signal, int wait);
+static int CheckProcess(int pid, const char *label);
+
+//
+// Macros used to test the pid of a child.
+//
+
+#define IsFailed(pid) ((pid) < 0)
+#define IsRunning(pid) ((pid) > 1)
+#define IsNotRunning(pid) ((pid) == 0)
+#define IsRestarting(pid) ((pid) == 1)
+
+#define SetNotRunning(pid) ((pid) = 0)
+#define SetRestarting(pid) ((pid) = 1)
+
+//
+// Start or restart the house-keeper process.
+//
+
+static int StartKeeper();
+
+//
+// Cleanup functions.
+//
+
+void CleanupConnections();
+void CleanupListeners();
+void CleanupSockets();
+void CleanupGlobal();
+
+static void CleanupChildren();
+static void CleanupLocal();
+static void CleanupKeeper();
+static void CleanupStreams();
+
+//
+// Loop forever until the connections
+// to the peer proxy is dropped.
+//
+
+static void WaitCleanup();
+
+//
+// Initialization functions.
+//
+
+static int InitBeforeNegotiation();
+static int SetupProxyConnection();
+static int InitAfterNegotiation();
+static int SetupProxyInstance();
+static int SetupAuthInstance();
+static int SetupAgentInstance();
+
+static int SetupTcpSocket();
+static int SetupUnixSocket();
+static int SetupServiceSockets();
+static int SetupDisplaySocket(int &addr_family, sockaddr *&addr,
+ unsigned int &addr_length);
+
+//
+// Setup a listening socket and accept
+// a new connection.
+//
+
+static int ListenConnection(ChannelEndPoint &endPoint, const char *label);
+static int ListenConnectionTCP(const char *host, long port, const char *label);
+static int ListenConnectionUnix(const char *path, const char *label);
+static int ListenConnectionAny(sockaddr *addr, socklen_t addrlen, const char *label);
+static int AcceptConnection(int fd, int domain, const char *label);
+
+//
+// Other convenience functions.
+//
+
+static int PrepareProxyConnectionTCP(char** hostName, long int* portNum, int* timeout, int* proxyFileDescriptor, int* reason);
+static int PrepareProxyConnectionUnix(char** path, int* timeout, int* proxyFileDescriptor, int* reason);
+
+static int WaitForRemote(ChannelEndPoint &socketAddress);
+static int ConnectToRemote(ChannelEndPoint &socketAddress);
+
+static int SendProxyOptions(int fd);
+static int SendProxyCaches(int fd);
+static int ReadProxyVersion(int fd);
+static int ReadProxyOptions(int fd);
+static int ReadProxyCaches(int fd);
+static int ReadForwarderVersion(int fd);
+static int ReadForwarderOptions(int fd);
+
+static int ReadRemoteData(int fd, char *buffer, int size, char stop);
+static int WriteLocalData(int fd, const char *buffer, int size);
+
+static void PrintVersionInfo();
+static void PrintProcessInfo();
+static void PrintConnectionInfo();
+static void PrintUsageInfo(const char *option, const int error);
+static void PrintOptionIgnored(const char *type, const char *name, const char *value);
+
+//
+// This is not static to avoid a warning.
+//
+
+void PrintCopyrightInfo();
+
+static const char *GetOptions(const char *options);
+static const char *GetArg(int &argi, int argc, const char **argv);
+static int CheckArg(const char *type, const char *name, const char *value);
+static int ParseArg(const char *type, const char *name, const char *value);
+static int ValidateArg(const char *type, const char *name, const char *value);
+static void SetAndValidateChannelEndPointArg(const char *type, const char *name, const char *value,
+ ChannelEndPoint &endPoint);
+static int LowercaseArg(const char *type, const char *name, char *value);
+static int CheckSignal(int signal);
+
+extern "C"
+{
+ int ParseCommandLineOptions(int argc, const char **argv);
+ int ParseEnvironmentOptions(const char *env, int force);
+ int ParseBindOptions(char **host, int *port);
+}
+
+static int ParseFileOptions(const char *file);
+static int ParseRemoteOptions(char *opts);
+static int ParseForwarderOptions(char *opts);
+
+//
+// These functions are used to parse literal
+// values provided by the user and set the
+// control parameters accordingly.
+//
+
+static int ParseLinkOption(const char *opt);
+static int ParseBitrateOption(const char *opt);
+static int ParseCacheOption(const char *opt);
+static int ParseShmemOption(const char *opt);
+static int ParseImagesOption(const char *opt);
+static int ParsePackOption(const char *opt);
+
+//
+// Set host and port where NX proxy is supposed
+// to be listening in case such parameters are
+// given on the command line.
+//
+
+static int ParseHostOption(const char *opt, char *host, long &port);
+
+//
+// Translate a font server port specification
+// to the corresponding Unix socket path.
+//
+
+static int ParseFontPath(char *path);
+
+//
+// Translate a pack method id in a literal.
+//
+
+static int ParsePackMethod(const int method, const int quality);
+
+//
+// Try to increase the size of the allowed
+// core dumps.
+//
+
+static int SetCore();
+
+//
+// Set the proxy mode to either client or
+// server.
+//
+
+static int SetMode(int mode);
+
+//
+// Determine the path of the NX_* directories
+// from the environment.
+//
+
+static int SetDirectories();
+
+//
+// Set the defaults used for the log file and
+// statistics.
+//
+
+static int SetLogs();
+
+//
+// Check if local and remote protocol versions
+// are compatible and, eventually, downgrade
+// local version to the minimum level that is
+// known to work.
+//
+
+static int SetVersion();
+
+//
+// Setup the listening TCP ports used for the
+// additional channels according to user's
+// wishes.
+//
+
+static int SetPorts();
+
+//
+// Set the maximum number of open descriptors.
+//
+
+static int SetDescriptors();
+
+//
+// Set the path used for choosing the cache.
+// It must be selected after determining the
+// session type.
+//
+
+static int SetCaches();
+
+//
+// Initialize, one after the other, all the
+// configuration parameters.
+//
+
+static int SetParameters();
+
+//
+// Set the specific configuration parameter.
+//
+
+static int SetSession();
+static int SetStorage();
+static int SetShmem();
+static int SetPack();
+static int SetImages();
+static int SetLimits();
+
+//
+// Set up the control parameters based on
+// the link speed negotiated between the
+// proxies.
+//
+
+static int SetLink();
+
+static int SetLinkModem();
+static int SetLinkIsdn();
+static int SetLinkAdsl();
+static int SetLinkWan();
+static int SetLinkLan();
+
+//
+// Adjust the compression parameters.
+//
+
+static int SetCompression();
+
+static int SetCompressionModem();
+static int SetCompressionIsdn();
+static int SetCompressionAdsl();
+static int SetCompressionWan();
+static int SetCompressionLan();
+
+//
+// Determine the NX paths based on the
+// user's parameters or the environment.
+//
+
+char *GetClientPath();
+
+static char *GetSystemPath();
+static char *GetHomePath();
+static char *GetTempPath();
+static char *GetRootPath();
+static char *GetCachePath();
+static char *GetImagesPath();
+static char *GetSessionPath();
+static char *GetLastCache(char *list, const char *path);
+
+static int OpenLogFile(char *name, ostream *&stream);
+static int ReopenLogFile(char *name, ostream *&stream, int limit);
+
+//
+// Perform operations on the managed
+// descriptors in the main loop.
+//
+
+static void handleCheckSessionInLoop();
+static void handleCheckBitrateInLoop();
+
+#if defined(TEST) || defined(INFO)
+static void handleCheckSelectInLoop(int &setFDs, fd_set &readSet,
+ fd_set &writeSet, T_timestamp selectTs);
+static void handleCheckResultInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs,
+ struct timeval &startTs);
+static void handleCheckStateInLoop(int &setFDs);
+#endif
+
+static void handleCheckSessionInConnect();
+
+static inline void handleSetReadInLoop(fd_set &readSet, int &setFDs, struct timeval &selectTs);
+static inline void handleSetWriteInLoop(fd_set &writeSet, int &setFDs, struct timeval &selectTs);
+static inline void handleSetListenersInLoop(fd_set &writeSet, int &setFDs);
+static inline void handleSetAgentInLoop(int &setFDs, fd_set &readSet, fd_set &writeSet,
+ struct timeval &selectTs);
+
+static void handleAlertInLoop();
+static void handleStatisticsInLoop();
+
+static inline void handleAgentInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs);
+static inline void handleAgentLateInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs);
+
+static inline void handleReadableInLoop(int &resultFDs, fd_set &readSet);
+static inline void handleWritableInLoop(int &resultFDs, fd_set &writeSet);
+
+static inline void handleRotateInLoop();
+static inline void handleEventsInLoop();
+static inline void handleFlushInLoop();
+
+//
+// Manage the proxy link during the negotiation
+// phase.
+//
+
+static void handleNegotiationInLoop(int &setFDs, fd_set &readSet,
+ fd_set &writeSet, T_timestamp &selectTs);
+
+//
+// Print the 'terminating' messages in the
+// session log.
+//
+
+static inline void handleTerminatingInLoop();
+static inline void handleTerminatedInLoop();
+
+//
+// Monitor the size of the log file.
+//
+
+static void handleLogReopenInLoop(T_timestamp &lTs, T_timestamp &nTs);
+
+//
+// Directory where the NX binaries and libraries reside.
+//
+
+static char systemDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Directory used for temporary files.
+//
+
+static char tempDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Actually the full path to the client.
+//
+
+static char clientDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// User's home directory.
+//
+
+static char homeDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Root of directory structure to be created by proxy.
+//
+
+static char rootDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Root of statistics and log files to be created by proxy.
+//
+
+static char sessionDir[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Log files for errors and statistics. Error log is
+// the place where we print also debug informations.
+// Both files are closed, deleted and reopened as
+// their size exceed the limit set in control class.
+// The session log is not reopened, as it is used by
+// the NX client and server to track the advance of
+// the session.
+//
+
+static char errorsFileName[DEFAULT_STRING_LENGTH] = { 0 };
+static char statsFileName[DEFAULT_STRING_LENGTH] = { 0 };
+static char sessionFileName[DEFAULT_STRING_LENGTH] = { 0 };
+static char optionsFileName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal representing selected link speed
+// parameter. Value is translated in control values
+// used by proxies to stay synchronized.
+//
+
+static char linkSpeedName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal representing selected
+// cache size.
+//
+
+static char cacheSizeName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal representing selected
+// shared memory segment size.
+//
+
+static char shsegSizeName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal of images cache size.
+//
+
+static char imagesSizeName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal for bandwidth limit.
+//
+
+static char bitrateLimitName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// String literal for image packing method.
+//
+
+static char packMethodName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Product name provided by the server or client.
+//
+
+static char productName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Its corresponding value from NXpack.h.
+//
+
+static int packMethod = -1;
+static int packQuality = -1;
+
+//
+// String literal for session type. Persistent caches
+// are searched in directory whose name matches this
+// parameter.
+//
+
+static char sessionType[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Unique id assigned to session. It is used as
+// name of directory where all files are placed.
+//
+
+static char sessionId[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Set if we already parsed the options.
+//
+
+static int parsedOptions = 0;
+static int parsedCommand = 0;
+
+//
+// Buffer data received from the remote proxy at
+// session negotiation.
+//
+
+static char remoteData[MAXIMUM_REMOTE_OPTIONS_LENGTH] = { 0 };
+static int remotePosition = 0;
+
+//
+// Main loop file descriptors.
+//
+
+static int tcpFD = -1;
+static int unixFD = -1;
+static int cupsFD = -1;
+static int auxFD = -1;
+static int smbFD = -1;
+static int mediaFD = -1;
+static int httpFD = -1;
+static int fontFD = -1;
+static int slaveFD = -1;
+static int proxyFD = -1;
+
+//
+// Used for internal communication
+// with the X agent.
+//
+
+static int agentFD[2] = { -1, -1 };
+
+//
+// Flags determining which protocols and
+// ports are forwarded.
+//
+
+int useUnixSocket = 1;
+
+static int useTcpSocket = 1;
+static int useCupsSocket = 0;
+static int useAuxSocket = 0;
+static int useSmbSocket = 0;
+static int useMediaSocket = 0;
+static int useHttpSocket = 0;
+static int useFontSocket = 0;
+static int useSlaveSocket = 0;
+static int useAgentSocket = 0;
+
+//
+// Set if the launchd service is running
+// and its socket must be used as X socket.
+//
+
+static int useLaunchdSocket = 0;
+
+//
+// Set by user if he/she wants to modify
+// the default TCP_NODELAY option as set
+// in control.
+//
+
+static int useNoDelay = -1;
+
+//
+// Set if user wants to override default
+// flush timeout set according to link.
+//
+
+static int usePolicy = -1;
+
+//
+// Set if user wants to hide the RENDER
+// extension or wants to short-circuit
+// some simple replies at client side.
+//
+
+static int useRender = -1;
+static int useTaint = -1;
+
+//
+// Set if the user wants to reduce the
+// nominal size of the token messages
+// exchanged between the proxies.
+//
+
+static int useStrict = -1;
+
+//
+// Set if the proxy is running as part
+// of SSH on the client.
+//
+
+static int useEncryption = -1;
+
+//
+// Name of Unix socket created by the client proxy to
+// accept client connections. File must be unlinked
+// by cleanup function.
+//
+
+static char unixSocketName[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Other parameters.
+//
+
+static char acceptHost[DEFAULT_STRING_LENGTH] = { 0 };
+static char displayHost[DEFAULT_STRING_LENGTH] = { 0 };
+static char authCookie[DEFAULT_STRING_LENGTH] = { 0 };
+
+static int loopbackBind = DEFAULT_LOOPBACK_BIND;
+static int proxyPort = DEFAULT_NX_PROXY_PORT;
+static int xPort = DEFAULT_NX_X_PORT;
+
+//
+// Used to setup the connection the real
+// X display socket.
+//
+
+static int xServerAddrFamily = -1;
+static sockaddr *xServerAddr = NULL;
+static unsigned int xServerAddrLength = 0;
+
+//
+// The representation of a Unix socket path or
+// a bind address, denoting where the local proxy
+// will await the peer connection.
+//
+
+static ChannelEndPoint listenSocket;
+
+//
+// The TCP host and port or Unix file socket where
+// the remote proxy will be contacted.
+//
+
+static ChannelEndPoint connectSocket;
+
+//
+// Helper channels are disabled by default.
+//
+
+static ChannelEndPoint cupsPort;
+static ChannelEndPoint auxPort;
+static ChannelEndPoint smbPort;
+static ChannelEndPoint mediaPort;
+static ChannelEndPoint httpPort;
+static ChannelEndPoint slavePort;
+
+//
+// Can be either a port number or a Unix
+// socket.
+//
+
+static char fontPort[DEFAULT_STRING_LENGTH] = { 0 };
+
+//
+// Host and port where the existing proxy
+// is running.
+//
+
+static char bindHost[DEFAULT_STRING_LENGTH] = { 0 };
+static int bindPort = -1;
+
+//
+// Pointers to the callback functions and
+// parameter set by the agent
+//
+
+static void (*flushCallback)(void *, int) = NULL;
+static void *flushParameter = NULL;
+
+static void (*statisticsCallback)(void *, int) = NULL;
+static void *statisticsParameter = NULL;
+
+//
+// State variables shared between the init
+// function and the main loop.
+//
+
+T_timestamp initTs;
+T_timestamp startTs;
+T_timestamp logsTs;
+T_timestamp nowTs;
+
+//
+// This is set to the main proxy process id.
+//
+
+int lastProxy = 0;
+
+//
+// Set to last dialog process launched by proxy.
+//
+
+int lastDialog = 0;
+
+//
+// Set to watchdog process launched by proxy.
+//
+
+int lastWatchdog = 0;
+
+//
+// Set if a cache house-keeper process is running.
+//
+
+int lastKeeper = 0;
+
+//
+// Let an inner routine register the pid of a slave
+// process.
+//
+
+static int lastChild = 0;
+
+//
+// Exit code of the last child process exited.
+//
+
+static int lastStatus = 0;
+
+//
+// Set if shutdown was requested through a signal.
+//
+
+static int lastKill = 0;
+
+//
+// Set if the agent confirmed the destruction of
+// the NX transport.
+//
+
+static int lastDestroy = 0;
+
+//
+// This is set to the code and local flag of the
+// last requested alert.
+//
+
+static struct
+{
+ int code;
+ int local;
+
+} lastAlert;
+
+//
+// Manage the current signal masks.
+//
+
+static struct
+{
+ sigset_t saved;
+
+ int blocked;
+ int installed;
+
+ int enabled[32];
+ int forward[32];
+
+ struct sigaction action[32];
+
+} lastMasks;
+
+//
+// Manage the current timer.
+//
+
+static struct
+{
+ struct sigaction action;
+ struct itimerval value;
+ struct timeval start;
+ struct timeval next;
+
+} lastTimer;
+
+//
+// This is set to last signal received in handler.
+//
+
+static int lastSignal = 0;
+
+//
+// Set to the last time bytes readable were queried
+// by the agent.
+//
+
+static T_timestamp lastReadableTs = nullTimestamp();
+
+//
+// Here are interfaces declared in NX.h.
+//
+
+int NXTransProxy(int fd, int mode, const char* options)
+{
+ //
+ // Let the log temporarily go to the standard
+ // error. Be also sure we have a jump context,
+ // in the case any subsequent operation will
+ // cause a cleanup.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (setjmp(context) == 1)
+ {
+ #ifdef TEST
+ *logofs << "NXTransProxy: Out of the long jump with pid '"
+ << lastProxy << "'.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Check if have already performed a parsing of
+ // parameters, as in the case we are running as
+ // a stand-alone process. If needed create the
+ // parameters repository
+ //
+
+ if (control == NULL)
+ {
+ control = new Control();
+ }
+
+ lastProxy = getpid();
+
+ #ifdef TEST
+ *logofs << "NXTransProxy: Main process started with pid '"
+ << lastProxy << "'.\n" << logofs_flush;
+ #endif
+
+ SetMode(mode);
+
+ if (mode == NX_MODE_CLIENT)
+ {
+ if (fd != NX_FD_ANY)
+ {
+ #ifdef TEST
+
+ *logofs << "NXTransProxy: Agent descriptor for X client connections is FD#"
+ << fd << ".\n" << logofs_flush;
+
+ *logofs << "NXTransProxy: Disabling listening on further X client connections.\n"
+ << logofs_flush;
+
+ #endif
+
+ useTcpSocket = 0;
+ useUnixSocket = 0;
+ useAgentSocket = 1;
+
+ agentFD[1] = fd;
+ }
+ }
+ else if (mode == NX_MODE_SERVER)
+ {
+ if (fd != NX_FD_ANY)
+ {
+ #ifdef TEST
+ *logofs << "NXTransProxy: PANIC! Agent descriptor for X server connections "
+ << "not supported yet.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Agent descriptor for X server connections "
+ << "not supported yet.\n";
+
+ return -1;
+ }
+ }
+
+ const char *env = GetOptions(options);
+
+ if (ParseEnvironmentOptions(env, 0) < 0)
+ {
+ cerr << "Error" << ": Parsing of NX transport options failed.\n";
+
+ return -1;
+ }
+
+ //
+ // Set the path of the NX directories.
+ //
+
+ SetDirectories();
+
+ //
+ // Open the log files.
+ //
+
+ SetLogs();
+
+ #ifdef TEST
+ *logofs << "NXTransProxy: Going to run the NX transport loop.\n"
+ << logofs_flush;
+ #endif
+
+ WaitCleanup();
+
+ //
+ // This function should never return.
+ //
+
+ exit(0);
+}
+
+void NXTransExit(int code)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ static int recurse;
+
+ if (++recurse > 1)
+ {
+ #ifdef TEST
+ *logofs << "NXTransExit: Aborting process with pid '"
+ << getpid() << "' due to recursion through "
+ << "exit.\n" << logofs_flush;
+ #endif
+
+ abort();
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransExit: Process with pid '"
+ << getpid() << "' called exit with code '"
+ << code << "'.\n" << logofs_flush;
+ #endif
+
+ if (control != NULL)
+ {
+ //
+ // Be sure that there we can detect the
+ // termination of the watchdog.
+ //
+
+ EnableSignals();
+
+ //
+ // Close the NX transport if it was not
+ // shut down already.
+ //
+
+ NXTransDestroy(NX_FD_ANY);
+ }
+
+ exit(code);
+}
+
+int NXTransParseCommandLine(int argc, const char **argv)
+{
+ return ParseCommandLineOptions(argc, argv);
+}
+
+int NXTransParseEnvironment(const char *env, int force)
+{
+ return ParseEnvironmentOptions(env, force);
+}
+
+void NXTransCleanup()
+{
+ HandleCleanup();
+}
+
+void NXTransCleanupForReconnect()
+{
+ HandleCleanupForReconnect();
+}
+
+//
+// Check the parameters for subsequent
+// initialization of the NX transport.
+//
+
+int NXTransCreate(int fd, int mode, const char* options)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ //
+ // Be sure we have a jump context, in the
+ // case a subsequent operation will cause
+ // a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ //
+ // Create the parameters repository
+ //
+
+ if (control != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "NXTransCreate: PANIC! The NX transport seems "
+ << "to be already running.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The NX transport seems "
+ << "to be already running.\n";
+
+ return -1;
+ }
+
+ control = new Control();
+
+ if (control == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the NX transport.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the NX transport.\n";
+
+ return -1;
+ }
+
+ lastProxy = getpid();
+
+ #ifdef TEST
+ *logofs << "NXTransCreate: Caller process running with pid '"
+ << lastProxy << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Set the local proxy mode an parse the
+ // display NX options.
+ //
+
+ SetMode(mode);
+
+ const char *env = GetOptions(options);
+
+ if (ParseEnvironmentOptions(env, 0) < 0)
+ {
+ cerr << "Error" << ": Parsing of NX transport options failed.\n";
+
+ return -1;
+ }
+
+ //
+ // Set the path of the NX directories.
+ //
+
+ SetDirectories();
+
+ //
+ // Open the log files.
+ //
+
+ SetLogs();
+
+ //
+ // Use the provided descriptor.
+ //
+
+ proxyFD = fd;
+
+ #ifdef TEST
+ *logofs << "NXTransCreate: Called with NX proxy descriptor '"
+ << proxyFD << "'.\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "NXTransCreate: Creation of the NX transport completed.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// Tell the proxy to use the descriptor as the internal
+// connection to the X client side NX agent. This will
+// have the side effect of disabling listening for add-
+// itional X client connections.
+//
+
+int NXTransAgent(int fd[2])
+{
+ //
+ // Be sure we have a jump context, in the
+ // case a subsequent operation will cause
+ // a cleanup.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ if (control == NULL)
+ {
+ cerr << "Error" << ": Can't set the NX agent without a NX transport.\n";
+
+ return -1;
+ }
+ else if (control -> ProxyMode != proxy_client)
+ {
+ #ifdef PANIC
+ *logofs << "NXTransAgent: Invalid mode while setting the NX agent.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid mode while setting the NX agent.\n\n";
+
+ return -1;
+ }
+
+ useTcpSocket = 0;
+ useUnixSocket = 0;
+ useAgentSocket = 1;
+
+ agentFD[0] = fd[0];
+ agentFD[1] = fd[1];
+
+ #ifdef TEST
+
+ *logofs << "NXTransAgent: Internal descriptors for agent are FD#"
+ << agentFD[0] << " and FD#" << agentFD[1] << ".\n"
+ << logofs_flush;
+
+ *logofs << "NXTransAgent: Disabling listening for further X client "
+ << "connections.\n" << logofs_flush;
+
+ #endif
+
+ agent = new Agent(agentFD);
+
+ if (agent == NULL || agent -> isValid() != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the NX memory transport .\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the NX memory transport.\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransAgent: Enabling memory-to-memory transport.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int NXTransClose(int fd)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ /*
+ * Only handle the proxy connection. The X
+ * transport will take care of closing its
+ * end of the socket pair.
+ */
+
+ if (control != NULL && ((agent != NULL &&
+ (fd == agentFD[0] || fd == NX_FD_ANY)) ||
+ (fd == proxyFD || fd == NX_FD_ANY)))
+ {
+ if (proxy != NULL)
+ {
+ #ifdef TEST
+ *logofs << "NXTransClose: Closing down all the X connections.\n"
+ << logofs_flush;
+ #endif
+
+ CleanupConnections();
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransClose: The NX transport is not running.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+//
+// Close down the transport and free the
+// allocated NX resources.
+//
+
+int NXTransDestroy(int fd)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control != NULL && ((agent != NULL &&
+ (fd == agentFD[0] || fd == NX_FD_ANY)) ||
+ (fd == proxyFD || fd == NX_FD_ANY)))
+ {
+ //
+ // Shut down the X connections and
+ // wait the cleanup to complete.
+ //
+
+ if (proxy != NULL)
+ {
+ #ifdef TEST
+ *logofs << "NXTransDestroy: Closing down all the X connections.\n"
+ << logofs_flush;
+ #endif
+
+ CleanupConnections();
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransDestroy: Waiting for the NX transport to terminate.\n"
+ << logofs_flush;
+ #endif
+
+ lastDestroy = 1;
+
+ WaitCleanup();
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "NXTransDestroy: The NX transport is not running.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+//
+// Assume that the NX transport is valid
+// as long as the control class has not
+// been destroyed.
+//
+
+int NXTransRunning(int fd)
+{
+ return (control != NULL);
+}
+
+int NXTransContinue(struct timeval *selectTs)
+{
+ if (control != NULL)
+ {
+ //
+ // If no timeout is provided use
+ // the default.
+ //
+
+ T_timestamp newTs;
+
+ if (selectTs == NULL)
+ {
+ setTimestamp(newTs, control -> PingTimeout);
+
+ selectTs = &newTs;
+ }
+
+ //
+ // Use empty masks and only get the
+ // descriptors set by the proxy.
+ //
+
+ fd_set readSet;
+ fd_set writeSet;
+
+ int setFDs;
+ int errorFDs;
+ int resultFDs;
+
+ setFDs = 0;
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+
+ //
+ // Run a new loop. If the transport
+ // is gone avoid sleeping until the
+ // timeout.
+ //
+
+ if (NXTransPrepare(&setFDs, &readSet, &writeSet, selectTs) != 0)
+ {
+ NXTransSelect(&resultFDs, &errorFDs, &setFDs, &readSet, &writeSet, selectTs);
+
+ NXTransExecute(&resultFDs, &errorFDs, &setFDs, &readSet, &writeSet, selectTs);
+ }
+ }
+
+ return (control != NULL);
+}
+
+int NXTransSignal(int signal, int action)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control == NULL)
+ {
+ return 0;
+ }
+
+ if (action == NX_SIGNAL_RAISE)
+ {
+ #ifdef TEST
+ *logofs << "NXTransSignal: Raising signal '" << DumpSignal(signal)
+ << "' in the proxy handler.\n" << logofs_flush;
+ #endif
+
+ HandleSignal(signal);
+
+ return 1;
+ }
+ else if (signal == NX_SIGNAL_ANY)
+ {
+ #ifdef TEST
+ *logofs << "NXTransSignal: Setting action of all signals to '"
+ << action << "'.\n" << logofs_flush;
+ #endif
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (CheckSignal(i) == 1)
+ {
+ NXTransSignal(i, action);
+ }
+ }
+
+ return 1;
+ }
+ else if (CheckSignal(signal) == 1)
+ {
+ #ifdef TEST
+ *logofs << "NXTransSignal: Setting action of signal '"
+ << DumpSignal(signal) << "' to '" << action
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (action == NX_SIGNAL_ENABLE ||
+ action == NX_SIGNAL_FORWARD)
+ {
+ InstallSignal(signal, action);
+
+ return 1;
+ }
+ else if (action == NX_SIGNAL_DISABLE)
+ {
+ RestoreSignal(signal);
+
+ return 1;
+ }
+ }
+
+ #ifdef WARNING
+ *logofs << "NXTransSignal: WARNING! Unable to perform action '"
+ << action << "' on signal '" << DumpSignal(signal)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unable to perform action '" << action
+ << "' on signal '" << DumpSignal(signal)
+ << "'.\n";
+
+ return -1;
+}
+
+int NXTransCongestion(int fd)
+{
+ if (control != NULL && proxy != NULL)
+ {
+ #ifdef DUMP
+
+ int congestion = proxy -> getCongestion(proxyFD);
+
+ *logofs << "NXTransCongestion: Returning " << congestion
+ << " as current congestion level.\n" << logofs_flush;
+
+ return congestion;
+
+ #endif
+
+ return (proxy -> getCongestion(proxyFD));
+ }
+
+ return 0;
+}
+
+int NXTransHandler(int fd, int type, void (*handler)(void *parameter,
+ int reason), void *parameter)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ switch (type)
+ {
+ case NX_HANDLER_FLUSH:
+ {
+ flushCallback = handler;
+ flushParameter = parameter;
+
+ break;
+ }
+ case NX_HANDLER_STATISTICS:
+ {
+ //
+ // Reporting of statistics by the agent
+ // still needs to be implemented.
+ //
+
+ statisticsCallback = handler;
+ statisticsParameter = parameter;
+
+ break;
+ }
+ default:
+ {
+ #ifdef TEST
+ *logofs << "NXTransHandler: WARNING! Failed to set "
+ << "the NX callback for event '" << type << "' to '"
+ << (void *) handler << "' and parameter '"
+ << parameter << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "NXTransHandler: Set the NX "
+ << "callback for event '" << type << "' to '"
+ << (void *) handler << "' and parameter '"
+ << parameter << "'.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int NXTransRead(int fd, char *data, int size)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control != NULL && agent != NULL &&
+ fd == agentFD[0])
+ {
+ #ifdef DUMP
+ *logofs << "NXTransRead: Dequeuing " << size << " bytes "
+ << "from FD#" << agentFD[0] << ".\n" << logofs_flush;
+ #endif
+
+ int result = agent -> dequeueData(data, size);
+
+ #ifdef DUMP
+
+ if (result < 0 && EGET() == EAGAIN)
+ {
+ *logofs << "NXTransRead: WARNING! Dequeuing from FD#"
+ << agentFD[0] << " would block.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "NXTransRead: Dequeued " << result << " bytes "
+ << "to FD#" << agentFD[0] << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ return result;
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "NXTransRead: Reading " << size << " bytes "
+ << "from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ return read(fd, data, size);
+ }
+}
+
+int NXTransReadVector(int fd, struct iovec *iovdata, int iovsize)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control != NULL && agent != NULL &&
+ fd == agentFD[0])
+ {
+ #if defined(DUMP)
+
+ if (control -> ProxyStage >= stage_operational &&
+ agent -> localReadable() > 0)
+ {
+ *logofs << "NXTransReadVector: WARNING! Agent has data readable.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ char *base;
+
+ int length;
+ int result;
+
+ struct iovec *vector = iovdata;
+ int count = iovsize;
+
+ ESET(0);
+
+ int i = 0;
+ int total = 0;
+
+ for (; i < count; i++, vector++)
+ {
+ length = vector -> iov_len;
+ base = (char *) vector -> iov_base;
+
+ while (length > 0)
+ {
+ #ifdef DUMP
+ *logofs << "NXTransReadVector: Dequeuing " << length
+ << " bytes " << "from FD#" << agentFD[0] << ".\n"
+ << logofs_flush;
+ #endif
+
+ result = agent -> dequeueData(base, length);
+
+ #ifdef DUMP
+
+ if (result < 0 && EGET() == EAGAIN)
+ {
+ *logofs << "NXTransReadVector: WARNING! Dequeuing from FD#"
+ << agentFD[0] << " would block.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "NXTransReadVector: Dequeued " << result
+ << " bytes " << "from FD#" << agentFD[0] << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ if (result < 0 && total == 0)
+ {
+ return result;
+ }
+ else if (result <= 0)
+ {
+ return total;
+ }
+
+ ESET(0);
+
+ length -= result;
+ total += result;
+ base += result;
+ }
+ }
+
+ return total;
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "NXTransReadVector: Reading vector with "
+ << iovsize << " elements from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ return readv(fd, iovdata, iovsize);
+ }
+}
+
+int NXTransReadable(int fd, int *readable)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control == NULL || agent == NULL ||
+ fd != agentFD[0])
+ {
+ #ifdef DUMP
+
+ int result = GetBytesReadable(fd, readable);
+
+ if (result == -1)
+ {
+ *logofs << "NXTransReadable: Error detected on FD#"
+ << fd << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "NXTransReadable: Returning " << *readable
+ << " bytes as readable from FD#" << fd
+ << ".\n" << logofs_flush;
+ }
+
+ return result;
+
+ #else
+
+ return GetBytesReadable(fd, readable);
+
+ #endif
+ }
+
+ int result = agent -> dequeuableData();
+
+ switch (result)
+ {
+ case 0:
+ {
+ //
+ // The client might have enqueued data to our side
+ // and is now checking for the available events. As
+ // _XEventsQueued() may omit to call _XSelect(), we
+ // handle here the new data that is coming from the
+ // proxy to avoid spinning through this function
+ // again.
+ //
+
+ if (proxy != NULL && proxy -> canRead() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransReadable: WARNING! Trying to "
+ << "read to generate new agent data.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Set the context as the function
+ // can cause a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ if (proxy -> handleRead() < 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransReadable: Failure reading "
+ << "messages from proxy FD#" << proxyFD
+ << ".\n" << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+
+ //
+ // Call again the routine. By reading
+ // new control messages from the proxy
+ // the agent channel may be gone.
+ //
+
+ return NXTransReadable(fd, readable);
+ }
+
+ #ifdef DUMP
+ *logofs << "NXTransReadable: Returning " << 0
+ << " bytes as readable from FD#" << fd
+ << " with result 0.\n" << logofs_flush;
+ #endif
+
+ *readable = 0;
+
+ return 0;
+ }
+ case -1:
+ {
+ #ifdef DUMP
+ *logofs << "NXTransReadable: Returning " << 0
+ << " bytes as readable from FD#" << fd
+ << " with result -1.\n" << logofs_flush;
+ #endif
+
+ *readable = 0;
+
+ return -1;
+ }
+ default:
+ {
+ #ifdef DUMP
+ *logofs << "NXTransReadable: Returning " << result
+ << " bytes as readable from FD#" << fd
+ << " with result 0.\n" << logofs_flush;
+ #endif
+
+ *readable = result;
+
+ return 0;
+ }
+ }
+}
+
+int NXTransWrite(int fd, char *data, int size)
+{
+ //
+ // Be sure we have a valid log file.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (control != NULL && agent != NULL &&
+ fd == agentFD[0])
+ {
+ int result;
+
+ if (proxy != NULL)
+ {
+ if (proxy -> canRead(agentFD[1]) == 0)
+ {
+ #if defined(DUMP) || defined(TEST)
+ *logofs << "NXTransWrite: WARNING! Delayed enqueuing to FD#"
+ << agentFD[0] << " with proxy unable to read.\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+
+ return -1;
+ }
+
+ //
+ // Set the context as the function
+ // can cause a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ //
+ // Don't enqueue the data to the transport
+ // but let the channel borrow the buffer.
+ //
+
+ #ifdef DUMP
+ *logofs << "NXTransWrite: Letting the channel borrow "
+ << size << " bytes from FD#" << agentFD[0]
+ << ".\n" << logofs_flush;
+ #endif
+
+ result = proxy -> handleRead(agentFD[1], data, size);
+
+ if (result == 1)
+ {
+ result = size;
+ }
+ else
+ {
+ if (result == 0)
+ {
+ ESET(EAGAIN);
+ }
+ else
+ {
+ ESET(EPIPE);
+ }
+
+ result = -1;
+ }
+ }
+ else
+ {
+ //
+ // We don't have a proxy connection, yet.
+ // Enqueue the data to the agent transport.
+ //
+
+ #ifdef DUMP
+ *logofs << "NXTransWrite: Enqueuing " << size << " bytes "
+ << "to FD#" << agentFD[0] << ".\n" << logofs_flush;
+ #endif
+
+ result = agent -> enqueueData(data, size);
+ }
+
+ #ifdef DUMP
+
+ if (result < 0)
+ {
+ if (EGET() == EAGAIN)
+ {
+ *logofs << "NXTransWrite: WARNING! Enqueuing to FD#"
+ << agentFD[0] << " would block.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "NXTransWrite: WARNING! Error enqueuing to FD#"
+ << agentFD[0] << ".\n" << logofs_flush;
+ }
+ }
+ else
+ {
+ *logofs << "NXTransWrite: Enqueued " << result << " bytes "
+ << "to FD#" << agentFD[0] << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ return result;
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "NXTransWrite: Writing " << size << " bytes "
+ << "to FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ return write(fd, data, size);
+ }
+}
+
+int NXTransWriteVector(int fd, struct iovec *iovdata, int iovsize)
+{
+ //
+ // Be sure we have a valid log file and a
+ // jump context because we will later call
+ // functions that can perform a cleanup.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ int result = 0;
+
+ if (control != NULL && agent != NULL &&
+ fd == agentFD[0])
+ {
+ //
+ // See the comment in NXTransWrite().
+ //
+
+ if (proxy != NULL)
+ {
+ if (proxy -> canRead(agentFD[1]) == 0)
+ {
+ #if defined(DUMP) || defined(TEST)
+ *logofs << "NXTransWriteVector: WARNING! Delayed enqueuing to FD#"
+ << agentFD[0] << " with proxy unable to read.\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+
+ return -1;
+ }
+ }
+
+ //
+ // Set the context as the function
+ // can cause a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ char *base;
+
+ int length;
+
+ struct iovec *vector = iovdata;
+ int count = iovsize;
+
+ ESET(0);
+
+ int i = 0;
+ int total = 0;
+
+ for (; i < count; i++, vector++)
+ {
+ length = vector -> iov_len;
+ base = (char *) vector -> iov_base;
+
+ while (length > 0)
+ {
+ if (proxy != NULL)
+ {
+ //
+ // Don't enqueue the data to the transport
+ // but let the channel borrow the buffer.
+ //
+
+ #ifdef DUMP
+ *logofs << "NXTransWriteVector: Letting the channel borrow "
+ << length << " bytes from FD#" << agentFD[0]
+ << ".\n" << logofs_flush;
+ #endif
+
+ result = proxy -> handleRead(agentFD[1], base, length);
+
+ if (result == 1)
+ {
+ result = length;
+ }
+ else
+ {
+ if (result == 0)
+ {
+ ESET(EAGAIN);
+ }
+ else
+ {
+ ESET(EPIPE);
+ }
+
+ result = -1;
+ }
+ }
+ else
+ {
+ //
+ // We don't have a proxy connection, yet.
+ // Enqueue the data to the agent transport.
+ //
+
+ #ifdef DUMP
+ *logofs << "NXTransWriteVector: Enqueuing " << length
+ << " bytes " << "to FD#" << agentFD[0] << ".\n"
+ << logofs_flush;
+ #endif
+
+ result = agent -> enqueueData(base, length);
+ }
+
+ #ifdef DUMP
+
+ if (result < 0)
+ {
+ if (EGET() == EAGAIN)
+ {
+ *logofs << "NXTransWriteVector: WARNING! Enqueuing to FD#"
+ << agentFD[0] << " would block.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "NXTransWriteVector: WARNING! Error enqueuing to FD#"
+ << agentFD[0] << ".\n" << logofs_flush;
+ }
+ }
+ else
+ {
+ *logofs << "NXTransWriteVector: Enqueued " << result
+ << " bytes " << "to FD#" << agentFD[0] << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ if (result < 0 && total == 0)
+ {
+ return result;
+ }
+ else if (result <= 0)
+ {
+ return total;
+ }
+
+ ESET(0);
+
+ length -= result;
+ total += result;
+ base += result;
+ }
+ }
+
+ return total;
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "NXTransWriteVector: Writing vector with "
+ << iovsize << " elements to FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ return writev(fd, iovdata, iovsize);
+ }
+}
+
+int NXTransPolicy(int fd, int type)
+{
+ if (control != NULL)
+ {
+ if (usePolicy == -1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransPolicy: Setting flush policy on "
+ << "proxy FD#" << proxyFD << " to '"
+ << DumpPolicy(type == NX_POLICY_DEFERRED ?
+ policy_deferred : policy_immediate)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ control -> FlushPolicy = (type == NX_POLICY_DEFERRED ?
+ policy_deferred : policy_immediate);
+
+ if (proxy != NULL)
+ {
+ proxy -> handleFlush();
+ }
+
+ return 1;
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransPolicy: Ignoring the agent "
+ << "setting with user policy set to '"
+ << DumpPolicy(control -> FlushPolicy)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+int NXTransFlushable(int fd)
+{
+ if (proxy == NULL || agent == NULL ||
+ fd != agentFD[0])
+ {
+ #ifdef DUMP
+ *logofs << "NXTransFlushable: Returning 0 bytes as "
+ << "flushable for unrecognized FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #if defined(DUMP) || defined(INFO)
+ *logofs << "NXTransFlushable: Returning " << proxy ->
+ getFlushable(proxyFD) << " as bytes flushable on "
+ << "proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ return proxy -> getFlushable(proxyFD);
+ }
+}
+
+int NXTransFlush(int fd)
+{
+ if (proxy != NULL)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransFlush: Requesting an immediate flush of "
+ << "proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ return proxy -> handleFlush();
+ }
+
+ return 0;
+}
+
+int NXTransChannel(int fd, int channelFd, int type)
+{
+ if (proxy != NULL)
+ {
+ //
+ // Set the context as the function
+ // can cause a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "NXTransChannel: Going to create a new channel "
+ << "with type '" << type << "' on FD#" << channelFd
+ << ".\n" << logofs_flush;
+ #endif
+
+ int result = -1;
+
+ switch (type)
+ {
+ case NX_CHANNEL_X11:
+ {
+ if (useUnixSocket == 1 || useTcpSocket == 1 ||
+ useAgentSocket == 1 || useAuxSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_x11, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_CUPS:
+ {
+ if (useCupsSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_cups, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_SMB:
+ {
+ if (useSmbSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_smb, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_MEDIA:
+ {
+ if (useMediaSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_media, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_HTTP:
+ {
+ if (useHttpSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_http, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_FONT:
+ {
+ if (useFontSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_font, channelFd);
+ }
+
+ break;
+ }
+ case NX_CHANNEL_SLAVE:
+ {
+ if (useSlaveSocket == 1)
+ {
+ result = proxy -> handleNewConnection(channel_slave, channelFd);
+ }
+
+ break;
+ }
+ default:
+ {
+ #ifdef WARNING
+ *logofs << "NXTransChannel: WARNING! Unrecognized channel "
+ << "type '" << type << "'.\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+ }
+
+ #ifdef WARNING
+
+ if (result != 1)
+ {
+ *logofs << "NXTransChannel: WARNING! Could not create the "
+ << "new channel with type '" << type << "' on FD#"
+ << channelFd << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ return result;
+ }
+
+ return 0;
+}
+
+const char *NXTransFile(int type)
+{
+ char *name = NULL;
+
+ switch (type)
+ {
+ case NX_FILE_SESSION:
+ {
+ name = sessionFileName;
+
+ break;
+ }
+ case NX_FILE_ERRORS:
+ {
+ name = errorsFileName;
+
+ break;
+ }
+ case NX_FILE_OPTIONS:
+ {
+ name = optionsFileName;
+
+ break;
+ }
+ case NX_FILE_STATS:
+ {
+ name = statsFileName;
+
+ break;
+ }
+ }
+
+ if (name != NULL && *name != '\0')
+ {
+ return name;
+ }
+
+ return NULL;
+}
+
+long NXTransTime()
+{
+ static T_timestamp last = getTimestamp();
+
+ T_timestamp now = getTimestamp();
+
+ long diff = diffTimestamp(last, now);
+
+ last = now;
+
+ return diff;
+}
+
+int NXTransAlert(int code, int local)
+{
+ if (proxy != NULL)
+ {
+ #if defined(DUMP) || defined(INFO)
+ *logofs << "NXTransAlert: Requesting a NX dialog with code "
+ << code << " and local " << local << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (local == 0)
+ {
+ //
+ // Set the context as the function
+ // can cause a cleanup.
+ //
+
+ if (setjmp(context) == 1)
+ {
+ return -1;
+ }
+
+ proxy -> handleAlert(code);
+ }
+ else
+ {
+ //
+ // Show the alert at the next loop.
+ //
+
+ HandleAlert(code, local);
+ }
+
+ return 1;
+ }
+ #if defined(DUMP) || defined(INFO)
+ else
+ {
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ *logofs << "NXTransAlert: Can't request an alert without "
+ << "a valid NX transport.\n" << logofs_flush;
+ }
+ #endif
+
+ return 0;
+}
+
+//
+// Prepare the file sets and the timeout
+// for a later execution of the select().
+//
+
+int NXTransPrepare(int *setFDs, fd_set *readSet,
+ fd_set *writeSet, struct timeval *selectTs)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ //
+ // Control is NULL if the NX transport was
+ // reset or was never created. If control
+ // is valid then prepare to jump back when
+ // the transport is destroyed.
+ //
+
+ if (control == NULL || setjmp(context) == 1)
+ {
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "\nNXTransPrepare: Going to prepare the NX transport.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> ProxyStage < stage_operational)
+ {
+ handleNegotiationInLoop(*setFDs, *readSet, *writeSet, *selectTs);
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (isTimestamp(*selectTs) == 0)
+ {
+ *logofs << "Loop: WARNING! Preparing the select with requested "
+ << "timeout of " << selectTs -> tv_sec << " S and "
+ << (double) selectTs -> tv_usec / 1000 << " Ms.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Preparing the select with requested "
+ << "timeout of " << selectTs -> tv_sec << " S and "
+ << (double) selectTs -> tv_usec / 1000 << " Ms.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Set descriptors of listening sockets.
+ //
+
+ handleSetListenersInLoop(*readSet, *setFDs);
+
+ //
+ // Set descriptors of both proxy and X
+ // connections.
+ //
+
+ handleSetReadInLoop(*readSet, *setFDs, *selectTs);
+
+ //
+ // Find out which file descriptors have
+ // data to write.
+ //
+
+ handleSetWriteInLoop(*writeSet, *setFDs, *selectTs);
+ }
+
+ //
+ // Prepare the masks for handling the memory-
+ // to-memory transport. This is required even
+ // during session negotiation.
+ //
+
+ if (agent != NULL)
+ {
+ handleSetAgentInLoop(*setFDs, *readSet, *writeSet, *selectTs);
+ }
+
+ //
+ // Register time spent handling messages.
+ //
+
+ nowTs = getNewTimestamp();
+
+ int diffTs = diffTimestamp(startTs, nowTs);
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 0 - at " << strMsTimestamp()
+ << " with " << diffTs << " Ms elapsed.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // TODO: Should add the read time in two
+ // parts otherwise the limits are checked
+ // before the counters are updated with
+ // time spent in the last loop.
+ //
+
+ if (control -> ProxyStage >= stage_operational)
+ {
+ statistics -> addReadTime(diffTs);
+ }
+
+ startTs = nowTs;
+
+ #ifdef DEBUG
+ *logofs << "Loop: New timestamp is " << strMsTimestamp(startTs)
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// Let's say that we call select() to find out
+// if any of the handled descriptors has data,
+// but actually things are a bit more complex
+// than that.
+//
+
+int NXTransSelect(int *resultFDs, int *errorFDs, int *setFDs, fd_set *readSet,
+ fd_set *writeSet, struct timeval *selectTs)
+{
+ int diffTs;
+ #ifdef TIME
+
+ static T_timestamp lastTs;
+
+ #endif
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ //
+ // Control is NULL if the NX transport was
+ // reset or never created. If control is
+ // valid then prepare for jumping back in
+ // the case of an error.
+ //
+
+ if (control == NULL || setjmp(context) == 1)
+ {
+ *resultFDs = select(*setFDs, readSet, writeSet, NULL, selectTs);
+
+ *errorFDs = errno;
+
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "\nNXTransSelect: Going to select the NX descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+
+ handleCheckSelectInLoop(*setFDs, *readSet, *writeSet, *selectTs);
+
+ #endif
+
+ #ifdef TIME
+
+ diffTs = diffTimestamp(lastTs, getNewTimestamp());
+
+ if (diffTs > 20)
+ {
+ *logofs << "Loop: TIME! Spent " << diffTs
+ << " Ms handling messages for proxy FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ }
+
+ lastTs = getNewTimestamp();
+
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+
+ if (isTimestamp(*selectTs) == 0)
+ {
+ *logofs << "Loop: WARNING! Executing the select with requested "
+ << "timeout of " << selectTs -> tv_sec << " S and "
+ << (double) selectTs -> tv_usec / 1000 << " Ms.\n"
+ << logofs_flush;
+ }
+ else if (proxy != NULL && proxy -> getFlushable(proxyFD) > 0)
+ {
+ *logofs << "Loop: WARNING! Proxy FD#" << proxyFD
+ << " has " << proxy -> getFlushable(proxyFD)
+ << " bytes to write but timeout is "
+ << selectTs -> tv_sec << " S and "
+ << selectTs -> tv_usec / 1000 << " Ms.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Wait for the selected sockets
+ // or the timeout.
+ //
+
+ ESET(0);
+
+ *resultFDs = select(*setFDs, readSet, writeSet, NULL, selectTs);
+
+ *errorFDs = EGET();
+
+ #ifdef TIME
+
+ diffTs = diffTimestamp(lastTs, getNewTimestamp());
+
+ if (diffTs > 100)
+ {
+ *logofs << "Loop: TIME! Spent " << diffTs
+ << " Ms waiting for new data for proxy FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ }
+
+ lastTs = getNewTimestamp();
+
+ #endif
+
+ //
+ // Check the result of the select.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ handleCheckResultInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs, startTs);
+
+ #endif
+
+ //
+ // Get time spent in select. The accouting is done
+ // in milliseconds. This is a real problem on fast
+ // machines where each loop is unlikely to take
+ // more than 500 us, so consider that the results
+ // can be inaccurate.
+ //
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(startTs, nowTs);
+
+ #ifdef TEST
+ *logofs << "Loop: Out of select after " << diffTs << " Ms "
+ << "at " << strMsTimestamp(nowTs) << " with result "
+ << *resultFDs << ".\n" << logofs_flush;
+ #endif
+
+ startTs = nowTs;
+
+ #ifdef DEBUG
+ *logofs << "Loop: New timestamp is " << strMsTimestamp(startTs)
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (control -> ProxyStage >= stage_operational)
+ {
+ statistics -> addIdleTime(diffTs);
+ }
+
+ if (*resultFDs < 0)
+ {
+ //
+ // Check if the call was interrupted or if any of the
+ // managed descriptors has become invalid. This can
+ // happen to the X11 code, before the descriptor is
+ // removed from the managed set.
+ //
+
+ #ifdef __sun
+
+ if (*errorFDs == EINTR || *errorFDs == EBADF ||
+ *errorFDs == EINVAL)
+
+ #else
+
+ if (*errorFDs == EINTR || *errorFDs == EBADF)
+
+ #endif
+
+ {
+ #ifdef TEST
+
+ if (*errorFDs == EINTR)
+ {
+ *logofs << "Loop: Select failed due to EINTR error.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: WARNING! Call to select failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ }
+
+ #endif
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to select failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to select failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ return 1;
+}
+
+//
+// Perform the required actions on all
+// the descriptors having I/O pending.
+//
+
+int NXTransExecute(int *resultFDs, int *errorFDs, int *setFDs, fd_set *readSet,
+ fd_set *writeSet, struct timeval *selectTs)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ //
+ // Control is NULL if the NX transport was
+ // reset or never created. If control is
+ // valid then prepare for jumping back in
+ // the case of an error.
+ //
+
+ if (control == NULL || setjmp(context) == 1)
+ {
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "\nNXTransExecute: Going to execute I/O on the NX descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> ProxyStage >= stage_operational)
+ {
+ //
+ // Check if I/O is possible on the proxy and
+ // local agent descriptors.
+ //
+
+ if (agent != NULL)
+ {
+ handleAgentInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 1 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Rotate the channel that will be handled
+ // first.
+ //
+
+ handleRotateInLoop();
+
+ //
+ // Flush any data on newly writable sockets.
+ //
+
+ handleWritableInLoop(*resultFDs, *writeSet);
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 2 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if any socket has become readable.
+ //
+
+ handleReadableInLoop(*resultFDs, *readSet);
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 3 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Handle the scheduled events on channels.
+ //
+ // - Restart, if possible, any client that was
+ // put to sleep.
+ //
+ // - Check if there are pointer motion events to
+ // flush. This applies only to X server side.
+ //
+ // - Check if any channel has exited the conges-
+ // tion state.
+ //
+ // - Check if there are images that are currently
+ // being streamed.
+ //
+
+ handleEventsInLoop();
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 4 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if user sent a signal to produce
+ // statistics.
+ //
+
+ handleStatisticsInLoop();
+
+ //
+ // We may have flushed the proxy link or
+ // handled data coming from the remote.
+ // Post-process the masks and set the
+ // selected agent descriptors as ready.
+ //
+
+ if (agent != NULL)
+ {
+ handleAgentLateInLoop(*resultFDs, *errorFDs, *setFDs, *readSet, *writeSet, *selectTs);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 5 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if there is any data to flush.
+ // Agents should flush the proxy link
+ // explicitly.
+ //
+
+ handleFlushInLoop();
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 6 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+ }
+
+ //
+ // Check if we have an alert to show.
+ //
+
+ handleAlertInLoop();
+
+ if (control -> ProxyStage >= stage_operational)
+ {
+ //
+ // Check if it's time to give up.
+ //
+
+ handleCheckSessionInLoop();
+
+ //
+ // Check if local proxy is consuming
+ // too many resources.
+ //
+
+ handleCheckBitrateInLoop();
+
+ //
+ // Check coherency of internal state.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ handleCheckStateInLoop(*setFDs);
+
+ #endif
+
+ #ifdef TEST
+ *logofs << "Loop: Mark - 7 - at " << strMsTimestamp()
+ << " with " << diffTimestamp(startTs, getTimestamp())
+ << " Ms elapsed.\n" << logofs_flush;
+ #endif
+ }
+
+ //
+ // Truncate the logs if needed.
+ //
+
+ handleLogReopenInLoop(logsTs, nowTs);
+
+ return 1;
+}
+
+//
+// Initialize the connection parameters and
+// prepare for negotiating the link with the
+// remote proxy.
+//
+
+int InitBeforeNegotiation()
+{
+ //
+ // Disable limits on core dumps.
+ //
+
+ SetCore();
+
+ //
+ // Install the signal handlers.
+ //
+
+ InstallSignals();
+
+ //
+ // Track how much time we spent in initialization.
+ //
+
+ nowTs = getNewTimestamp();
+
+ startTs = nowTs;
+ initTs = nowTs;
+
+ #ifdef TEST
+ *logofs << "Loop: INIT! Taking mark for initialization at "
+ << strMsTimestamp(initTs) << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // If not explicitly specified, determine if local
+ // mode is client or server according to parameters
+ // provided so far.
+ //
+
+ if (WE_SET_PROXY_MODE == 0)
+ {
+ cerr << "Error" << ": Please specify either the -C or -S option.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Start a watchdog. If initialization cannot
+ // be completed before timeout, then clean up
+ // everything and exit.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Starting watchdog process with timeout of "
+ << control -> InitTimeout / 1000 << " seconds.\n"
+ << logofs_flush;
+ #endif
+
+ lastWatchdog = NXTransWatchdog(control -> InitTimeout);
+
+ if (IsFailed(lastWatchdog))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't start the NX watchdog process.\n"
+ << logofs_flush;
+ #endif
+
+ SetNotRunning(lastWatchdog);
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Watchdog started with pid '"
+ << lastWatchdog << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+
+ //
+ // Print preliminary info.
+ //
+
+ PrintProcessInfo();
+
+ //
+ // Set cups, multimedia and other
+ // auxiliary ports.
+ //
+
+ SetPorts();
+
+ //
+ // Increase the number of maximum open
+ // file descriptors for this process.
+ //
+
+ SetDescriptors();
+
+ //
+ // Set local endianess.
+ //
+
+ unsigned int test = 1;
+
+ setHostBigEndian(*((unsigned char *) (&test)) == 0);
+
+ #ifdef TEST
+ *logofs << "Loop: Local host is "
+ << (hostBigEndian() ? "big endian" : "little endian")
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ //
+ // Listen on sockets that mimic an X display to
+ // which X clients will be able to connect (e.g.
+ // unix:8 and/or localhost:8).
+ //
+
+ if (useTcpSocket == 1)
+ {
+ SetupTcpSocket();
+ }
+
+ if (useUnixSocket == 1)
+ {
+ SetupUnixSocket();
+ }
+ }
+ else
+ {
+ //
+ // Don't listen for X connections.
+ //
+
+ useUnixSocket = 0;
+ useTcpSocket = 0;
+ useAgentSocket = 0;
+
+ //
+ // Get ready to open the local display.
+ //
+
+ SetupDisplaySocket(xServerAddrFamily, xServerAddr, xServerAddrLength);
+ }
+
+ //
+ // If we are the NX server-side proxy we need to
+ // complete our initializazion. We will mandate
+ // our parameters at the time the NX client will
+ // connect.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ SetParameters();
+ }
+
+ return 1;
+}
+
+int SetupProxyConnection()
+{
+
+ if (proxyFD == -1)
+ {
+
+ char *socketUri = NULL;
+
+ // Let's make sure, the default value for listenSocket is properly set. Doing this
+ // here, because we have to make sure that we call it after the connectSocket
+ // declaration is really really complete.
+
+ if (listenSocket.disabled() && connectSocket.disabled())
+ {
+ char listenPortValue[20] = { 0 };
+ sprintf(listenPortValue, "%ld", (long)(proxyPort + DEFAULT_NX_PROXY_PORT_OFFSET));
+
+ SetAndValidateChannelEndPointArg("local", "listen", listenPortValue, listenSocket);
+ }
+
+ #ifdef TEST
+ connectSocket.getSpec(&socketUri);
+ *logofs << "Loop: connectSocket is "<< ( connectSocket.enabled() ? "enabled" : "disabled") << ". "
+ << "The socket URI is '"<< ( socketUri != NULL ? socketUri : "<unset>") << "'.\n" << logofs_flush;
+ listenSocket.getSpec(&socketUri);
+ *logofs << "Loop: listenSocket is "<< ( listenSocket.enabled() ? "enabled" : "disabled") << ". "
+ << "The socket URI is '"<< ( socketUri != NULL ? socketUri : "<unset>") << "'.\n" << logofs_flush;
+ free(socketUri);
+ socketUri = NULL;
+ #endif
+
+ if (WE_INITIATE_CONNECTION)
+ {
+ if (connectSocket.getSpec(&socketUri))
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to connect to '" << socketUri
+ << "'.\n" << logofs_flush;
+ #endif
+ free(socketUri);
+
+ proxyFD = ConnectToRemote(connectSocket);
+
+ #ifdef TEST
+ *logofs << "Loop: Connected to remote proxy on FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Connected to remote proxy on FD#"
+ << proxyFD << ".\n";
+ }
+ }
+ else
+ {
+
+ if (listenSocket.isTCPSocket() && (listenSocket.getTCPPort() < 0))
+ {
+ listenSocket.setSpec(DEFAULT_NX_PROXY_PORT_OFFSET + proxyPort);
+ }
+
+ if (listenSocket.getSpec(&socketUri))
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to wait for connection at '"
+ << socketUri << "'.\n" << logofs_flush;
+ #endif
+ free(socketUri);
+
+ proxyFD = WaitForRemote(listenSocket);
+
+ #ifdef TEST
+ if (WE_LISTEN_FORWARDER)
+ {
+ *logofs << "Loop: Connected to remote forwarder on FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Connected to remote proxy on FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ }
+ #endif
+
+ }
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Using the inherited connection on FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Set TCP_NODELAY on proxy descriptor
+ // to reduce startup time. Option will
+ // later be disabled if needed.
+ //
+ // either listenSocket or connectSocket is used here...
+
+ if(listenSocket.isTCPSocket() || connectSocket.isTCPSocket())
+
+ SetNoDelay(proxyFD, 1);
+
+ //
+ // We need non-blocking input since the
+ // negotiation phase.
+ //
+
+ SetNonBlocking(proxyFD, 1);
+
+ return 1;
+}
+
+//
+// Create the required proxy and channel classes
+// and get ready for handling the encoded traffic.
+//
+
+int InitAfterNegotiation()
+{
+ #ifdef TEST
+ *logofs << "Loop: Connection with remote proxy completed.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Connection with remote proxy completed.\n"
+ << logofs_flush;
+
+ //
+ // If we are the server proxy we completed
+ // our initializazion phase according to
+ // the values provided by the client side.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ SetParameters();
+ }
+
+ //
+ // Set up the listeners for the additional
+ // services.
+ //
+
+ SetupServiceSockets();
+
+ //
+ // Create the proxy class and the statistics
+ // repository and pass all the configuration
+ // data we negotiated with the remote peer.
+ //
+
+ SetupProxyInstance();
+
+ //
+ // We completed both parsing of user's parameters
+ // and handlshaking with remote proxy. Now print
+ // a brief summary including the most significant
+ // control values.
+ //
+
+ PrintConnectionInfo();
+
+ //
+ // Cancel the initialization watchdog.
+ //
+
+ if (IsRunning(lastWatchdog))
+ {
+ KillProcess(lastWatchdog, "watchdog", SIGTERM, 1);
+
+ SetNotRunning(lastWatchdog);
+
+ lastSignal = 0;
+ }
+
+ //
+ // Start the house-keeper process. It will
+ // remove the oldest persistent caches, if
+ // the amount of storage exceed the limits
+ // set by the user.
+ //
+
+ StartKeeper();
+
+ //
+ // Set the log size check timestamp.
+ //
+
+ nowTs = getNewTimestamp();
+
+ logsTs = nowTs;
+
+ //
+ // TODO: Due to the way the new NX transport is working,
+ // the accounting of time spent handling messages must
+ // be rewritten. In particular, at the moment it only
+ // shows the time spent encoding and decoding messages
+ // in the main loop, after executing a select. It doesn't
+ // take into account the time spent in the NXTrans* calls
+ // where messages can be encoded and decoded implicitly,
+ // on demand of the agent. When the agent transport is
+ // in use, these calls constitute the vast majority of
+ // the encoding activity. The result is that the number
+ // of KB encoded per second shown by the proxy statistics
+ // is actually much lower than the real throughput gene-
+ // rated by the proxy.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: INIT! Completed initialization at "
+ << strMsTimestamp(nowTs) << " with "
+ << diffTimestamp(initTs, nowTs) << " Ms "
+ << "since the init mark.\n" << logofs_flush;
+ #endif
+
+ initTs = getNewTimestamp();
+
+ //
+ // We can now start handling binary data from
+ // our peer proxy.
+ //
+
+ if (agent == NULL)
+ {
+ cerr << "Session" << ": Session started at '"
+ << strTimestamp() << "'.\n";
+ }
+
+ return 1;
+}
+
+int SetMode(int mode)
+{
+ //
+ // Set the local proxy mode.
+ //
+
+ if (control -> ProxyMode == proxy_undefined)
+ {
+ if (mode == NX_MODE_CLIENT)
+ {
+ #ifdef TEST
+ *logofs << "Loop: INIT! Initializing with mode "
+ << "NX_MODE_CLIENT at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ control -> ProxyMode = proxy_client;
+ }
+ else if (mode == NX_MODE_SERVER)
+ {
+ #ifdef TEST
+ *logofs << "Loop: INIT! Initializing with mode "
+ << "NX_MODE_SERVER at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ control -> ProxyMode = proxy_server;
+ }
+ else
+ {
+ cerr << "Error" << ": Please specify either "
+ << "the -C or -S option.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ return 1;
+}
+
+int SetupProxyInstance()
+{
+ if (control -> ProxyMode == proxy_client)
+ {
+ proxy = new ClientProxy(proxyFD);
+ }
+ else
+ {
+ proxy = new ServerProxy(proxyFD);
+ }
+
+ if (proxy == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the NX proxy.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the NX proxy.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Create the statistics repository.
+ //
+
+ statistics = new Statistics(proxy);
+
+ if (statistics == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the NX statistics.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the NX statistics.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // If user gave us a proxy cookie than create the
+ // X11 authorization repository and find the real
+ // cookie to be used for our X display.
+ //
+
+ SetupAuthInstance();
+
+ //
+ // Reset the static members in channels.
+ //
+
+ proxy -> handleChannelConfiguration();
+
+ //
+ // Inform the proxies about the ports where they
+ // will have to forward the network connections.
+ //
+
+ proxy -> handleDisplayConfiguration(displayHost, xServerAddrFamily,
+ xServerAddr, xServerAddrLength);
+
+ proxy -> handlePortConfiguration(cupsPort, smbPort, mediaPort,
+ httpPort, fontPort);
+
+ //
+ // We handed over the sockaddr structure we
+ // created when we set up the display socket
+ // to the proxy.
+ //
+
+ xServerAddr = NULL;
+
+ //
+ // Set socket options on proxy link, then propagate link
+ // configuration to proxy. This includes translating some
+ // control parameters in 'local' and 'remote'. Finally
+ // adjust cache parameters according to pack method and
+ // session type selected by user.
+ //
+
+ if (proxy -> handleSocketConfiguration() < 0 ||
+ proxy -> handleLinkConfiguration() < 0 ||
+ proxy -> handleCacheConfiguration() < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error configuring the NX transport.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error configuring the NX transport.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Load the message stores from the persistent
+ // cache.
+ //
+
+ proxy -> handleLoad(load_if_first);
+
+ //
+ // Inform the proxy that from now on it can
+ // start handling the encoded data.
+ //
+
+ proxy -> setOperational();
+
+ //
+ // Are we going to use an internal IPC connection
+ // with an agent? In this case create the channel
+ // by using the socket descriptor provided by the
+ // caller at the proxy initialization.
+ //
+
+ SetupAgentInstance();
+
+ //
+ // Check if we need to verify the existence of
+ // a matching client cache at shutdown.
+ //
+
+ #ifdef MATCH
+
+ control -> PersistentCacheCheckOnShutdown = 1;
+
+ #endif
+
+ //
+ // Flush any data produced so far.
+ //
+
+ proxy -> handleFlush();
+
+ #if defined(TEST) || defined(INFO)
+
+ if (proxy -> getFlushable(proxyFD) > 0)
+ {
+ *logofs << "Loop: WARNING! Proxy FD#" << proxyFD << " has data "
+ << "to flush after setup of the NX transport.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ return 1;
+}
+
+int SetupAuthInstance()
+{
+ //
+ // If user gave us a proxy cookie, then create the
+ // X11 authorization repository and find the real
+ // cookie to be used for our X display.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ if (authCookie != NULL && *authCookie != '\0')
+ {
+ if (useLaunchdSocket == 1)
+ {
+ //
+ // If we are going to retrieve the X11 autho-
+ // rization through the launchd service, make
+ // a connection to its socket to trigger the
+ // X server starting.
+ //
+
+ sockaddr_un launchdAddrUnix;
+
+ unsigned int launchdAddrLength = sizeof(sockaddr_un);
+
+ int launchdAddrFamily = AF_UNIX;
+
+ launchdAddrUnix.sun_family = AF_UNIX;
+
+ const int launchdAddrNameLength = 108;
+
+ int success = -1;
+
+ strncpy(launchdAddrUnix.sun_path, displayHost, launchdAddrNameLength);
+
+ *(launchdAddrUnix.sun_path + launchdAddrNameLength - 1) = '\0';
+
+ #ifdef TEST
+ *logofs << "Loop: Connecting to launchd service "
+ << "on Unix port '" << displayHost << "'.\n" << logofs_flush;
+ #endif
+
+ int launchdFd = socket(launchdAddrFamily, SOCK_STREAM, PF_UNSPEC);
+
+ if (launchdFd < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ else if ((success = connect(launchdFd, (sockaddr *) &launchdAddrUnix, launchdAddrLength)) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Connection to launchd service "
+ << "on Unix port '" << displayHost << "' failed "
+ << "with error " << EGET() << ", '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (launchdFd >= 0)
+ {
+ close(launchdFd);
+ }
+
+ //
+ // The real cookie will not be available
+ // until the X server starts. Query for the
+ // cookie in a loop, unless the connection
+ // to the launchd service failed.
+ //
+
+ int attempts = (success < 0 ? 1 : 10);
+
+ for (int i = 0; i < attempts; i++)
+ {
+ delete auth;
+
+ auth = new Auth(displayHost, authCookie);
+
+ if (auth != NULL && auth -> isFake() == 1)
+ {
+ usleep(200000);
+
+ continue;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ auth = new Auth(displayHost, authCookie);
+ }
+
+ if (auth == NULL || auth -> isValid() != 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the X authorization.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the X authorization.\n";
+
+ HandleCleanup();
+ }
+ else if (auth -> isFake() == 1)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Could not retrieve the X server "
+ << "authentication cookie.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to read data from the X "
+ << "auth command.\n";
+
+ cerr << "Warning" << ": Generated a fake cookie for X "
+ << "authentication.\n";
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: No proxy cookie was provided for "
+ << "authentication.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": No proxy cookie was provided for "
+ << "authentication.\n";
+
+ #ifdef TEST
+ *logofs << "Loop: Forwarding the real X authorization "
+ << "cookie.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Forwarding the real X authorization "
+ << "cookie.\n";
+ }
+ }
+
+ return 1;
+}
+
+int SetupAgentInstance()
+{
+ if (control -> ProxyMode == proxy_client &&
+ useAgentSocket == 1)
+ {
+ //
+ // This will temporarily disable signals to safely
+ // load the cache, then will send a control packet
+ // to the remote end, telling that cache has to be
+ // loaded, so it's important that proxy is already
+ // set in operational state.
+ //
+
+ int result;
+
+ if (agent != NULL)
+ {
+ result = proxy -> handleNewAgentConnection(agent);
+ }
+ else
+ {
+ result = proxy -> handleNewConnection(channel_x11, agentFD[1]);
+ }
+
+ if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating the NX agent connection.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating the NX agent connection.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ return 1;
+}
+
+int SetupTcpSocket()
+{
+ //
+ // Open TCP socket emulating local display.
+ //
+
+ return ListenConnectionTCP((loopbackBind ? "localhost" : "*"), X_TCP_PORT + proxyPort, "X11");
+}
+
+int SetupUnixSocket()
+{
+ //
+ // Open UNIX domain socket for display.
+ //
+
+ if (!control->TempPath) {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Temporal path is null.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Temporal path is null.\n";
+ HandleCleanup();
+ }
+
+ unsigned int required = snprintf(unixSocketName, DEFAULT_STRING_LENGTH, "%s/.X11-unix", control->TempPath);
+ if (required < sizeof(unixSocketName)) {
+
+ // No need to execute the following actions conditionally
+ mkdir(unixSocketName, (0777 | S_ISVTX));
+ chmod(unixSocketName, (0777 | S_ISVTX));
+
+ required = snprintf(unixSocketName, DEFAULT_STRING_LENGTH, "%s/.X11-unix/X%d", control->TempPath, proxyPort);
+ if (required < sizeof(unixSocketName)) {
+
+ unixFD = ListenConnectionUnix(unixSocketName, "x11");
+ if (unixFD >= 0)
+ chmod(unixSocketName, 0777);
+ return unixFD;
+ }
+ }
+
+ unixSocketName[0] = '\0'; // Just in case!
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! path for unix socket is too long.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": path for Unix socket is too long.\n";
+ HandleCleanup();
+}
+
+//
+// The following is a dumb copy-paste. The
+// nxcompsh library should offer a better
+// implementation.
+//
+
+int SetupDisplaySocket(int &addr_family, sockaddr *&addr,
+ unsigned int &addr_length)
+{
+ addr_family = AF_INET;
+ addr = NULL;
+ addr_length = 0;
+
+ char *display;
+
+ if (*displayHost == '\0')
+ {
+ //
+ // Assume DISPLAY as the X server to which
+ // we will forward the proxied connections.
+ // This means that NX parameters have been
+ // passed through other means.
+ //
+
+ display = getenv("DISPLAY");
+
+ if (display == NULL || *display == '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Host X server DISPLAY is not set.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Host X server DISPLAY is not set.\n";
+
+ HandleCleanup();
+ }
+ else if (strncasecmp(display, "nx/nx,", 6) == 0 ||
+ strncasecmp(display, "nx,", 3) == 0 ||
+ strncasecmp(display, "nx/nx:", 6) == 0 ||
+ strncasecmp(display, "nx:", 3) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! NX transport on host X server '"
+ << display << "' not supported.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": NX transport on host X server '"
+ << display << "' not supported.\n";
+
+ cerr << "Error" << ": Please run the local proxy specifying "
+ << "the host X server to connect to.\n";
+
+ HandleCleanup();
+ }
+ else if (strlen(display) >= DEFAULT_STRING_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Host X server DISPLAY cannot exceed "
+ << DEFAULT_STRING_LENGTH << " characters.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Host X server DISPLAY cannot exceed "
+ << DEFAULT_STRING_LENGTH << " characters.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(displayHost, display);
+ }
+
+ display = new char[strlen(displayHost) + 1];
+
+ if (display == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Out of memory handling DISPLAY variable.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Out of memory handling DISPLAY variable.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(display, displayHost);
+
+ #ifdef __APPLE__
+
+ if ((strncasecmp(display, "/tmp/launch", 11) == 0) || (strncasecmp(display, "/private/tmp/com.apple.launchd", 30) == 0))
+ {
+ #ifdef TEST
+ *logofs << "Loop: Using launchd service on socket '"
+ << display << "'.\n" << logofs_flush;
+ #endif
+
+ useLaunchdSocket = 1;
+ }
+
+ #endif
+
+ char *separator = strrchr(display, ':');
+
+ if ((separator == NULL) || !isdigit(*(separator + 1)))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid display '" << display << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid display '" << display << "'.\n";
+
+ HandleCleanup();
+ }
+
+ *separator = '\0';
+
+ xPort = atoi(separator + 1);
+
+ #ifdef TEST
+ *logofs << "Loop: Using local X display '" << displayHost
+ << "' with host '" << display << "' and port '"
+ << xPort << "'.\n" << logofs_flush;
+ #endif
+
+ #ifdef __APPLE__
+
+ if (separator == display || strcmp(display, "unix") == 0 ||
+ useLaunchdSocket == 1)
+
+ #else
+
+ if (separator == display || strcmp(display, "unix") == 0)
+
+ #endif
+ {
+ //
+ // UNIX domain port.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Using real X server on UNIX domain socket.\n"
+ << logofs_flush;
+ #endif
+
+ sockaddr_un *xServerAddrUNIX = new sockaddr_un;
+
+ addr_family = AF_UNIX;
+ xServerAddrUNIX -> sun_family = AF_UNIX;
+
+ //
+ // The scope of this function is to fill either the sockaddr_un
+ // (when the display is set to the Unix Domain socket) or the
+ // sockaddr_in structure (when connecting by TCP) only once, so
+ // that the structure will be later used at the time the server
+ // proxy will try to forward the connection to the X server. We
+ // don't need to verify that the socket does exist at the pre-
+ // sent moment. The method that forwards the connection will
+ // perform the required checks and will retry, if needed. Anyway
+ // we need to select the name of the socket, so we check if the
+ // well-known directory exists and take that as an indication of
+ // where the socket will be created.
+ //
+
+ // Try abstract X11 socket first (via a test connect), if that fails
+ // fall back to Unix domain socket file.
+
+ #ifdef __linux__
+ int testSocketFD;
+ testSocketFD = socket(addr_family, SOCK_STREAM, PF_UNSPEC);
+
+ int len = sprintf(unixSocketName + 1, "/tmp/.X11-unix/X%d", xPort);
+ unixSocketName[0] = '\0';
+
+ sockaddr_un *xServerAddrABSTRACT = new sockaddr_un;
+ memset(xServerAddrABSTRACT, 0, addr_length);
+ xServerAddrABSTRACT -> sun_family = AF_UNIX;
+ memcpy(xServerAddrABSTRACT -> sun_path, unixSocketName, len+1);
+ addr_length = len +3;
+
+ int ret = connect(testSocketFD, (struct sockaddr *) xServerAddrABSTRACT, addr_length);
+ if (ret == 0) {
+
+ cerr << "Info" << ": Using abstract X11 socket in kernel namespace "
+ << "for accessing DISPLAY=:" << xPort << ".\n";
+
+ close(testSocketFD);
+ addr = (sockaddr *) xServerAddrABSTRACT;
+ return 1;
+
+ } else {
+
+ cerr << "Info" << ": Falling back to file system X11 socket "
+ << "for accessing DISPLAY=:" << xPort << ".\n";
+
+ #endif
+
+ struct stat statInfo;
+
+ char unixSocketDir[DEFAULT_STRING_LENGTH];
+
+ snprintf(unixSocketDir, DEFAULT_STRING_LENGTH - 1, "%s/.X11-unix",
+ control -> TempPath);
+
+ #ifdef __APPLE__
+
+ if (useLaunchdSocket == 1)
+ {
+ char *slash = rindex(display, '/');
+
+ if (slash != NULL)
+ {
+ *slash = '\0';
+ }
+
+ snprintf(unixSocketDir, DEFAULT_STRING_LENGTH - 1, "%s", display);
+ }
+
+ #endif
+
+ *(unixSocketDir + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming X socket in directory '"
+ << unixSocketDir << "'.\n" << logofs_flush;
+ #endif
+
+ if (stat(unixSocketDir, &statInfo) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't determine the location of "
+ << "the X display socket.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't determine the location of "
+ << "the X display socket.\n";
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error " << EGET() << " '" << ESTR()
+ << "' checking '" << unixSocketDir << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error " << EGET() << " '" << ESTR()
+ << "' checking '" << unixSocketDir << "'.\n";
+
+ HandleCleanup();
+ }
+
+ sprintf(unixSocketName, "%s/X%d", unixSocketDir, xPort);
+
+ #ifdef __APPLE__
+
+ if (useLaunchdSocket == 1)
+ {
+ strncpy(unixSocketName, displayHost, DEFAULT_STRING_LENGTH - 1);
+ }
+
+ #endif
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming X socket name '" << unixSocketName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ strcpy(xServerAddrUNIX -> sun_path, unixSocketName);
+
+ addr = (sockaddr *) xServerAddrUNIX;
+ addr_length = sizeof(sockaddr_un);
+
+ #ifdef __linux__
+
+ }
+ #endif
+ }
+ else
+ {
+ //
+ // TCP port.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Using real X server on TCP port.\n"
+ << logofs_flush;
+ #endif
+
+ addr_family = AF_INET;
+
+ int xServerIPAddr = GetHostAddress(display);
+
+ if (xServerIPAddr == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Unknown display host '" << display
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown display host '" << display
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ sockaddr_in *xServerAddrTCP = new sockaddr_in;
+
+ xServerAddrTCP -> sin_family = AF_INET;
+ xServerAddrTCP -> sin_port = htons(X_TCP_PORT + xPort);
+ xServerAddrTCP -> sin_addr.s_addr = xServerIPAddr;
+
+ addr = (sockaddr *) xServerAddrTCP;
+ addr_length = sizeof(sockaddr_in);
+ }
+
+ delete [] display;
+
+ return 1;
+}
+
+int SetupServiceSockets()
+{
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (useCupsSocket)
+ {
+ if ((cupsFD = ListenConnection(cupsPort, "CUPS")) < 0)
+ {
+ useCupsSocket = 0;
+ }
+ }
+
+ if (useAuxSocket)
+ {
+ if ((auxFD = ListenConnection(auxPort, "auxiliary X11")) < 0)
+ {
+ useAuxSocket = 0;
+ }
+ }
+
+ if (useSmbSocket)
+ {
+ if ((smbFD = ListenConnection(smbPort, "SMB")) < 0)
+ {
+ useSmbSocket = 0;
+ }
+ }
+
+ if (useMediaSocket)
+ {
+ if ((mediaFD = ListenConnection(mediaPort, "media")) < 0)
+ {
+ useMediaSocket = 0;
+ }
+ }
+
+ if (useHttpSocket)
+ {
+ if ((httpFD = ListenConnection(httpPort, "http")) < 0)
+ {
+ useHttpSocket = 0;
+ }
+ }
+
+ useFontSocket = 0;
+ }
+ else
+ {
+ //
+ // Get ready to listen for the font server connections
+ //
+
+ if (useFontSocket)
+ {
+ // Since ProtoStep7 (#issue 108)
+ int port = atoi(fontPort);
+
+ if ((fontFD = ListenConnectionTCP("localhost", port, "font")) < 0)
+ {
+ useFontSocket = 0;
+ }
+ }
+
+ useCupsSocket = 0;
+ useAuxSocket = 0;
+ useSmbSocket = 0;
+ useMediaSocket = 0;
+ useHttpSocket = 0;
+ }
+
+ //
+ // Slave channels can be originated
+ // by both sides.
+ //
+
+ if (useSlaveSocket)
+ {
+ // Since ProtoStep7 (#issue 108)
+ if ((slaveFD = ListenConnection(slavePort, "slave")) < 0)
+ {
+ useSlaveSocket = 0;
+ }
+ }
+
+ return 1;
+}
+
+int ListenConnectionAny(sockaddr *addr, socklen_t addrlen, const char *label)
+{
+ int newFD = socket(addr->sa_family, SOCK_STREAM, PF_UNSPEC);
+
+ if (newFD == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to socket failed for " << label
+ << " socket. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed for " << label
+ << " socket. Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ goto SetupSocketError;
+ }
+
+ if (addr->sa_family == AF_INET)
+ if (SetReuseAddress(newFD) < 0)
+ {
+ // SetReuseAddress already warns with an error
+ goto SetupSocketError;
+ }
+
+ if (bind(newFD, addr, addrlen) == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to bind failed for " << label
+ << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to bind failed for " << label
+ << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+ goto SetupSocketError;
+ }
+
+ if (listen(newFD, 8) == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to listen failed for " << label
+ << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to listen failed for " << label
+ << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ goto SetupSocketError;
+ }
+
+ return newFD;
+
+SetupSocketError:
+
+ if (newFD != -1)
+ {
+ close(newFD);
+ }
+
+ //
+ // May optionally return. The session would
+ // continue without the service. The problem
+ // with this approach is that it would make
+ // harder to track problems with allocation
+ // of ports in clients and server.
+ //
+
+ HandleCleanup();
+ return -1;
+}
+
+int ListenConnectionUnix(const char *path, const char *label)
+{
+
+ sockaddr_un unixAddr;
+ unixAddr.sun_family = AF_UNIX;
+#ifdef UNIX_PATH_MAX
+ if (strlen(path) >= UNIX_PATH_MAX)
+#else
+ if (strlen(path) >= sizeof(unixAddr.sun_path))
+#endif
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Socket path \"" << path << "\" for "
+ << label << " is too long.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Socket path \"" << path << "\" for "
+ << label << " is too long.\n";
+
+ HandleCleanup();
+ return -1;
+ }
+
+ strcpy(unixAddr.sun_path, path);
+ return ListenConnectionAny((sockaddr *)&unixAddr, sizeof(unixAddr), label);
+}
+
+int ListenConnectionTCP(const char *host, long port, const char *label)
+{
+ sockaddr_in tcpAddr;
+ tcpAddr.sin_family = AF_INET;
+ tcpAddr.sin_port = htons(port);
+
+ if (loopbackBind ||
+ !host ||
+ !strcmp(host, "") ||
+ !strcmp(host, "localhost")) {
+ tcpAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ }
+ else if(strcmp(host, "*") == 0) {
+ tcpAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+ else {
+ int ip = tcpAddr.sin_addr.s_addr = GetHostAddress(host);
+ if (ip == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Unknown " << label << " host '" << host
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown " << label << " host '" << host
+ << "'.\n";
+
+ HandleCleanup();
+ return -1;
+ }
+ }
+
+ return ListenConnectionAny((sockaddr *)&tcpAddr, sizeof(tcpAddr), label);
+}
+
+int ListenConnection(ChannelEndPoint &endpoint, const char *label)
+{
+ char *unixPath, *host;
+ long port;
+ if (endpoint.getUnixPath(&unixPath)) {
+ return ListenConnectionUnix(unixPath, label);
+ }
+ else if (endpoint.getTCPHostAndPort(&host, &port)) {
+ return ListenConnectionTCP(host, port, label);
+ }
+ return -1;
+}
+
+static int AcceptConnection(int fd, int domain, const char *label)
+{
+ struct sockaddr newAddr;
+
+ socklen_t addrLen = sizeof(newAddr);
+
+ #ifdef TEST
+
+ if (domain == AF_UNIX)
+ {
+ *logofs << "Loop: Going to accept new Unix " << label
+ << " connection on FD#" << fd << ".\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Going to accept new TCP " << label
+ << " connection on FD#" << fd << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ int newFD = accept(fd, &newAddr, &addrLen);
+
+ if (newFD < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to accept failed for "
+ << label << " connection. Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to accept failed for "
+ << label << " connection. Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+ }
+
+ return newFD;
+}
+
+void HandleShutdown()
+{
+ if (proxy -> getShutdown() == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! No shutdown of proxy link "
+ << "performed by remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Close the socket before showing the alert.
+ // It seems that the closure of the socket can
+ // sometimes take several seconds, even after
+ // the connection is broken. The result is that
+ // the dialog can be shown long after the user
+ // has gone after the failed session. Note that
+ // disabling the linger timeout does not seem
+ // to make any difference.
+ //
+
+ CleanupSockets();
+
+ cerr << "Error" << ": Connection with remote peer broken.\n";
+
+ #ifdef TEST
+ *logofs << "Loop: Bytes received so far are "
+ << (unsigned long long) statistics -> getBytesIn()
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Please check the state of your "
+ << "network and retry.\n";
+
+ handleTerminatingInLoop();
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Showing the proxy abort dialog.\n"
+ << logofs_flush;
+ #endif
+
+ HandleAlert(ABORT_PROXY_CONNECTION_ALERT, 1);
+
+ handleAlertInLoop();
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Finalized the remote proxy shutdown.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ HandleCleanup();
+}
+
+
+void WaitCleanup()
+{
+ T_timestamp selectTs;
+
+ while (NXTransRunning(NX_FD_ANY))
+ {
+ setTimestamp(selectTs, control -> PingTimeout);
+
+ NXTransContinue(&selectTs);
+ }
+}
+
+int KillProcess(int pid, const char *label, int signal, int wait)
+{
+ if (pid > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Killing the " << label << " process '"
+ << pid << "' from process with pid '" << getpid()
+ << "' with signal '" << DumpSignal(signal)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ signal = (signal == 0 ? SIGTERM : signal);
+
+ if (kill(pid, signal) < 0 && EGET() != ESRCH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Couldn't kill the " << label
+ << " process with pid '" << pid << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Couldn't kill the " << label
+ << " process with pid '" << pid << "'.\n";
+ }
+
+ if (wait == 1)
+ {
+ WaitChild(pid, label, 1);
+ }
+
+ return 1;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: No " << label << " process "
+ << "to kill with pid '" << pid
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+}
+
+int CheckProcess(int pid, const char *label)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Checking the " << label << " process '"
+ << pid << "' from process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (kill(pid, SIGCONT) < 0 && EGET() == ESRCH)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! The " << label << " process "
+ << "with pid '" << pid << "' has exited.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": The " << label << " process "
+ << "with pid '" << pid << "' has exited.\n";
+
+ return 0;
+ }
+
+ return 1;
+}
+
+int StartKeeper()
+{
+ #if defined(TEST) || defined(INFO)
+
+ if (IsRunning(lastKeeper) == 1 ||
+ IsRestarting(lastKeeper) == 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! The house-keeping process is "
+ << "alreay running with pid '" << lastKeeper
+ << "'.\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Don't care harvesting the persistent caches if
+ // the memory cache is not enabled. If the memory
+ // cache is not enabled neither we produced per-
+ // sistent caches. The user can still delete any
+ // persistent cache produced by the previous runs
+ // by using the client GUI.
+ //
+ // TODO: At the moment the user doesn't have a way
+ // to specify the amount of disk space to use for
+ // the persistent caches, but only the amount of
+ // space to use for images.
+ //
+
+ if (control -> LocalTotalStorageSize > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Starting the house-keeping process with "
+ << "storage size " << control -> PersistentCacheDiskLimit
+ << ".\n" << logofs_flush;
+ #endif
+
+ lastKeeper = NXTransKeeper(control -> PersistentCacheDiskLimit,
+ 0, control -> RootPath);
+
+ if (IsFailed(lastKeeper))
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Failed to start the NX keeper process.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to start the NX keeper process.\n";
+
+ SetNotRunning(lastKeeper);
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Keeper started with pid '"
+ << lastKeeper << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Nothing to do for the keeper process "
+ << "with persistent cache not enabled.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+void HandleCleanupForReconnect()
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to clean up system resources for Reconnect "
+ << "in process '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+ handleTerminatedInLoop();
+ DisableSignals();
+ if (control)
+ CleanupChildren();
+ CleanupListeners();
+ CleanupSockets();
+ CleanupKeeper();
+ CleanupStreams();
+ CleanupLocal();
+ CleanupGlobal();
+ RestoreSignals();
+ ServerCache::lastInitReply.set(0,NULL);
+ ServerCache::lastKeymap.set(0,NULL);
+ ServerCache::getKeyboardMappingLastMap.set(0,NULL);
+}
+void HandleCleanup(int code)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to clean up system resources "
+ << "in process '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ handleTerminatedInLoop();
+
+ //
+ // Suspend any signal while cleaning up.
+ //
+
+ DisableSignals();
+
+ if (getpid() == lastProxy)
+ {
+ //
+ // Terminate all the children.
+ //
+
+ CleanupChildren();
+
+ //
+ // Close all listeners.
+ //
+
+ CleanupListeners();
+
+ //
+ // Close all sockets.
+ //
+
+ CleanupSockets();
+
+ //
+ // Release the global objects.
+ //
+
+ CleanupGlobal();
+
+ //
+ // Restore the original signal handlers.
+ //
+
+ RestoreSignals();
+ }
+
+ //
+ // This is our last chance to print a message. If this
+ // is the process which created the transport we will
+ // jump back into the loop, letting the caller find out
+ // that the connection is broken, otherwise we assume
+ // that this is a child of the proxy and so we will
+ // safely exit.
+ //
+
+ #ifdef TEST
+
+ if (getpid() == lastProxy)
+ {
+ *logofs << "Loop: Reverting to loop context in process with "
+ << "pid '" << getpid() << "' at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Exiting from child process with pid '"
+ << getpid() << "' at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ if (getpid() == lastProxy)
+ {
+ //
+ // Reset all values to their default.
+ //
+
+ CleanupLocal();
+
+ CleanupStreams();
+
+ longjmp(context, 1);
+ }
+ else
+ {
+ //
+ // Give a last chance to the process
+ // to cleanup the ancillary classes.
+ //
+
+ CleanupKeeper();
+
+ CleanupStreams();
+
+ exit(code);
+ }
+}
+
+void CleanupKeeper()
+{
+ if (keeper != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up keeper in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete keeper;
+
+ keeper = NULL;
+ }
+}
+
+void CleanupStreams()
+{
+ //
+ // TODO: The cleanup procedure skips deletion of
+ // the I/O streams under Windows. This is intended
+ // to avoid a strange segfault occurring randomly,
+ // at the time the proxy is being shut down.
+ //
+
+ #ifndef __CYGWIN32__
+
+ #ifdef TEST
+ *logofs << "Loop: Freeing up streams in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (logofs != NULL && logofs != &cerr &&
+ *errorsFileName != '\0')
+ {
+ *logofs << flush;
+
+ delete logofs;
+
+ //
+ // Let the log go again to the standard
+ // error.
+ //
+
+ logofs = &cerr;
+ }
+
+ if (statofs != NULL && statofs != &cerr &&
+ *statsFileName != '\0')
+ {
+ *statofs << flush;
+
+ delete statofs;
+
+ statofs = NULL;
+ }
+
+ if (errofs != NULL)
+ {
+ *errofs << flush;
+
+ if (errofs == &cerr)
+ {
+ errofs = NULL;
+ }
+ else if (errsbuf != NULL)
+ {
+ cerr.rdbuf(errsbuf);
+
+ errsbuf = NULL;
+
+ delete errofs;
+ }
+
+ errofs = NULL;
+ }
+
+ #endif /* #ifndef __CYGWIN32__ */
+
+ //
+ // Reset these as they can't be reset
+ // in CleanupLocal().
+ //
+
+ *sessionFileName = '\0';
+ *errorsFileName = '\0';
+ *optionsFileName = '\0';
+ *statsFileName = '\0';
+}
+
+void CleanupChildren()
+{
+ //
+ // Remove any watchdog.
+ //
+
+ if (IsRunning(lastWatchdog))
+ {
+ KillProcess(lastWatchdog, "watchdog", SIGTERM, 1);
+
+ SetNotRunning(lastWatchdog);
+
+ lastSignal = 0;
+ }
+
+ //
+ // Kill the cache house-keeping process.
+ //
+
+ if (IsRunning(lastKeeper))
+ {
+ KillProcess(lastKeeper, "house-keeping", SIGTERM, 1);
+
+ SetNotRunning(lastKeeper);
+ }
+
+ //
+ // Let any running dialog to continue until it is
+ // closed by the user. In general this is the exp-
+ // ected behaviour, as for example when we are
+ // exiting because the link was abrouptedly shut
+ // down.
+ //
+
+ if (IsRunning(lastDialog))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: WARNING! Leaving the dialog process '"
+ << lastDialog << "' running in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ SetNotRunning(lastDialog);
+ }
+
+ //
+ // Give user a chance to start a new session.
+ //
+
+ if (control -> EnableRestartOnShutdown == 1)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Respawning the NX client "
+ << "on display '" << displayHost << "'.\n"
+ << logofs_flush;
+ #endif
+
+ NXTransClient(displayHost);
+ }
+
+ for (int i = 0; i < control -> KillDaemonOnShutdownNumber; i++)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Killing the NX daemon with "
+ << "pid '" << control -> KillDaemonOnShutdown[i]
+ << "'.\n" << logofs_flush;
+ #endif
+
+ KillProcess(control -> KillDaemonOnShutdown[i], "daemon", SIGTERM, 0);
+ }
+}
+
+void CleanupGlobal()
+{
+ if (proxy != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up proxy in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete proxy;
+
+ proxy = NULL;
+ }
+
+ if (agent != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up agent in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete agent;
+
+ agent = NULL;
+ }
+
+ if (auth != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up auth data in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete auth;
+
+ auth = NULL;
+ }
+
+ if (statistics != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up statistics in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete statistics;
+
+ statistics = NULL;
+ }
+
+ if (control != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Freeing up control in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete control;
+
+ control = NULL;
+ }
+}
+
+void CleanupConnections()
+{
+ if (proxy -> getChannels(channel_x11) != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing any remaining X connections.\n"
+ << logofs_flush;
+ #endif
+
+ proxy -> handleCloseAllXConnections();
+
+ #ifdef TEST
+ *logofs << "Loop: Closing any remaining listener.\n"
+ << logofs_flush;
+ #endif
+
+ proxy -> handleCloseAllListeners();
+ }
+
+ proxy -> handleFinish();
+}
+
+void CleanupListeners()
+{
+ if (useTcpSocket == 1)
+ {
+ if (tcpFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing TCP listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(tcpFD);
+
+ tcpFD = -1;
+ }
+
+ useTcpSocket = 0;
+ }
+
+ if (useUnixSocket == 1)
+ {
+ if (unixFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing UNIX listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(unixFD);
+
+ unixFD = -1;
+ }
+
+ if (*unixSocketName != '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to remove the Unix domain socket '"
+ << unixSocketName << "' in process " << "with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ unlink(unixSocketName);
+ }
+
+ useUnixSocket = 0;
+ }
+
+ if (useAgentSocket == 1)
+ {
+ //
+ // There is no listener for the
+ // agent descriptor.
+ //
+
+ useAgentSocket = 0;
+ }
+
+ if (useCupsSocket == 1)
+ {
+ if (cupsFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing CUPS listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(cupsFD);
+
+ cupsFD = -1;
+ }
+
+ useCupsSocket = 0;
+ }
+
+ if (useAuxSocket == 1)
+ {
+ if (auxFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing auxiliary X11 listener "
+ << "in process " << "with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ close(auxFD);
+
+ auxFD = -1;
+ }
+
+ useAuxSocket = 0;
+ }
+
+ if (useSmbSocket == 1)
+ {
+ if (smbFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing SMB listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(smbFD);
+
+ smbFD = -1;
+ }
+
+ useSmbSocket = 0;
+ }
+
+ if (useMediaSocket == 1)
+ {
+ if (mediaFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing multimedia listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(mediaFD);
+
+ mediaFD = -1;
+ }
+
+ useMediaSocket = 0;
+ }
+
+ if (useHttpSocket == 1)
+ {
+ if (httpFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing http listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(httpFD);
+
+ httpFD = -1;
+ }
+
+ useHttpSocket = 0;
+ }
+
+ if (useFontSocket == 1)
+ {
+ if (fontFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing font server listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(fontFD);
+
+ fontFD = -1;
+ }
+
+ useFontSocket = 0;
+ }
+
+ if (useSlaveSocket == 1)
+ {
+ if (slaveFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing slave listener in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(slaveFD);
+
+ slaveFD = -1;
+ }
+
+ useSlaveSocket = 0;
+ }
+}
+
+void CleanupSockets()
+{
+ if (proxyFD != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing proxy FD in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(proxyFD);
+
+ proxyFD = -1;
+ }
+
+ if (agentFD[1] != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Closing agent FD in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ close(agentFD[1]);
+
+ agentFD[0] = -1;
+ agentFD[1] = -1;
+ }
+}
+
+void CleanupLocal()
+{
+ *homeDir = '\0';
+ *rootDir = '\0';
+ *tempDir = '\0';
+ *systemDir = '\0';
+ *sessionDir = '\0';
+
+ *linkSpeedName = '\0';
+ *cacheSizeName = '\0';
+ *shsegSizeName = '\0';
+ *imagesSizeName = '\0';
+ *bitrateLimitName = '\0';
+ *packMethodName = '\0';
+ *productName = '\0';
+
+ packMethod = -1;
+ packQuality = -1;
+
+ *sessionType = '\0';
+ *sessionId = '\0';
+
+ parsedOptions = 0;
+ parsedCommand = 0;
+
+ *remoteData = '\0';
+ remotePosition = 0;
+
+ tcpFD = -1;
+ unixFD = -1;
+ cupsFD = -1;
+ auxFD = -1;
+ smbFD = -1;
+ mediaFD = -1;
+ httpFD = -1;
+ fontFD = -1;
+ slaveFD = -1;
+ proxyFD = -1;
+
+ agentFD[0] = -1;
+ agentFD[1] = -1;
+
+ useUnixSocket = 1;
+ useTcpSocket = 1;
+ useCupsSocket = 0;
+ useAuxSocket = 0;
+ useSmbSocket = 0;
+ useMediaSocket = 0;
+ useHttpSocket = 0;
+ useFontSocket = 0;
+ useSlaveSocket = 0;
+ useAgentSocket = 0;
+
+ useNoDelay = -1;
+ usePolicy = -1;
+ useRender = -1;
+ useTaint = -1;
+
+ *unixSocketName = '\0';
+
+ *acceptHost = '\0';
+ *displayHost = '\0';
+ *authCookie = '\0';
+
+ proxyPort = DEFAULT_NX_PROXY_PORT;
+ xPort = DEFAULT_NX_X_PORT;
+
+ xServerAddrFamily = -1;
+ xServerAddrLength = 0;
+
+ delete xServerAddr;
+
+ xServerAddr = NULL;
+
+ listenSocket.disable();
+ connectSocket.disable();
+
+ cupsPort.disable();
+ auxPort.disable();
+ smbPort.disable();
+ mediaPort.disable();
+ httpPort.disable();
+ slavePort.disable();
+
+ *fontPort = '\0';
+
+ *bindHost = '\0';
+ bindPort = -1;
+
+ initTs = nullTimestamp();
+ startTs = nullTimestamp();
+ logsTs = nullTimestamp();
+ nowTs = nullTimestamp();
+
+ lastProxy = 0;
+ lastDialog = 0;
+ lastWatchdog = 0;
+ lastKeeper = 0;
+ lastStatus = 0;
+ lastKill = 0;
+ lastDestroy = 0;
+
+ lastReadableTs = nullTimestamp();
+
+ lastAlert.code = 0;
+ lastAlert.local = 0;
+
+ lastMasks.blocked = 0;
+ lastMasks.installed = 0;
+
+ memset(&lastMasks.saved, 0, sizeof(sigset_t));
+
+ for (int i = 0; i < 32; i++)
+ {
+ lastMasks.enabled[i] = 0;
+ lastMasks.forward[i] = 0;
+
+ memset(&lastMasks.action[i], 0, sizeof(struct sigaction));
+ }
+
+ lastSignal = 0;
+
+ memset(&lastTimer.action, 0, sizeof(struct sigaction));
+ memset(&lastTimer.value, 0, sizeof(struct itimerval));
+
+ lastTimer.start = nullTimestamp();
+ lastTimer.next = nullTimestamp();
+}
+
+int CheckAbort()
+{
+ if (lastSignal != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Aborting the procedure due to signal '"
+ << lastSignal << "', '" << DumpSignal(lastSignal)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Aborting the procedure due to signal '"
+ << lastSignal << "'.\n";
+
+ lastSignal = 0;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void HandleAbort()
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ *logofs << flush;
+
+ handleTerminatingInLoop();
+
+ if (lastSignal == SIGHUP)
+ {
+ lastSignal = 0;
+ }
+
+ //
+ // The current default is to just quit the program.
+ // Code has not been updated to deal with the new
+ // NX transport loop.
+ //
+
+ if (control -> EnableCoreDumpOnAbort == 1)
+ {
+ if (agent != NULL)
+ {
+ cerr << "Session" << ": Terminating session at '"
+ << strTimestamp() << "'.\n";
+ }
+
+ cerr << "Error" << ": Generating a core file to help "
+ << "the investigations.\n";
+
+ cerr << "Session" << ": Session terminated at '"
+ << strTimestamp() << "'.\n";
+
+ cerr << flush;
+
+ signal(SIGABRT, SIG_DFL);
+
+ raise(SIGABRT);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Showing the proxy abort dialog.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ //
+ // Close the socket before showing the alert.
+ // It seems that the closure of the socket can
+ // sometimes take several seconds, even after
+ // the connection is broken.
+ //
+
+ CleanupSockets();
+
+ if (lastKill == 0)
+ {
+ HandleAlert(ABORT_PROXY_CONNECTION_ALERT, 1);
+ }
+ else
+ {
+ HandleAlert(ABORT_PROXY_SHUTDOWN_ALERT, 1);
+ }
+
+ handleAlertInLoop();
+ }
+
+ HandleCleanup();
+}
+
+void HandleAlert(int code, int local)
+{
+ if (lastAlert.code == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Requesting an alert dialog with code "
+ << code << " and local " << local << ".\n"
+ << logofs_flush;
+ #endif
+
+ lastAlert.code = code;
+ lastAlert.local = local;
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "Loop: WARNING! Alert dialog already requested "
+ << "with code " << lastAlert.code << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return;
+}
+
+void FlushCallback(int length)
+{
+ if (flushCallback != NULL)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Reporting a flush request at "
+ << strMsTimestamp() << " with " << length
+ << " bytes written.\n" << logofs_flush;
+ #endif
+
+ (*flushCallback)(flushParameter, length);
+ }
+ #if defined(TEST) || defined(INFO)
+ else if (control -> ProxyMode == proxy_client)
+ {
+ *logofs << "Loop: WARNING! Can't find a flush "
+ << "callback in process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ }
+ #endif
+}
+
+void KeeperCallback()
+{
+ if (IsRunning(lastKeeper) == 0)
+ {
+ //
+ // Let the house-keeping process take care
+ // of the persistent image cache.
+ //
+
+ if (control -> ImageCacheEnableLoad == 1 ||
+ control -> ImageCacheEnableSave == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Starting the house-keeping process with "
+ << "image storage size " << control -> ImageCacheDiskLimit
+ << ".\n" << logofs_flush;
+ #endif
+
+ lastKeeper = NXTransKeeper(0, control -> ImageCacheDiskLimit,
+ control -> RootPath);
+
+ if (IsFailed(lastKeeper))
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Can't start the NX keeper process.\n"
+ << logofs_flush;
+ #endif
+
+ SetNotRunning(lastKeeper);
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Keeper started with pid '"
+ << lastKeeper << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Nothing to do for the keeper process "
+ << "with image cache not enabled.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Nothing to do with the keeper process "
+ << "already running.\n" << logofs_flush;
+ }
+ #endif
+}
+
+void InstallSignals()
+{
+ #ifdef TEST
+ *logofs << "Loop: Installing signals in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (CheckSignal(i) == 1 &&
+ lastMasks.enabled[i] == 0)
+ {
+ InstallSignal(i, NX_SIGNAL_ENABLE);
+ }
+ }
+
+ lastMasks.installed = 1;
+}
+
+void RestoreSignals()
+{
+ #ifdef TEST
+ *logofs << "Loop: Restoring signals in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ if (lastMasks.installed == 1)
+ {
+ //
+ // Need to keep monitoring the children.
+ //
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (lastMasks.enabled[i] == 1)
+ {
+ RestoreSignal(i);
+ }
+ }
+ }
+
+ lastMasks.installed = 0;
+
+ if (lastMasks.blocked == 1)
+ {
+ EnableSignals();
+ }
+}
+
+void DisableSignals()
+{
+ if (lastMasks.blocked == 0)
+ {
+ sigset_t newMask;
+
+ sigemptyset(&newMask);
+
+ //
+ // Block also the other signals that may be
+ // installed by the agent, that are those
+ // signals for which the function returns 2.
+ //
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (CheckSignal(i) > 0)
+ {
+ #ifdef DUMP
+ *logofs << "Loop: Disabling signal " << i << " '"
+ << DumpSignal(i) << "' in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ sigaddset(&newMask, i);
+ }
+ }
+
+ sigprocmask(SIG_BLOCK, &newMask, &lastMasks.saved);
+
+ lastMasks.blocked++;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: WARNING! Signals were already blocked in "
+ << "process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+}
+
+void EnableSignals()
+{
+ if (lastMasks.blocked == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Enabling signals in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ sigprocmask(SIG_SETMASK, &lastMasks.saved, NULL);
+
+ lastMasks.blocked = 0;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Signals were not blocked in "
+ << "process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Signals were not blocked in "
+ << "process with pid '" << getpid() << "'.\n";
+ }
+}
+
+void InstallSignal(int signal, int action)
+{
+ if (lastMasks.enabled[signal] == 1)
+ {
+ if (action == NX_SIGNAL_FORWARD)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Forwarding handler for signal " << signal
+ << " '" << DumpSignal(signal) << "' in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ lastMasks.forward[signal] = 1;
+
+ return;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Reinstalling handler for signal " << signal
+ << " '" << DumpSignal(signal) << "' in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Installing handler for signal " << signal
+ << " '" << DumpSignal(signal) << "' in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ if (signal == SIGALRM && isTimestamp(lastTimer.start))
+ {
+ ResetTimer();
+ }
+
+ struct sigaction newAction;
+
+ memset(&newAction, 0, sizeof(newAction));
+
+ newAction.sa_handler = HandleSignal;
+
+ sigemptyset(&(newAction.sa_mask));
+
+ if (signal == SIGCHLD)
+ {
+ newAction.sa_flags = SA_NOCLDSTOP;
+ }
+ else
+ {
+ newAction.sa_flags = 0;
+ }
+
+ sigaction(signal, &newAction, &lastMasks.action[signal]);
+
+ lastMasks.enabled[signal] = 1;
+
+ if (action == NX_SIGNAL_FORWARD)
+ {
+ lastMasks.forward[signal] = 1;
+ }
+}
+
+void RestoreSignal(int signal)
+{
+ if (lastMasks.enabled[signal] == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Signal '" << DumpSignal(signal)
+ << " not installed in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Signal '" << DumpSignal(signal)
+ << " not installed in process with pid '"
+ << getpid() << "'.\n";
+
+ return;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Restoring handler for signal " << signal
+ << " '" << DumpSignal(signal) << "' in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (signal == SIGALRM && isTimestamp(lastTimer.start))
+ {
+ ResetTimer();
+ }
+
+ sigaction(signal, &lastMasks.action[signal], NULL);
+
+ lastMasks.enabled[signal] = 0;
+ lastMasks.forward[signal] = 0;
+}
+
+void HandleSignal(int signal)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ #if defined(UNSAFE) && (defined(TEST) || defined(INFO))
+
+ if (lastSignal != 0)
+ {
+ *logofs << "Loop: WARNING! Last signal is '" << lastSignal
+ << "', '" << DumpSignal(signal) << "' and not zero "
+ << "in process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ }
+
+ *logofs << "Loop: Signal '" << signal << "', '"
+ << DumpSignal(signal) << "' received in process "
+ << "with pid '" << getpid() << "'.\n" << logofs_flush;
+
+ #endif
+
+ if (getpid() != lastProxy && signalHandler != NULL)
+ {
+ #if defined(UNSAFE) && (defined(TEST) || defined(INFO))
+ *logofs << "Loop: Calling slave handler in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if ((*signalHandler)(signal) == 0)
+ {
+ return;
+ }
+ }
+
+ switch (signal)
+ {
+ case SIGUSR1:
+ {
+ if (proxy != NULL && lastSignal == 0)
+ {
+ lastSignal = SIGUSR1;
+ }
+
+ break;
+ }
+ case SIGUSR2:
+ {
+ if (proxy != NULL && lastSignal == 0)
+ {
+ lastSignal = SIGUSR2;
+ }
+
+ break;
+ }
+ case SIGPIPE:
+ {
+ //
+ // It can happen that SIGPIPE is delivered
+ // to the proxy even in the case some other
+ // descriptor is unexpectedly closed.
+ //
+ // if (agentFD[1] != -1)
+ // {
+ // cerr << "Info" << ": Received signal 'SIGPIPE'. "
+ // << "Closing agent conection.\n";
+ //
+ // shutdown(agentFD[1], SHUT_RDWR);
+ // }
+ //
+
+ break;
+ }
+ case SIGALRM:
+ {
+ //
+ // Nothing to do. Just wake up the
+ // process on blocking operations.
+ //
+
+ break;
+ }
+ case SIGCHLD:
+ {
+ //
+ // Check if any of our children has exited.
+ //
+
+ if (HandleChildren() != 0)
+ {
+ signal = 0;
+ }
+
+ //
+ // Don't save this signal or it will override
+ // any previous signal sent by child before
+ // exiting.
+ //
+
+ break;
+ }
+
+ #ifdef __CYGWIN32__
+
+ case 12:
+ {
+ //
+ // Nothing to do. This signal is what is delivered
+ // by the Cygwin library when trying use a shared
+ // memory function if the daemon is not running.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Received signal '12' in "
+ << "process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+
+ *logofs << "Loop: WARNING! Please check that the "
+ << "cygserver daemon is running.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+
+ #endif
+
+ default:
+ {
+ //
+ // Register the signal so we can handle it
+ // inside the main loop. We will probably
+ // dispose any resource and exit.
+ //
+
+ if (getpid() == lastProxy)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Registering end of session request "
+ << "due to signal '" << signal << "', '"
+ << DumpSignal(signal) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ lastSignal = signal;
+ }
+ else
+ {
+ //
+ // This is a child, so exit immediately.
+ //
+
+ HandleCleanup();
+ }
+ }
+ }
+
+ if (signal != 0 && lastMasks.forward[signal] == 1)
+ {
+ if (lastMasks.action[signal].sa_handler != NULL &&
+ lastMasks.action[signal].sa_handler != HandleSignal)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Forwarding signal '" << signal << "', '"
+ << DumpSignal(signal) << "' to previous handler.\n"
+ << logofs_flush;
+ #endif
+
+ lastMasks.action[signal].sa_handler(signal);
+ }
+ #ifdef WARNING
+ else if (lastMasks.action[signal].sa_handler == NULL)
+ {
+ *logofs << "Loop: WARNING! Parent requested to forward "
+ << "signal '" << signal << "', '" << DumpSignal(signal)
+ << "' but didn't set a handler.\n" << logofs_flush;
+ }
+ #endif
+ }
+}
+
+int HandleChildren()
+{
+ //
+ // Try to waitpid() for each child because the
+ // call might have return ECHILD and so we may
+ // have lost any of the processes.
+ //
+
+ if (IsRunning(lastDialog) && HandleChild(lastDialog) == 1)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Resetting pid of last dialog process "
+ << "in handler.\n" << logofs_flush;
+ #endif
+
+ SetNotRunning(lastDialog);
+
+ if (proxy != NULL)
+ {
+ proxy -> handleResetAlert();
+ }
+
+ return 1;
+ }
+
+ if (IsRunning(lastWatchdog) && HandleChild(lastWatchdog) == 1)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Watchdog is gone. Setting the last "
+ << "signal to SIGHUP.\n" << logofs_flush;
+ #endif
+
+ lastSignal = SIGHUP;
+
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Resetting pid of last watchdog process "
+ << "in handler.\n" << logofs_flush;
+ #endif
+
+ SetNotRunning(lastWatchdog);
+
+ return 1;
+ }
+
+ //
+ // The house-keeping process exits after a
+ // number of iterations to keep the memory
+ // pollution low. It is restarted on demand
+ // by the lower layers, using the callback
+ // function.
+ //
+
+ if (IsRunning(lastKeeper) && HandleChild(lastKeeper) == 1)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Resetting pid of last house-keeping "
+ << "process in handler.\n" << logofs_flush;
+ #endif
+
+ SetNotRunning(lastKeeper);
+
+ return 1;
+ }
+
+ //
+ // The pid will be checked by the code
+ // that registered the child.
+ //
+
+ if (IsRunning(lastChild))
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Resetting pid of last child process "
+ << "in handler.\n" << logofs_flush;
+ #endif
+
+ SetNotRunning(lastChild);
+
+ return 1;
+ }
+
+ proxy->checkSlaves();
+
+ //
+ // This can actually happen either because we
+ // reset the pid of the child process as soon
+ // as we kill it, or because of a child process
+ // of our parent.
+ //
+
+ #if defined(UNSAFE) && (defined(TEST) || defined(INFO))
+ *logofs << "Loop: Ignoring signal received for the "
+ << "unregistered child.\n" << logofs_flush;
+ #endif
+
+ return 0;
+}
+
+int HandleChild(int child)
+{
+ int pid;
+
+ int status = 0;
+ int options = WNOHANG | WUNTRACED;
+
+ while ((pid = waitpid(child, &status, options)) &&
+ pid == -1 && EGET() == EINTR);
+
+ return CheckChild(pid, status);
+}
+
+int WaitChild(int child, const char* label, int force)
+{
+ int pid;
+
+ int status = 0;
+ int options = WUNTRACED;
+
+ for (;;)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Waiting for the " << label
+ << " process '" << child << "' to die.\n"
+ << logofs_flush;
+ #endif
+
+ pid = waitpid(child, &status, options);
+
+ if (pid == -1 && EGET() == EINTR)
+ {
+ if (force == 0)
+ {
+ return 0;
+ }
+
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring signal while "
+ << "waiting for the " << label << " process '"
+ << child << "' to die.\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+
+ break;
+ }
+
+ return (EGET() == ECHILD ? 1 : CheckChild(pid, status));
+}
+
+int CheckChild(int pid, int status)
+{
+ lastStatus = 0;
+
+ if (pid > 0)
+ {
+ if (WIFSTOPPED(status))
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Child process '" << pid << "' was stopped "
+ << "with signal " << (WSTOPSIG(status)) << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ if (WIFEXITED(status))
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Child process '" << pid << "' exited "
+ << "with status '" << (WEXITSTATUS(status))
+ << "'.\n" << logofs_flush;
+ #endif
+
+ lastStatus = WEXITSTATUS(status);
+ }
+ else if (WIFSIGNALED(status))
+ {
+ if (CheckSignal(WTERMSIG(status)) != 1)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Child process '" << pid
+ << "' died because of signal " << (WTERMSIG(status))
+ << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Child process '" << pid
+ << "' died because of signal " << (WTERMSIG(status))
+ << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n";
+ }
+ #if defined(UNSAFE) && defined(TEST)
+ else
+ {
+ *logofs << "Loop: Child process '" << pid
+ << "' died because of signal " << (WTERMSIG(status))
+ << ", '" << DumpSignal(WTERMSIG(status)) << "'.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ lastStatus = 1;
+ }
+
+ return 1;
+ }
+ }
+ else if (pid < 0)
+ {
+ if (EGET() != ECHILD)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to waitpid failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to waitpid failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // This can happen when the waitpid() is
+ // blocking, as the SIGCHLD is received
+ // within the call.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: No more children processes running.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void RegisterChild(int child)
+{
+ #if defined(TEST) || defined(INFO)
+
+ if (IsNotRunning(lastChild))
+ {
+ *logofs << "Loop: Registering child process '" << child
+ << "' in process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: WARNING! Overriding registered child '"
+ << lastChild << "' with new child '" << child
+ << "' in process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ }
+
+ #endif
+
+ lastChild = child;
+}
+
+int CheckParent(const char *name, const char *type, int parent)
+{
+ if (parent != getppid() || parent == 1)
+ {
+ #ifdef WARNING
+ *logofs << name << ": WARNING! Parent process appears "
+ << "to be dead. Exiting " << type << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Parent process appears "
+ << "to be dead. Exiting " << type << ".\n";
+
+ return 0;
+ }
+
+ return 1;
+}
+
+void HandleTimer(int signal)
+{
+ if (signal == SIGALRM)
+ {
+ if (isTimestamp(lastTimer.start))
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Timer expired at " << strMsTimestamp()
+ << " in process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy != NULL)
+ {
+ proxy -> handleTimer();
+ }
+
+ ResetTimer();
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Inconsistent timer state "
+ << " in process with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Inconsistent timer state "
+ << " in process with pid '" << getpid() << "'.\n";
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Inconsistent signal '"
+ << signal << "', '" << DumpSignal(signal)
+ << "' received in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Inconsistent signal '"
+ << signal << "', '" << DumpSignal(signal)
+ << "' received in process with pid '"
+ << getpid() << "'.\n";
+ }
+}
+
+void SetTimer(int value)
+{
+ getNewTimestamp();
+
+ if (isTimestamp(lastTimer.start))
+ {
+ int diffTs = diffTimestamp(lastTimer.start, getTimestamp());
+
+ if (diffTs > lastTimer.next.tv_usec / 1000 * 2)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Timer missed to expire at "
+ << strMsTimestamp() << " in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Timer missed to expire at "
+ << strMsTimestamp() << " in process with pid '"
+ << getpid() << "'.\n";
+
+ HandleTimer(SIGALRM);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: Timer already running at "
+ << strMsTimestamp() << " in process with pid '"
+ << getpid() << "'.\n" << logofs_flush;
+ #endif
+
+ return;
+ }
+ }
+
+ //
+ // Save the former handler.
+ //
+
+ struct sigaction action;
+
+ memset(&action, 0, sizeof(action));
+
+ action.sa_handler = HandleTimer;
+
+ sigemptyset(&action.sa_mask);
+
+ action.sa_flags = 0;
+
+ sigaction(SIGALRM, &action, &lastTimer.action);
+
+ //
+ // Start the timer.
+ //
+
+ lastTimer.next = getTimestamp(value);
+
+ struct itimerval timer;
+
+ timer.it_interval = lastTimer.next;
+ timer.it_value = lastTimer.next;
+
+ #ifdef TEST
+ *logofs << "Loop: Timer set to " << lastTimer.next.tv_sec
+ << " S and " << lastTimer.next.tv_usec / 1000
+ << " Ms at " << strMsTimestamp() << " in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (setitimer(ITIMER_REAL, &timer, &lastTimer.value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to setitimer failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to setitimer failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ lastTimer.next = nullTimestamp();
+
+ return;
+ }
+
+ lastTimer.start = getTimestamp();
+}
+
+void ResetTimer()
+{
+ if (isTimestamp(lastTimer.start) == 0)
+ {
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Timer not running in process "
+ << "with pid '" << getpid() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return;
+ }
+
+ #if defined(UNSAFE) && defined(TEST)
+ *logofs << "Loop: Timer reset at " << strMsTimestamp()
+ << " in process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Restore the old signal mask and timer.
+ //
+
+ if (setitimer(ITIMER_REAL, &lastTimer.value, NULL) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to setitimer failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to setitimer failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+
+ if (sigaction(SIGALRM, &lastTimer.action, NULL) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to sigaction failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to sigaction failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+ }
+
+ lastTimer.start = lastTimer.next = nullTimestamp();
+}
+
+//
+// Open TCP or UNIX file socket to listen for remote proxy
+// and block until remote connects. If successful close
+// the listening socket and return FD on which the other
+// party is connected.
+//
+
+int WaitForRemote(ChannelEndPoint &socketAddress)
+{
+ char hostLabel[DEFAULT_STRING_LENGTH] = { 0 };
+ char *socketUri = NULL;
+
+ int retryAccept = -1;
+
+ int pFD = -1;
+ int newFD = -1;
+
+ int acceptIPAddr = 0;
+
+ if (socketAddress.isTCPSocket())
+ {
+
+ //
+ // Get IP address of host to be awaited.
+ //
+
+ if (*acceptHost != '\0')
+ {
+ acceptIPAddr = GetHostAddress(acceptHost);
+
+ if (acceptIPAddr == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Cannot accept connections from unknown host '"
+ << acceptHost << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot accept connections from unknown host '"
+ << acceptHost << "'.\n";
+
+ goto WaitForRemoteError;
+ }
+ snprintf(hostLabel, sizeof(hostLabel), "'%s'", acceptHost);
+ }
+ else
+ {
+ strcpy(hostLabel, "any host");
+ }
+
+ long _bindPort;
+ if (socketAddress.getTCPHostAndPort(NULL, &_bindPort))
+ {
+ socketAddress.setSpec(loopbackBind ? "localhost" : "*", _bindPort);
+ }
+ else
+ {
+ // This should never happen
+ cerr << "Error" << ": Unable to change bind host\n";
+ }
+ }
+ else if (socketAddress.isUnixSocket())
+ strcpy(hostLabel, "this host");
+ else
+ strcpy(hostLabel, "unknown origin (something went wrong!!!)");
+
+
+ pFD = ListenConnection(socketAddress, "NX");
+
+ socketAddress.getSpec(&socketUri);
+ #ifdef TEST
+ *logofs << "Loop: Waiting for connection from "
+ << hostLabel << " on socket '" << socketUri
+ << "'.\n" << logofs_flush;
+ #endif
+ cerr << "Info" << ": Waiting for connection from "
+ << hostLabel << " on socket '" << socketUri
+ << "'.\n";
+ free(socketUri);
+
+ //
+ // How many times to loop waiting for connections
+ // from the selected host? Each loop wait for at
+ // most 20 seconds so a default value of 3 gives
+ // a timeout of 1 minute.
+ //
+ // TODO: Handling of timeouts and retry attempts
+ // must be rewritten.
+ //
+
+ retryAccept = control -> OptionProxyRetryAccept;
+
+ for (;;)
+ {
+ fd_set readSet;
+
+ FD_ZERO(&readSet);
+ FD_SET(pFD, &readSet);
+
+ T_timestamp selectTs;
+
+ selectTs.tv_sec = 20;
+ selectTs.tv_usec = 0;
+
+ int result = select(pFD + 1, &readSet, NULL, NULL, &selectTs);
+
+ getNewTimestamp();
+
+ if (result == -1)
+ {
+ if (EGET() == EINTR)
+ {
+ if (CheckAbort() != 0)
+ {
+ goto WaitForRemoteError;
+ }
+
+ continue;
+ }
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to select failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to select failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ goto WaitForRemoteError;
+ }
+ else if (result > 0 && FD_ISSET(pFD, &readSet))
+ {
+
+ sockaddr_in newAddrINET;
+
+ if (socketAddress.isUnixSocket())
+ {
+ socklen_t addrLen = sizeof(sockaddr_un);
+ newFD = accept(pFD, NULL, &addrLen);
+ }
+ else if (socketAddress.isTCPSocket())
+ {
+ socklen_t addrLen = sizeof(sockaddr_in);
+ newFD = accept(pFD, (sockaddr *) &newAddrINET, &addrLen);
+ }
+ if (newFD == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to accept failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to accept failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ goto WaitForRemoteError;
+ }
+
+ if (socketAddress.isUnixSocket())
+ {
+
+ char * unixPath = NULL;
+ socketAddress.getUnixPath(&unixPath);
+ #ifdef TEST
+ *logofs << "Loop: Accepted connection from this host on Unix file socket '"
+ << unixPath << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Accepted connection from this host on Unix file socket '"
+ << unixPath << "'.\n";
+ free(unixPath);
+
+ break;
+ }
+ else if (socketAddress.isTCPSocket())
+ {
+
+ char *connectedHost = inet_ntoa(newAddrINET.sin_addr);
+
+ if (*acceptHost == '\0' || (int) newAddrINET.sin_addr.s_addr == acceptIPAddr)
+ {
+
+ #ifdef TEST
+
+ unsigned int connectedPort = ntohs(newAddrINET.sin_port);
+
+ *logofs << "Loop: Accepted connection from '" << connectedHost
+ << "' with port '" << connectedPort << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Accepted connection from '"
+ << connectedHost << "'.\n";
+
+ break;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: WARNING! Refusing connection from '" << connectedHost
+ << "' on port '" << socketAddress.getTCPPort() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Refusing connection from '"
+ << connectedHost << "'.\n";
+ }
+
+ //
+ // Not the best way to elude a DOS attack...
+ //
+
+ sleep(5);
+
+ close(newFD);
+
+ }
+
+ }
+
+ if (--retryAccept == 0)
+ {
+ if (socketAddress.isUnixSocket())
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Connection via Unix file socket from this host "
+ << "could not be established.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Connection via Unix file socket from this host "
+ << "could not be established.\n";
+ }
+ else if (*acceptHost == '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Connection with remote host "
+ << "could not be established.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Connection with remote host "
+ << "could not be established.\n";
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Connection with remote host '"
+ << acceptHost << "' could not be established.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Connection with remote host '"
+ << acceptHost << "' could not be established.\n";
+ }
+
+ goto WaitForRemoteError;
+ }
+ else
+ {
+ handleCheckSessionInConnect();
+ }
+ }
+
+ close(pFD);
+
+ return newFD;
+
+WaitForRemoteError:
+
+ close(pFD);
+
+ HandleCleanup();
+}
+
+int PrepareProxyConnectionTCP(char** hostName, long int* portNum, int* timeout, int* proxyFileDescriptor, int* reason)
+{
+
+ if (!proxyFileDescriptor)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionTCP). "
+ << "'proxyFileDescriptor' must not be a NULL pointer.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Implementation error (PrepareProxyConnectionTCP). "
+ << "'proxyFileDescriptor' must not be a NULL pointer.\n";
+
+ return -1;
+ }
+
+ if (!reason)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionTCP). "
+ << "'reason' must not be a NULL pointer.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Implementation error (PrepareProxyConnectionTCP). "
+ << "'reason' must not be a NULL pointer.\n";
+
+ return -1;
+ }
+
+ int remoteIPAddr = GetHostAddress(*hostName);
+ if (remoteIPAddr == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Unknown remote host '"
+ << *hostName << "'.\n" << logofs_flush;
+ #endif
+ cerr << "Error" << ": Unknown remote host '"
+ << *hostName << "'.\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Connecting to remote host '"
+ << *hostName << ":" << *portNum << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Connecting to remote host '"
+ << *hostName << ":" << *portNum << "'.\n"
+ << logofs_flush;
+
+ *proxyFileDescriptor = -1;
+ *reason = -1;
+
+ sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(*portNum);
+ addr.sin_addr.s_addr = remoteIPAddr;
+
+ *proxyFileDescriptor = socket(AF_INET, SOCK_STREAM, PF_UNSPEC);
+ *reason = EGET();
+
+ if (*proxyFileDescriptor == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to socket failed. "
+ << "Error is " << *reason << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed. "
+ << "Error is " << *reason << " '" << ESTR()
+ << "'.\n";
+ return -1;
+
+ }
+ else if (SetReuseAddress(*proxyFileDescriptor) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Ensure operation is timed out
+ // if there is a network problem.
+ //
+
+ if (timeout)
+ SetTimer(*timeout);
+ else
+ SetTimer(20000);
+
+ int result = connect(*proxyFileDescriptor, (sockaddr *) &addr, sizeof(sockaddr_in));
+
+ *reason = EGET();
+
+ ResetTimer();
+
+ return result;
+
+}
+
+int PrepareProxyConnectionUnix(char** path, int* timeout, int* proxyFileDescriptor, int* reason)
+{
+
+ if (!proxyFileDescriptor)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionUnix). "
+ << "proxyFileDescriptor must not be a NULL pointer.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Implementation error (PrepareProxyConnectionUnix). "
+ << "proxyFileDescriptor must not be a NULL pointer.\n";
+
+ return -1;
+ }
+
+ if (!reason)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Implementation error (PrepareProxyConnectionUnix). "
+ << "'reason' must not be a NULL pointer.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Implementation error (PrepareProxyConnectionUnix). "
+ << "'reason' must not be a NULL pointer.\n";
+
+ return -1;
+ }
+
+ /* FIXME: Add socket file existence and permission checks */
+
+ *proxyFileDescriptor = -1;
+ *reason = -1;
+
+ sockaddr_un addr;
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, *path, 108 - 1);
+
+ *proxyFileDescriptor = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
+ *reason = EGET();
+
+ if (*proxyFileDescriptor == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Call to socket failed. "
+ << "Error is " << *reason << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed. "
+ << "Error is " << *reason << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+
+ //
+ // Ensure operation is timed out
+ // if there is a network problem.
+ //
+
+ if (timeout)
+ SetTimer(*timeout);
+ else
+ SetTimer(20000);
+
+ int result = connect(*proxyFileDescriptor, (sockaddr *) &addr, sizeof(sockaddr_un));
+
+ *reason = EGET();
+
+ ResetTimer();
+
+ return result;
+}
+
+//
+// Connect to remote proxy. If successful
+// return FD of connection, else return -1.
+//
+
+int ConnectToRemote(ChannelEndPoint &socketAddress)
+{
+
+ //
+ // How many times we retry to connect to remote
+ // host / Unix domain socket in case of failure?
+ //
+
+ int retryConnect = control -> OptionProxyRetryConnect;
+
+ //
+ // Show an alert after 20 seconds and use the
+ // same timeout to interrupt the connect. The
+ // retry timeout is incremental, starting from
+ // 100 miliseconds up to 1 second.
+ //
+
+ int alertTimeout = 20000;
+ int connectTimeout = 20000;
+ int retryTimeout = 100;
+
+ T_timestamp lastRetry = getNewTimestamp();
+
+ int result = -1;
+ int reason = -1;
+ int pFD = -1;
+
+ char *hostName = NULL;
+ long int portNum = -1;
+ char *unixPath = NULL;
+
+ for (;;)
+ {
+
+ #ifdef DEBUG
+ *logofs << "Loop: Timer set to " << connectTimeout / 1000
+ << " S " << "with retry set to " << retryConnect
+ << " in process with pid '" << getpid()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (socketAddress.getUnixPath(&unixPath))
+ result = PrepareProxyConnectionUnix(&unixPath, &connectTimeout, &pFD, &reason);
+ else if (socketAddress.getTCPHostAndPort(&hostName, &portNum))
+ result = PrepareProxyConnectionTCP(&hostName, &portNum, &connectTimeout, &pFD, &reason);
+
+ if (result < 0)
+ {
+ close(pFD);
+
+ if (CheckAbort() != 0)
+ {
+ goto ConnectToRemoteError;
+ }
+ else if (--retryConnect == 0)
+ {
+ ESET(reason);
+
+ if (socketAddress.isUnixSocket())
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Connection to Unix file socket '"
+ << unixPath << "' failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Connection to Unix file socket '"
+ << unixPath << "' failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+ }
+ else
+ {
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Connection to '" << hostName
+ << ":" << portNum << "' failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Connection to '" << hostName
+ << ":" << portNum << "' failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+ }
+ goto ConnectToRemoteError;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: Sleeping " << retryTimeout
+ << " ms before retrying.\n"
+ << logofs_flush;
+ #endif
+
+ usleep(retryTimeout * 1000);
+
+ retryTimeout <<= 1;
+
+ if (retryTimeout > 1000 * 1000)
+ {
+ retryTimeout = 1000 * 1000;
+ }
+ }
+
+ //
+ // Check if it is time to show an alert dialog.
+ //
+
+ if (diffTimestamp(lastRetry, getNewTimestamp()) >=
+ (alertTimeout - control -> LatencyTimeout))
+ {
+ if (IsNotRunning(lastDialog))
+ {
+ handleCheckSessionInConnect();
+
+ //
+ // Wait for the dialog process to die
+ // unless a signal is received.
+ //
+
+ while (IsRunning(lastDialog))
+ {
+ WaitChild(lastDialog, "dialog", 0);
+
+ if (CheckAbort() != 0)
+ {
+ //
+ // The client ignores the TERM signal
+ // on Windows.
+ //
+
+ #ifdef __CYGWIN32__
+
+ KillProcess(lastDialog, "dialog", SIGKILL, 1);
+
+ #else
+
+ KillProcess(lastDialog, "dialog", SIGTERM, 1);
+
+ #endif
+
+ goto ConnectToRemoteError;
+ }
+ }
+
+ lastRetry = getTimestamp();
+ }
+ }
+ #ifdef TEST
+ {
+ *logofs << "Loop: Not showing the dialog with "
+ << (diffTimestamp(lastRetry, getTimestamp()) / 1000)
+ << " seconds elapsed.\n" << logofs_flush;
+ }
+ #endif
+
+ ESET(reason);
+
+ #ifdef TEST
+ if (unixPath && unixPath[0] != '\0' )
+ {
+ *logofs << "Loop: Connection to Unix socket file '"
+ << unixPath << "' failed with error '"
+ << ESTR() << "'. Retrying.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Connection to '" << hostName
+ << ":" << portNum << "' failed with error '"
+ << ESTR() << "'. Retrying.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+ else
+ {
+ //
+ // Connection was successful.
+ //
+
+ break;
+ }
+ }
+
+ return pFD;
+
+ConnectToRemoteError:
+
+ if (pFD != -1)
+ {
+ close(pFD);
+ }
+
+ HandleCleanup();
+}
+
+//
+// Make a string of options for the remote
+// proxy and write it to the descriptor.
+// The string includes the local version.
+//
+
+int SendProxyOptions(int fd)
+{
+ char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
+
+ //
+ // Send the "compatibility" version first, then our
+ // actual version. Old proxies will take the first
+ // value and ignore the second.
+ //
+
+ sprintf(options, "NXPROXY-%s-%i.%i.%i",
+ control -> NXPROXY_COMPATIBILITY_VERSION,
+ control -> LocalVersionMajor,
+ control -> LocalVersionMinor,
+ control -> LocalVersionPatch);
+
+ //
+ // If you want to send options from proxy
+ // initiating the connection use something
+ // like this:
+ //
+ // if (WE_PROVIDE_CREDENTIALS)
+ // {
+ // sprintf(options + strlen(options), "%s=%s", option, value);
+ // }
+ //
+ // If you want to send options according to
+ // local proxy mode use something like this:
+ //
+ // if (control -> ProxyMode == proxy_client)
+ // {
+ // sprintf(options + strlen(options), "%s=%s", option, value);
+ // }
+ //
+
+ //
+ // Send the authorization cookie if any. We assume
+ // user can choose to not provide any auth cookie
+ // and allow any connection to be accepted.
+ //
+
+ if (WE_PROVIDE_CREDENTIALS && *authCookie != '\0')
+ {
+ sprintf(options + strlen(options), " cookie=%s,", authCookie);
+ }
+ else
+ {
+ sprintf(options + strlen(options), " ");
+ }
+
+ //
+ // Now link characteristics and compression
+ // options. Delta compression, as well as
+ // preferred pack method, are imposed by
+ // client proxy.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ sprintf(options + strlen(options), "link=%s,pack=%s,cache=%s,",
+ linkSpeedName, packMethodName, cacheSizeName);
+
+ if (*bitrateLimitName != '\0')
+ {
+ sprintf(options + strlen(options), "limit=%s,",
+ bitrateLimitName);
+ }
+
+ //
+ // Let the user disable the render extension
+ // and let the X client proxy know if it can
+ // short-circuit the X replies. Also pass
+ // along the session type to ensure that the
+ // remote proxy gets the right value.
+ //
+
+ sprintf(options + strlen(options), "render=%d,taint=%d,",
+ (control -> HideRender == 0),
+ control -> TaintReplies);
+
+ if (*sessionType != '\0')
+ {
+ sprintf(options + strlen(options), "type=%s,", sessionType);
+ }
+ else
+ {
+ sprintf(options + strlen(options), "type=default,");
+ }
+
+ //
+ // Add the 'strict' option, if needed.
+ //
+
+ // Since ProtoStep7 (#issue 108)
+ if (useStrict != -1)
+ {
+ sprintf(options + strlen(options), "strict=%d,", useStrict);
+ }
+
+ //
+ // Tell the remote the size of the shared
+ // memory segment.
+ //
+
+ // Since ProtoStep7 (#issue 108)
+ if (*shsegSizeName != '\0')
+ {
+ sprintf(options + strlen(options), "shseg=%s,", shsegSizeName);
+ }
+
+ //
+ // Send image cache parameters.
+ //
+
+ sprintf(options + strlen(options), "images=%s,", imagesSizeName);
+
+ sprintf(options + strlen(options), "delta=%d,stream=%d,data=%d ",
+ control -> LocalDeltaCompression,
+ control -> LocalStreamCompressionLevel,
+ control -> LocalDataCompressionLevel);
+ }
+ else
+ {
+ //
+ // If no special compression level was selected,
+ // server side will use compression levels set
+ // by client.
+ //
+
+ if (control -> LocalStreamCompressionLevel < 0)
+ {
+ sprintf(options + strlen(options), "stream=default,");
+ }
+ else
+ {
+ sprintf(options + strlen(options), "stream=%d,",
+ control -> LocalStreamCompressionLevel);
+ }
+
+ if (control -> LocalDataCompressionLevel < 0)
+ {
+ sprintf(options + strlen(options), "data=default ");
+ }
+ else
+ {
+ sprintf(options + strlen(options), "data=%d ",
+ control -> LocalDataCompressionLevel);
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Sending remote options '"
+ << options << "'.\n" << logofs_flush;
+ #endif
+
+ return WriteLocalData(fd, options, strlen(options));
+}
+
+int ReadProxyVersion(int fd)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to read the remote proxy version "
+ << "from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Read until the first space in string.
+ // We expect the remote version number.
+ //
+
+ char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
+
+ int result = ReadRemoteData(fd, options, sizeof(options), ' ');
+
+ if (result <= 0)
+ {
+ if (result < 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ HandleAlert(ABORT_PROXY_NEGOTIATION_ALERT, 1);
+ }
+
+ handleAlertInLoop();
+ }
+
+ return result;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Received remote version string '"
+ << options << "' from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (strncmp(options, "NXPROXY-", strlen("NXPROXY-")) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Parse error in remote options string '"
+ << options << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Parse error in remote options string '"
+ << options << "'.\n";
+
+ return -1;
+ }
+
+ //
+ // Try to determine if this is a pre-2.0.0
+ // version advertising itself as compatible
+ // with the 1.2.2.
+ //
+
+ int major = -1;
+ int minor = -1;
+ int patch = -1;
+
+ sscanf(options, "NXPROXY-%i.%i.%i-%i.%i.%i", &(control -> RemoteVersionMajor),
+ &(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch),
+ &major, &minor, &patch);
+
+ if (control -> RemoteVersionMajor == 1 &&
+ control -> RemoteVersionMinor == 2 &&
+ control -> RemoteVersionPatch == 2 &&
+ major != -1 && minor != -1 && patch != -1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Read trailing remote version '" << major
+ << "." << minor << "." << patch << "'.\n"
+ << logofs_flush;
+ #endif
+
+ control -> CompatVersionMajor = major;
+ control -> CompatVersionMinor = minor;
+ control -> CompatVersionPatch = patch;
+
+ control -> RemoteVersionMajor = major;
+ control -> RemoteVersionMinor = minor;
+ control -> RemoteVersionPatch = patch;
+ }
+ else
+ {
+ //
+ // We read the remote version at the first
+ // round. If the second version is missing,
+ // we will retain the values read before.
+ //
+
+ sscanf(options, "NXPROXY-%i.%i.%i-%i.%i.%i", &(control -> CompatVersionMajor),
+ &(control -> CompatVersionMinor), &(control -> CompatVersionPatch),
+ &(control -> RemoteVersionMajor), &(control -> RemoteVersionMinor),
+ &(control -> RemoteVersionPatch));
+ }
+
+ *logofs << "Loop: Identified remote version '" << control -> RemoteVersionMajor
+ << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch
+ << "'.\n" << logofs_flush;
+
+ *logofs << "Loop: Remote compatibility version '" << control -> CompatVersionMajor
+ << "." << control -> CompatVersionMinor << "." << control -> CompatVersionPatch
+ << "'.\n" << logofs_flush;
+
+ *logofs << "Loop: Local version '" << control -> LocalVersionMajor
+ << "." << control -> LocalVersionMinor << "." << control -> LocalVersionPatch
+ << "'.\n" << logofs_flush;
+
+ if (SetVersion() < 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ HandleAlert(WRONG_PROXY_VERSION_ALERT, 1);
+ }
+
+ handleAlertInLoop();
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ReadProxyOptions(int fd)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to read the remote proxy options "
+ << "from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
+
+ int result = ReadRemoteData(fd, options, sizeof(options), ' ');
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Received remote options string '"
+ << options << "' from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Get the remote options, delimited by a space character.
+ // Note that there will be a further initialization phase
+ // at the time proxies negotiate cache file to restore.
+ //
+
+ if (ParseRemoteOptions(options) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Couldn't negotiate a valid "
+ << "session with remote NX proxy.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Couldn't negotiate a valid "
+ << "session with remote NX proxy.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int SendProxyCaches(int fd)
+{
+ #ifdef TEST
+ *logofs << "Loop: Synchronizing local and remote caches.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ //
+ // Prepare a list of caches matching this
+ // session type and send it to the remote.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Going to send the list of local caches.\n"
+ << logofs_flush;
+ #endif
+
+ SetCaches();
+
+ int entries = DEFAULT_REMOTE_CACHE_ENTRIES;
+
+ const char prefix = 'C';
+
+ if (control -> LocalDeltaCompression == 0 ||
+ control -> PersistentCacheEnableLoad == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Writing an empty list to FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ return WriteLocalData(fd, "cachelist=none ", strlen("cachelist=none "));
+ }
+
+ int count = 0;
+
+ #ifdef TEST
+ *logofs << "Loop: Looking for cache files in directory '"
+ << control -> PersistentCachePath << "'.\n" << logofs_flush;
+ #endif
+
+ DIR *cacheDir = opendir(control -> PersistentCachePath);
+
+ if (cacheDir != NULL)
+ {
+ dirent *dirEntry;
+
+ int prologue = 0;
+
+ while (((dirEntry = readdir(cacheDir)) != NULL) && (count < entries))
+ {
+ if (*dirEntry -> d_name == prefix &&
+ strlen(dirEntry -> d_name) == (MD5_LENGTH * 2 + 2))
+ {
+ if (prologue == 0)
+ {
+ WriteLocalData(fd, "cachelist=", strlen("cachelist="));
+
+ prologue = 1;
+ }
+ else
+ {
+ WriteLocalData(fd, ",", strlen(","));
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Writing entry '" << control -> PersistentCachePath
+ << "/" << dirEntry -> d_name << "' to FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Write cache file name to the socket,
+ // including leading 'C-' or 'S-'.
+ //
+
+ WriteLocalData(fd, dirEntry -> d_name, MD5_LENGTH * 2 + 2);
+
+ count++;
+ }
+ }
+
+ closedir(cacheDir);
+ }
+
+ if (count == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Writing an empty list to FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ return WriteLocalData(fd, "cachelist=none ", strlen("cachelist=none "));
+ }
+ else
+ {
+ return WriteLocalData(fd, " ", 1);
+ }
+ }
+ else
+ {
+ //
+ // Send back the selected cache name.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Going to send the selected cache.\n"
+ << logofs_flush;
+ #endif
+
+ char buffer[DEFAULT_STRING_LENGTH];
+
+ if (control -> PersistentCacheName != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Name of selected cache file is '"
+ << control -> PersistentCacheName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ sprintf(buffer, "cachefile=%s%s ",
+ *(control -> PersistentCacheName) == 'C' ? "S-" : "C-",
+ control -> PersistentCacheName + 2);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: No valid cache file was selected.\n"
+ << logofs_flush;
+ #endif
+
+ sprintf(buffer, "cachefile=none ");
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Sending string '" << buffer
+ << "' as selected cache file.\n"
+ << logofs_flush;
+ #endif
+
+ return WriteLocalData(fd, buffer, strlen(buffer));
+ }
+}
+
+int ReadProxyCaches(int fd)
+{
+ if (control -> ProxyMode == proxy_client)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to receive the selected proxy cache.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // We will read the name of cache plus the stop character.
+ //
+
+ char buffer[DEFAULT_STRING_LENGTH];
+
+ //
+ // Leave space for a trailing null.
+ //
+
+ int result = ReadRemoteData(fd, buffer, sizeof("cachefile=") + MD5_LENGTH * 2 + 3, ' ');
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ char *cacheName = strstr(buffer, "cachefile=");
+
+ if (cacheName == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid cache file option '"
+ << buffer << "' provided by remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid cache file option '"
+ << buffer << "' provided by remote proxy.\n";
+
+ HandleCleanup();
+ }
+
+ cacheName += strlen("cachefile=");
+
+ if (control -> PersistentCacheName != NULL)
+ {
+ delete [] control -> PersistentCacheName;
+ }
+
+ control -> PersistentCacheName = NULL;
+
+ if (strncasecmp(cacheName, "none", strlen("none")) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: No cache file selected by remote proxy.\n"
+ << logofs_flush;
+ #endif
+ }
+ else if (strlen(cacheName) != MD5_LENGTH * 2 + 3 ||
+ *(cacheName + MD5_LENGTH * 2 + 2) != ' ')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid cache file name '"
+ << cacheName << "' provided by remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid cache file name '"
+ << cacheName << "' provided by remote proxy.\n";
+
+ HandleCleanup();
+ }
+ else
+ {
+ //
+ // It is "C-" + 32 + "\0".
+ //
+
+ control -> PersistentCacheName = new char[MD5_LENGTH * 2 + 3];
+
+ *(cacheName + MD5_LENGTH * 2 + 2) = '\0';
+
+ strcpy(control -> PersistentCacheName, cacheName);
+
+ #ifdef TEST
+ *logofs << "Loop: Cache file '" << control -> PersistentCacheName
+ << "' selected by remote proxy.\n" << logofs_flush;
+ #endif
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to receive the list of remote caches.\n"
+ << logofs_flush;
+ #endif
+
+ SetCaches();
+
+ int size = ((MD5_LENGTH * 2 + 2) + strlen(",")) * DEFAULT_REMOTE_CACHE_ENTRIES +
+ strlen("cachelist=") + strlen(" ") + 1;
+
+ char *buffer = new char[size];
+
+ int result = ReadRemoteData(fd, buffer, size - 1, ' ');
+
+ if (result <= 0)
+ {
+ delete [] buffer;
+
+ return result;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Read list of caches from remote side as '"
+ << buffer << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Prepare the buffer. What we want is a list
+ // like "cache1,cache2,cache2" terminated by
+ // null.
+ //
+
+ *(buffer + strlen(buffer) - 1) = '\0';
+
+ if (strncasecmp(buffer, "cachelist=", strlen("cachelist=")) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: Wrong format for list of cache files "
+ << "read from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Wrong format for list of cache files.\n";
+
+ delete [] buffer;
+
+ return -1;
+ }
+
+ control -> PersistentCacheName = GetLastCache(buffer, control -> PersistentCachePath);
+
+ //
+ // Get rid of list of caches.
+ //
+
+ delete [] buffer;
+ }
+
+ return 1;
+}
+
+int ReadForwarderVersion(int fd)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to negotiate the forwarder version.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if we actually expect the session cookie.
+ //
+
+ if (*authCookie == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: No authentication cookie required "
+ << "from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
+
+ int result = ReadRemoteData(fd, options, sizeof(options), ' ');
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Received forwarder version string '" << options
+ << "' from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ if (strncmp(options, "NXSSH-", strlen("NXSSH-")) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Parse error in forwarder options string '"
+ << options << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Parse error in forwarder options string '"
+ << options << "'.\n";
+
+ return -1;
+ }
+
+ //
+ // Accept whatever forwarder version.
+ //
+
+ sscanf(options, "NXSSH-%i.%i.%i", &(control -> RemoteVersionMajor),
+ &(control -> RemoteVersionMinor), &(control -> RemoteVersionPatch));
+
+ #ifdef TEST
+ *logofs << "Loop: Read forwarder version '" << control -> RemoteVersionMajor
+ << "." << control -> RemoteVersionMinor << "." << control -> RemoteVersionPatch
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ReadForwarderOptions(int fd)
+{
+ //
+ // Get the forwarder cookie.
+ //
+
+ if (*authCookie == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: No authentication cookie required "
+ << "from FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ char options[DEFAULT_REMOTE_OPTIONS_LENGTH];
+
+ int result = ReadRemoteData(fd, options, sizeof(options), ' ');
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Received forwarder options string '"
+ << options << "' from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (ParseForwarderOptions(options) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Couldn't negotiate a valid "
+ << "cookie with the NX forwarder.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Couldn't negotiate a valid "
+ << "cookie with the NX forwarder.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ReadRemoteData(int fd, char *buffer, int size, char stop)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to read remote data from FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ if (size >= MAXIMUM_REMOTE_OPTIONS_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Maximum remote options buffer "
+ << "limit exceeded.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Maximum remote options buffer "
+ << "limit exceeded.\n";
+
+ HandleCleanup();
+ }
+
+ while (remotePosition < (size - 1))
+ {
+ int result = read(fd, remoteData + remotePosition, 1);
+
+ getNewTimestamp();
+
+ if (result <= 0)
+ {
+ if (result == -1)
+ {
+ if (EGET() == EAGAIN)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Reading data from FD#" << fd
+ << " would block.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ if (CheckAbort() != 0)
+ {
+ return -1;
+ }
+
+ continue;
+ }
+ }
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! The remote NX proxy closed "
+ << "the connection.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The remote NX proxy closed "
+ << "the connection.\n";
+
+ return -1;
+ }
+ else if (*(remoteData + remotePosition) == stop)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Read stop character from FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ remotePosition++;
+
+ //
+ // Copy the fake terminating null
+ // in the buffer.
+ //
+
+ *(remoteData + remotePosition) = '\0';
+
+ memcpy(buffer, remoteData, remotePosition + 1);
+
+ #ifdef TEST
+ *logofs << "Loop: Remote string '" << remoteData
+ << "' read from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ int t = remotePosition;
+
+ remotePosition = 0;
+
+ return t;
+ }
+ else
+ {
+ //
+ // Make sure string received
+ // from far end is printable.
+ //
+
+ if (isgraph(*(remoteData + remotePosition)) == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Non printable character decimal '"
+ << (unsigned int) *(remoteData + remotePosition)
+ << "' received in remote data from FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Non printable character decimal '"
+ << (unsigned int) *(remoteData + remotePosition)
+ << "' received in remote data from FD#"
+ << fd << ".\n" << logofs_flush;
+
+ *(remoteData + remotePosition) = ' ';
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Read a further character "
+ << "from FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ remotePosition++;
+ }
+ }
+
+ *(remoteData + remotePosition) = '\0';
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Stop character missing "
+ << "from FD#" << fd << " after " << remotePosition
+ << " characters read in string '" << remoteData
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Stop character missing "
+ << "from FD#" << fd << " after " << remotePosition
+ << " characters read in string '" << remoteData
+ << "'.\n";
+
+ memcpy(buffer, remoteData, remotePosition);
+
+ remotePosition = 0;
+
+ return -1;
+}
+
+static int
+hexval(char c) {
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+ if ((c >= 'a') && (c <= 'f'))
+ return c - 'a' + 10;
+ if ((c >= 'A') && (c <= 'F'))
+ return c - 'A' + 10;
+ return -1;
+}
+
+static void
+URLDecodeInPlace(char *str) {
+ if (str) {
+ char *to = str;
+ while (str[0]) {
+ if ((str[0] == '%') &&
+ (hexval(str[1]) >= 0) &&
+ (hexval(str[2]) >= 0)) {
+ *(to++) = hexval(str[1]) * 16 + hexval(str[2]);
+ str += 3;
+ }
+ else
+ *(to++) = *(str++);
+ }
+ *to = '\0';
+ }
+}
+
+int WriteLocalData(int fd, const char *buffer, int size)
+{
+ int position = 0;
+ int ret = 0;
+ fd_set writeSet;
+ struct timeval selectTs = {30, 0};
+
+ while (position < size)
+ {
+
+ // A write to a non-blocking socket may fail with EAGAIN. The problem is
+ // that cache data is done in several writes, and there's no easy way
+ // to handle failure without rewriting a significant amount of code.
+ //
+ // Bailing out of the outer loop would result in restarting the sending
+ // of the entire cache list, which would confuse the other side.
+
+ FD_ZERO(&writeSet);
+ FD_SET(fd, &writeSet);
+
+ ret = select(fd+1, NULL, &writeSet, NULL, &selectTs);
+
+ #ifdef DEBUG
+ *logofs << "Loop: WriteLocalData: select() returned with a code of " << ret << " and remaining timeout of "
+ << selectTs.tv_sec << " sec, " << selectTs.tv_usec << "usec\n" << logofs_flush;
+ #endif
+
+ if ( ret < 0 )
+ {
+ *logofs << "Loop: Error in select() when writing data to FD#" << fd << ": " << strerror(EGET()) << "\n" << logofs_flush;
+
+ if ( EGET() == EINTR )
+ continue;
+
+ return -1;
+ }
+ else if ( ret == 0 )
+ {
+ *logofs << "Loop: Timeout expired in select() when writing data to FD#" << fd << ": " << strerror(EGET()) << "\n" << logofs_flush;
+ return -1;
+ }
+
+ int result = write(fd, buffer + position, size - position);
+
+ getNewTimestamp();
+
+ if (result <= 0)
+ {
+ if (result < 0 && (EGET() == EINTR || EGET() == EAGAIN || EGET() == EWOULDBLOCK))
+ {
+ continue;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Error writing data to FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ position += result;
+ }
+
+ return position;
+}
+
+//
+// Parse the string passed by calling process in
+// the environment. This is not necessarily the
+// content of DISPLAY variable, but can be the
+// parameters passed when creating the process
+// or thread.
+//
+
+int ParseEnvironmentOptions(const char *env, int force)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ //
+ // Be sure we have a parameters repository
+ // and a context to jump into because this
+ // can be called before creating the proxy.
+ //
+
+ if (control == NULL)
+ {
+ control = new Control();
+ }
+
+ if (setjmp(context) == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Out of the long jump while parsing "
+ << "the environment options.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ if (force == 0 && parsedOptions == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Skipping a further parse of environment "
+ << "options string '" << (env != NULL ? env : "")
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ if (env == NULL || *env == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: Nothing to do with empty environment "
+ << "options string '" << (env != NULL ? env : "")
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Going to parse the environment options "
+ << "string '" << env << "'.\n"
+ << logofs_flush;
+ #endif
+
+ parsedOptions = 1;
+
+ //
+ // Copy the string passed as parameter
+ // because we need to modify it.
+ //
+
+ char opts[DEFAULT_DISPLAY_OPTIONS_LENGTH];
+
+ #ifdef VALGRIND
+
+ memset(opts, '\0', DEFAULT_DISPLAY_OPTIONS_LENGTH);
+
+ #endif
+
+ if (strlen(env) >= DEFAULT_DISPLAY_OPTIONS_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Environment options string '" << env
+ << "' exceeds length of " << DEFAULT_DISPLAY_OPTIONS_LENGTH
+ << " characters.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Environment options string '" << env
+ << "' exceeds length of " << DEFAULT_DISPLAY_OPTIONS_LENGTH
+ << " characters.\n";
+
+ return -1;
+ }
+
+ strcpy(opts, env);
+
+ char *nextOpts = opts;
+
+ //
+ // Ensure that DISPLAY environment variable
+ // (roughly) follows the X convention for
+ // transport notation.
+ //
+
+ if (strncasecmp(opts, "nx/nx,:", 7) == 0 ||
+ strncasecmp(opts, "nx,:", 4) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Parse error in options string '"
+ << opts << "' at 'nx,:'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Parse error in options string '"
+ << opts << "' at 'nx,:'.\n";
+
+ return -1;
+ }
+ else if (strncasecmp(opts, "nx/nx,", 6) == 0)
+ {
+ nextOpts += 6;
+ }
+ else if (strncasecmp(opts, "nx,", 3) == 0)
+ {
+ nextOpts += 3;
+ }
+ else if (strncasecmp(opts, "nx:", 3) == 0)
+ {
+ nextOpts += 3;
+ }
+ else if (force == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Ignoring host X server display string '"
+ << opts << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Save here the name of the options file and
+ // parse it after all the other options.
+ //
+
+ char fileOptions[DEFAULT_STRING_LENGTH] = { 0 };
+
+ //
+ // The options string is intended to be a series
+ // of name/value tuples in the form name=value
+ // separated by the ',' character ended by a ':'
+ // followed by remote NX proxy port.
+ //
+
+ char *name;
+ char *value;
+
+ value = strrchr(nextOpts, ':');
+
+ if (value != NULL)
+ {
+ char *check = value + 1;
+
+ if (*check == '\0' || isdigit(*check) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify NX port in string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify NX port in string '"
+ << value << "'.\n";
+
+ return -1;
+ }
+
+ proxyPort = atoi(check);
+
+ //
+ // Get rid of the port specification.
+ //
+
+ *value = '\0';
+ }
+ else if (proxyPort == DEFAULT_NX_PROXY_PORT && force == 0)
+ {
+ //
+ // Complain only if user didn't specify
+ // the port on the command line.
+ //
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify NX port in string '"
+ << opts << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify NX port in string '"
+ << opts << "'.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Parsing options string '"
+ << nextOpts << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Now all the other optional parameters.
+ //
+
+ name = strtok(nextOpts, "=");
+
+ char connectHost[DEFAULT_STRING_LENGTH] = { 0 };
+ long connectPort = -1;
+
+ while (name)
+ {
+ value = strtok(NULL, ",");
+ URLDecodeInPlace(value);
+
+ if (CheckArg("environment", name, value) < 0)
+ {
+ return -1;
+ }
+
+ if (strcasecmp(name, "options") == 0)
+ {
+ strncpy(fileOptions, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "display") == 0)
+ {
+ strncpy(displayHost, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "link") == 0)
+ {
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParseLinkOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify 'link' option in string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify 'link' option in string '"
+ << value << "'.\n";
+ if (ParseLinkOption("adsl") < 0)
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "limit") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParseBitrateOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify option 'limit' in string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify option 'limit' in string '"
+ << value << "'.\n";
+
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "type") == 0)
+ {
+ //
+ // Type of session, for example "desktop",
+ // "application", "windows", etc.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ if (strcasecmp(value, "default") == 0)
+ {
+ *sessionType = '\0';
+ }
+ else
+ {
+ strncpy(sessionType, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ }
+ }
+ else if (strcasecmp(name, "listen") == 0)
+ {
+ char *socketUri = NULL;
+ if (connectSocket.getSpec(&socketUri))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't handle 'listen' and 'connect' parameters "
+ << "at the same time.\n" << logofs_flush;
+
+ *logofs << "Loop: PANIC! Refusing 'listen' parameter with 'connect' being '"
+ << socketUri << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't handle 'listen' and 'connect' parameters "
+ << "at the same time.\n";
+
+ cerr << "Error" << ": Refusing 'listen' parameter with 'connect' being '"
+ << socketUri << "'.\n";
+
+ free(socketUri);
+ return -1;
+ }
+
+ SetAndValidateChannelEndPointArg("local", name, value, listenSocket);
+
+ }
+ else if (strcasecmp(name, "loopback") == 0)
+ {
+ loopbackBind = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "accept") == 0)
+ {
+ char *socketUri = NULL;
+ if (connectSocket.getSpec(&socketUri))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't handle 'accept' and 'connect' parameters "
+ << "at the same time.\n" << logofs_flush;
+
+ *logofs << "Loop: PANIC! Refusing 'accept' parameter with 'connect' being '"
+ << socketUri << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't handle 'accept' and 'connect' parameters "
+ << "at the same time.\n";
+
+ cerr << "Error" << ": Refusing 'accept' parameter with 'connect' being '"
+ << socketUri << "'.\n";
+
+ free(socketUri);
+ return -1;
+ }
+
+ strncpy(acceptHost, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "connect") == 0)
+ {
+ if (*acceptHost != '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't handle 'connect' and 'accept' parameters "
+ << "at the same time.\n" << logofs_flush;
+
+ *logofs << "Loop: PANIC! Refusing 'connect' parameter with 'accept' being '"
+ << acceptHost << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't handle 'connect' and 'accept' parameters "
+ << "at the same time.\n";
+
+ cerr << "Error" << ": Refusing 'connect' parameter with 'accept' being '"
+ << acceptHost << "'.\n";
+
+ return -1;
+ }
+ if ((strncmp(value, "tcp:", 4) == 0) || (strncmp(value, "unix:", 5) == 0))
+ SetAndValidateChannelEndPointArg("local", name, value, connectSocket);
+ else
+ // if the "connect" parameter does not start with "unix:" or "tcp:" assume
+ // old parameter usage style (providing hostname string only).
+ strcpy(connectHost, value);
+ }
+ else if (strcasecmp(name, "port") == 0)
+ {
+ connectPort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "retry") == 0)
+ {
+ control -> OptionProxyRetryConnect = ValidateArg("local", name, value);
+ control -> OptionServerRetryConnect = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "session") == 0)
+ {
+ strncpy(sessionFileName, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "errors") == 0)
+ {
+ //
+ // The old name of the parameter was 'log'
+ // but the default name for the file is
+ // 'errors' so it is more logical to use
+ // the same name.
+ //
+
+ strncpy(errorsFileName, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "root") == 0)
+ {
+ strncpy(rootDir, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "id") == 0)
+ {
+ strncpy(sessionId, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "stats") == 0)
+ {
+ control -> EnableStatistics = 1;
+
+ strncpy(statsFileName, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "cookie") == 0)
+ {
+ LowercaseArg("local", name, value);
+
+ strncpy(authCookie, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "nodelay") == 0)
+ {
+ useNoDelay = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "policy") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ usePolicy = ValidateArg("local", name, value);
+ }
+ }
+ else if (strcasecmp(name, "render") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ useRender = ValidateArg("local", name, value);
+ }
+ }
+ else if (strcasecmp(name, "taint") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ useTaint = ValidateArg("local", name, value);
+ }
+ }
+ else if (strcasecmp(name, "delta") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ control -> LocalDeltaCompression = ValidateArg("local", name, value);
+ }
+ }
+ else if (strcasecmp(name, "data") == 0)
+ {
+ control -> LocalDataCompressionLevel = ValidateArg("local", name, value);
+
+ if (control -> LocalDataCompressionLevel == 0)
+ {
+ control -> LocalDataCompression = 0;
+ }
+ else
+ {
+ control -> LocalDataCompression = 1;
+ }
+ }
+ else if (strcasecmp(name, "stream") == 0)
+ {
+ control -> LocalStreamCompressionLevel = ValidateArg("local", name, value);
+
+ if (control -> LocalStreamCompressionLevel == 0)
+ {
+ control -> LocalStreamCompression = 0;
+ }
+ else
+ {
+ control -> LocalStreamCompression = 1;
+ }
+ }
+ else if (strcasecmp(name, "memory") == 0)
+ {
+ control -> LocalMemoryLevel = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "cache") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParseCacheOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify cache size for string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify cache size for string '"
+ << value << "'.\n";
+
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "images") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParseImagesOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify images cache size for string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify images cache size for string '"
+ << value << "'.\n";
+
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "shseg") == 0)
+ {
+ //
+ // The 'shmem' option is used by the agent, together
+ // with 'shpix' literal. We make the 'shseg' option
+ // specific to the proxy and use it to determine the
+ // size of the shared memory segment, or otherwise 0,
+ // if the use of the shared memory extension should
+ // not be enabled on the real X server.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParseShmemOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify size of shared memory "
+ << "segment in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify size of shared memory "
+ << "segment in string '" << value << "'.\n";
+
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "load") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ control -> PersistentCacheEnableLoad = ValidateArg("local", name, value);
+
+ if (control -> PersistentCacheEnableLoad > 0)
+ {
+ control -> PersistentCacheEnableLoad = 1;
+ }
+ else
+ {
+ if (control -> PersistentCacheName != NULL)
+ {
+ delete [] control -> PersistentCacheName;
+ }
+
+ control -> PersistentCacheName = NULL;
+
+ control -> PersistentCacheEnableLoad = 0;
+ }
+ }
+ }
+ else if (strcasecmp(name, "save") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ control -> PersistentCacheEnableSave = ValidateArg("local", name, value);
+
+ if (control -> PersistentCacheEnableSave > 0)
+ {
+ control -> PersistentCacheEnableSave = 1;
+ }
+ else
+ {
+ if (control -> PersistentCacheName != NULL)
+ {
+ delete [] control -> PersistentCacheName;
+ }
+
+ control -> PersistentCacheName = NULL;
+
+ control -> PersistentCacheEnableSave = 0;
+ }
+ }
+ }
+ else if (strcasecmp(name, "cups") == 0)
+ {
+ SetAndValidateChannelEndPointArg("local", name, value, cupsPort);
+ }
+ else if (strcasecmp(name, "sync") == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! No 'sync' channel in current version. "
+ << "Assuming 'cups' channel.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": No 'sync' channel in current version. "
+ << "Assuming 'cups' channel.\n";
+
+ SetAndValidateChannelEndPointArg("local", name, value, cupsPort);
+ }
+ else if (strcasecmp(name, "keybd") == 0 ||
+ strcasecmp(name, "aux") == 0)
+ {
+ SetAndValidateChannelEndPointArg("local", name, value, auxPort);
+ }
+ else if (strcasecmp(name, "samba") == 0 ||
+ strcasecmp(name, "smb") == 0)
+ {
+ SetAndValidateChannelEndPointArg("local", name, value, smbPort);
+ }
+ else if (strcasecmp(name, "media") == 0)
+ {
+ SetAndValidateChannelEndPointArg("local", name, value, mediaPort);
+ }
+ else if (strcasecmp(name, "http") == 0)
+ {
+ SetAndValidateChannelEndPointArg("local", name, value, httpPort);
+ }
+ else if (strcasecmp(name, "font") == 0)
+ {
+ strncpy(fontPort, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "slave") == 0)
+ {
+ SetAndValidateChannelEndPointArg("local", name, value, slavePort);
+ }
+ else if (strcasecmp(name, "mask") == 0)
+ {
+ control -> ChannelMask = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "timeout") == 0)
+ {
+ int timeout = ValidateArg("local", name, value);
+
+ if (timeout == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling timeout on broken "
+ << "proxy connection.\n" << logofs_flush;
+ #endif
+
+ control -> ProxyTimeout = 0;
+ }
+ else
+ {
+ control -> ProxyTimeout = timeout * 1000;
+ }
+ }
+ else if (strcasecmp(name, "cleanup") == 0)
+ {
+ int cleanup = ValidateArg("local", name, value);
+
+ if (cleanup == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling grace timeout on "
+ << "proxy shutdown.\n" << logofs_flush;
+ #endif
+
+ control -> CleanupTimeout = 0;
+ }
+ else
+ {
+ control -> CleanupTimeout = cleanup * 1000;
+ }
+ }
+ else if (strcasecmp(name, "pack") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else if (ParsePackOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify pack method for string '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify pack method for string '"
+ << value << "'.\n";
+ if (ParsePackOption("nopack")<0)
+ return -1;
+ }
+ }
+ else if (strcasecmp(name, "core") == 0)
+ {
+ control -> EnableCoreDumpOnAbort = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "kill") == 0)
+ {
+ if (control -> KillDaemonOnShutdownNumber <
+ control -> KillDaemonOnShutdownLimit)
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Adding process with pid '"
+ << ValidateArg("local", name, value) << " to the "
+ << "daemons to kill at shutdown.\n"
+ << logofs_flush;
+ #endif
+
+ control -> KillDaemonOnShutdown[control ->
+ KillDaemonOnShutdownNumber] =
+ ValidateArg("local", name, value);
+
+ control -> KillDaemonOnShutdownNumber++;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Number of daemons to kill "
+ << "at shutdown exceeded.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Number of daemons to kill "
+ << "at shutdown exceeded.\n";
+ }
+ }
+ else if (strcasecmp(name, "strict") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ PrintOptionIgnored("local", name, value);
+ }
+ else
+ {
+ useStrict = ValidateArg("local", name, value);
+ }
+ }
+ else if (strcasecmp(name, "encryption") == 0)
+ {
+ useEncryption = ValidateArg("local", name, value);
+ }
+ else if (strcasecmp(name, "product") == 0)
+ {
+ strncpy(productName, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ else if (strcasecmp(name, "rootless") == 0 ||
+ strcasecmp(name, "geometry") == 0 ||
+ strcasecmp(name, "resize") == 0 ||
+ strcasecmp(name, "fullscreen") == 0 ||
+ strcasecmp(name, "keyboard") == 0 ||
+ strcasecmp(name, "clipboard") == 0 ||
+ strcasecmp(name, "streaming") == 0 ||
+ strcasecmp(name, "backingstore") == 0 ||
+ strcasecmp(name, "sleep") == 0 ||
+ strcasecmp(name, "tolerancechecks") == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "Loop: Ignoring agent option '" << name
+ << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+ else if (strcasecmp(name, "composite") == 0 ||
+ strcasecmp(name, "shmem") == 0 ||
+ strcasecmp(name, "shpix") == 0 ||
+ strcasecmp(name, "kbtype") == 0 ||
+ strcasecmp(name, "client") == 0 ||
+ strcasecmp(name, "shadow") == 0 ||
+ strcasecmp(name, "shadowuid") == 0 ||
+ strcasecmp(name, "shadowmode") == 0 ||
+ strcasecmp(name, "clients") == 0 ||
+ strcasecmp(name, "xinerama") == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "Loop: Ignoring agent option '" << name
+ << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+ else if (strcasecmp(name, "defer") == 0 ||
+ strcasecmp(name, "tile") == 0 ||
+ strcasecmp(name, "menu") == 0 ||
+ strcasecmp(name, "state") == 0 )
+ {
+ #ifdef DEBUG
+ *logofs << "Loop: Ignoring agent option '" << name
+ << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring unknown option '"
+ << name << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring unknown option '"
+ << name << "' with value '" << value << "'.\n";
+ }
+
+ name = strtok(NULL, "=");
+
+ } // End of while (name) ...
+
+ // Assemble the connectSocket channel end point if parameter values have been old-school...
+ if (connectSocket.disabled() && (connectHost[0] != '\0') && (proxyPort > 0 || connectPort > 0))
+ {
+ if (connectPort < 0)
+ connectPort = proxyPort + DEFAULT_NX_PROXY_PORT_OFFSET;
+
+ char tcpHostAndPort[DEFAULT_STRING_LENGTH] = { 0 };
+ sprintf(tcpHostAndPort, "tcp:%s:%ld", connectHost, connectPort);
+ SetAndValidateChannelEndPointArg("local", name, tcpHostAndPort, connectSocket);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Completed parsing of string '"
+ << env << "'.\n" << logofs_flush;
+ #endif
+
+ if ((*fileOptions != '\0') && (strncmp(fileOptions, "/dev/", 5) != 0) && (strncmp(fileOptions, "/proc/", 6) != 0) && (strncmp(fileOptions, "/sys/", 5) != 0))
+ {
+ if (strcmp(fileOptions, optionsFileName) != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Reading options from '" << fileOptions
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (ParseFileOptions(fileOptions) < 0)
+ {
+ return -1;
+ }
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Loop: WARNING! Name of the options file "
+ << "specified multiple times. Not parsing "
+ << "again.\n" << logofs_flush;
+ }
+ #endif
+
+ if (*optionsFileName == '\0')
+ {
+ strncpy(optionsFileName, value, DEFAULT_STRING_LENGTH - 1);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming name of options file '"
+ << optionsFileName << "'.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+ //
+ // If port where proxy is acting as an X server
+ // was not specified assume the same port where
+ // proxy is listening for the remote peer.
+ //
+
+ if (xPort == DEFAULT_NX_X_PORT)
+ {
+ xPort = proxyPort;
+ }
+
+ return 1;
+}
+
+//
+// Parse the command line options passed by user when
+// running proxy in stand alone mode. Note that passing
+// parameters this way is strongly discouraged. These
+// command line switch can change (and they do often).
+// Please, use the form "option=value" instead and set
+// the DISPLAY environment variable.
+//
+
+int ParseCommandLineOptions(int argc, const char **argv)
+{
+ //
+ // Be sure log file is valid.
+ //
+
+ if (logofs == NULL)
+ {
+ logofs = &cerr;
+ }
+
+ if (setjmp(context) == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Out of the long jump while parsing "
+ << "the command line options.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Be sure we have a parameters repository
+ //
+
+ if (control == NULL)
+ {
+ control = new Control();
+ }
+
+ if (parsedCommand == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Skipping a further parse of command line options.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Going to parse the command line options.\n"
+ << logofs_flush;
+ #endif
+
+ parsedCommand = 1;
+
+ //
+ // Print out arguments.
+ //
+
+ #ifdef TEST
+
+ *logofs << "Loop: Argc is " << argc << ".\n" << logofs_flush;
+
+ for (int argi = 0; argi < argc; argi++)
+ {
+ *logofs << "Loop: Argv[" << argi << "] is " << argv[argi]
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Shall use getopt here.
+ //
+
+ for (int argi = 1; argi < argc; argi++)
+ {
+ const char *nextArg = argv[argi];
+
+ if (*nextArg == '-')
+ {
+ switch (*(nextArg + 1))
+ {
+ case 'h':
+ {
+ PrintUsageInfo(nextArg, 0);
+
+ return -1;
+ }
+ case 'C':
+ {
+ //
+ // Start proxy in CLIENT mode.
+ //
+
+ if (WE_SET_PROXY_MODE == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Setting local proxy mode to proxy_client.\n"
+ << logofs_flush;
+ #endif
+
+ control -> ProxyMode = proxy_client;
+ }
+ else if (control -> ProxyMode != proxy_client)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't redefine local proxy to "
+ << "client mode.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't redefine local proxy to "
+ << "client mode.\n";
+
+ return -1;
+ }
+
+ break;
+ }
+ case 'S':
+ {
+ //
+ // Start proxy in SERVER mode.
+ //
+
+ if (WE_SET_PROXY_MODE == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Setting local proxy mode to proxy_server.\n"
+ << logofs_flush;
+ #endif
+
+ control -> ProxyMode = proxy_server;
+ }
+ else if (control -> ProxyMode != proxy_server)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't redefine local proxy to "
+ << "server mode.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't redefine local proxy to "
+ << "server mode.\n";
+
+ return -1;
+ }
+
+ break;
+ }
+ case 'v':
+ {
+ PrintVersionInfo();
+
+ return -1;
+ }
+ default:
+ {
+ PrintUsageInfo(nextArg, 1);
+
+ //
+ // Function GetArg() is not used anymore.
+ // Add a dummy call to avoid the warning.
+ //
+
+ if (0)
+ {
+ GetArg(argi, argc, argv);
+ }
+
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ if (nextArg)
+ {
+ //
+ // Try to parse the option as a remote host:port
+ // specification as in 'localhost:8'. Such a
+ // parameter can be specified at the end of the
+ // command line at the connecting side.
+ //
+
+ char cHost[DEFAULT_STRING_LENGTH] = { '\0' };
+ long cPort = 0;
+
+ if (ParseHostOption(nextArg, cHost, cPort) > 0)
+ {
+ //
+ // Assume port is at a proxied display offset.
+ //
+
+ proxyPort = cPort;
+
+ cPort += DEFAULT_NX_PROXY_PORT_OFFSET;
+ connectSocket.setSpec(cHost, cPort);
+
+ }
+ else if (ParseEnvironmentOptions(nextArg, 1) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+//
+// Set the variable to the values of host and
+// port where this proxy is going to hook to
+// an existing proxy.
+//
+
+int ParseBindOptions(char **host, int *port)
+{
+ if (*bindHost != '\0')
+ {
+ *host = bindHost;
+ *port = bindPort;
+
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+//
+// Read options from file and merge with environment.
+//
+
+int ParseFileOptions(const char *file)
+{
+ char *fileName;
+
+ if (*file != '/' && *file != '.')
+ {
+ char *filePath = GetSessionPath();
+
+ if (filePath == NULL)
+ {
+ cerr << "Error" << ": Cannot determine directory for NX option file.\n";
+
+ HandleCleanup();
+ }
+
+ fileName = new char[strlen(filePath) + strlen("/") +
+ strlen(file) + 1];
+
+ strcpy(fileName, filePath);
+
+ strcat(fileName, "/");
+ strcat(fileName, file);
+
+ delete [] filePath;
+ }
+ else
+ {
+ fileName = new char[strlen(file) + 1];
+
+ strcpy(fileName, file);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Going to read options from file '"
+ << fileName << "'.\n" << logofs_flush;
+ #endif
+
+ FILE *filePtr = fopen(fileName, "r");
+
+ if (filePtr == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't open options file '" << fileName
+ << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't open options file '" << fileName
+ << "'. Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ delete [] fileName;
+
+ return -1;
+ }
+
+ char options[DEFAULT_DISPLAY_OPTIONS_LENGTH];
+
+ #ifdef VALGRIND
+
+ memset(options, '\0', DEFAULT_DISPLAY_OPTIONS_LENGTH);
+
+ #endif
+
+ if (fgets(options, DEFAULT_DISPLAY_OPTIONS_LENGTH, filePtr) == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't read options from file '" << fileName
+ << "'. Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't read options from file '" << fileName
+ << "'. Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ fclose(filePtr);
+
+ delete [] fileName;
+
+ return -1;
+ }
+
+ fclose(filePtr);
+
+ //
+ // Purge the newline and the other non-
+ // printable characters in the string.
+ //
+
+ char *next = options;
+
+ while (*next != '\0')
+ {
+ if (isprint(*next) == 0)
+ {
+ *next = '\0';
+ }
+
+ next++;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Read options '" << options << "' from file '"
+ << fileName << "'.\n" << logofs_flush;
+ #endif
+
+ if (ParseEnvironmentOptions(options, 1) < 0)
+ {
+ delete [] fileName;
+
+ return -1;
+ }
+
+ delete [] fileName;
+
+ return 1;
+}
+
+//
+// Parse the option string passed from the
+// remote proxy at startup.
+//
+
+int ParseRemoteOptions(char *opts)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to parse the remote options "
+ << "string '" << opts << "'.\n"
+ << logofs_flush;
+ #endif
+
+ char *name;
+ char *value;
+
+ //
+ // The options string is intended to be a series
+ // of name/value tuples in the form name=value
+ // separated by the ',' character.
+ //
+
+ int hasCookie = 0;
+ int hasLink = 0;
+ int hasPack = 0;
+ int hasCache = 0;
+ int hasImages = 0;
+ int hasDelta = 0;
+ int hasStream = 0;
+ int hasData = 0;
+ int hasType = 0;
+
+ //
+ // Get rid of the terminating space.
+ //
+
+ if (*(opts + strlen(opts) - 1) == ' ')
+ {
+ *(opts + strlen(opts) - 1) = '\0';
+ }
+
+ name = strtok(opts, "=");
+
+ while (name)
+ {
+ value = strtok(NULL, ",");
+
+ if (CheckArg("remote", name, value) < 0)
+ {
+ return -1;
+ }
+
+ if (strcasecmp(name, "cookie") == 0)
+ {
+ if (WE_PROVIDE_CREDENTIALS)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring remote option 'cookie' "
+ << "with value '" << value << "' when initiating "
+ << "connection.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring remote option 'cookie' "
+ << "with value '" << value << "' when initiating "
+ << "connection.\n";
+ }
+ else if (strncasecmp(authCookie, value, strlen(authCookie)) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Authentication cookie '" << value
+ << "' doesn't match '" << authCookie << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Authentication cookie '" << value
+ << "' doesn't match '" << authCookie << "'.\n";
+
+ return -1;
+ }
+
+ hasCookie = 1;
+ }
+ else if (strcasecmp(name, "link") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ if (*linkSpeedName != '\0' && strcasecmp(linkSpeedName, value) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding option 'link' "
+ << "with new value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Overriding option 'link' "
+ << "with new value '" << value << "'.\n";
+ }
+
+ if (ParseLinkOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify remote 'link' "
+ << "option in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify remote 'link' "
+ << "option in string '" << value << "'.\n";
+
+ return -1;
+ }
+ }
+
+ hasLink = 1;
+ }
+ else if (strcasecmp(name, "pack") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ if (*packMethodName != '\0' && strcasecmp(packMethodName, value) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding option 'pack' "
+ << "with remote value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Overriding option 'pack' "
+ << "with remote value '" << value << "'.\n";
+ }
+
+ if (ParsePackOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid pack option '"
+ << value << "' requested by remote.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid pack option '"
+ << value << "' requested by remote.\n";
+
+ return -1;
+ }
+ }
+
+ hasPack = 1;
+ }
+ else if (strcasecmp(name, "cache") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ //
+ // Cache size is sent as a hint of how much memory
+ // the remote proxy is going to consume. A very low
+ // powered thin client could choose to refuse the
+ // connection.
+ //
+
+ if (ParseCacheOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify remote 'cache' "
+ << "option in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify remote 'cache' "
+ << "option in string '" << value << "'.\n";
+
+ return -1;
+ }
+ }
+
+ hasCache = 1;
+ }
+ else if (strcasecmp(name, "images") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ //
+ // Images cache size is sent as a hint.
+ // There is no obbligation for the local
+ // proxy to use the persistent cache.
+ //
+
+ if (ParseImagesOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify remote 'images' "
+ << "option in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify remote 'images' "
+ << "option in string '" << value << "'.\n";
+
+ return -1;
+ }
+ }
+
+ hasImages = 1;
+ }
+ else if (strcasecmp(name, "limit") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ if (*bitrateLimitName != '\0' &&
+ strcasecmp(bitrateLimitName, value) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding option 'limit' "
+ << "with new value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Overriding option 'limit' "
+ << "with new value '" << value << "'.\n";
+ }
+
+ if (ParseBitrateOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify 'limit' "
+ << "option in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify 'limit' "
+ << "option in string '" << value << "'.\n";
+
+ return -1;
+ }
+ }
+
+ }
+ else if (strcasecmp(name, "render") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ useRender = ValidateArg("remote", name, value);
+ }
+
+ }
+ else if (strcasecmp(name, "taint") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ useTaint = ValidateArg("remote", name, value);
+ }
+
+ }
+ else if (strcasecmp(name, "type") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ if (strcasecmp(value, "default") == 0)
+ {
+ *sessionType = '\0';
+ }
+ else
+ {
+ strncpy(sessionType, value, DEFAULT_STRING_LENGTH - 1);
+ }
+ }
+
+ hasType = 1;
+ }
+ else if (strcasecmp(name, "strict") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ useStrict = ValidateArg("remote", name, value);
+ }
+
+ }
+ else if (strcasecmp(name, "shseg") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else if (ParseShmemOption(value) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't identify size of shared memory "
+ << "segment in string '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify size of shared memory "
+ << "segment in string '" << value << "'.\n";
+
+ return -1;
+ }
+
+ }
+ else if (strcasecmp(name, "delta") == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ PrintOptionIgnored("remote", name, value);
+ }
+ else
+ {
+ control -> RemoteDeltaCompression = ValidateArg("remote", name, value);
+
+ //
+ // Follow for delta compression the
+ // same settings as the client proxy.
+ //
+
+ control -> LocalDeltaCompression = control -> RemoteDeltaCompression;
+ }
+
+ hasDelta = 1;
+ }
+ else if (strcasecmp(name, "stream") == 0)
+ {
+ //
+ // If remote side didn't choose its own
+ // stream compression level then assume
+ // local settings.
+ //
+
+ if (strcasecmp(value, "default") == 0)
+ {
+ //
+ // This applies only at client side.
+ //
+
+ control -> RemoteStreamCompression =
+ control -> LocalStreamCompression;
+
+ control -> RemoteStreamCompressionLevel =
+ control -> LocalStreamCompressionLevel;
+ }
+ else
+ {
+ control -> RemoteStreamCompressionLevel = ValidateArg("remote", name, value);
+
+ if (control -> RemoteStreamCompressionLevel > 0)
+ {
+ control -> RemoteStreamCompression = 1;
+ }
+ else
+ {
+ control -> RemoteStreamCompression = 0;
+ }
+
+ if (control -> LocalStreamCompressionLevel < 0)
+ {
+ control -> LocalStreamCompressionLevel = ValidateArg("remote", name, value);
+
+ if (control -> LocalStreamCompressionLevel > 0)
+ {
+ control -> LocalStreamCompression = 1;
+ }
+ else
+ {
+ control -> LocalStreamCompression = 0;
+ }
+ }
+ }
+
+ hasStream = 1;
+ }
+ else if (strcasecmp(name, "data") == 0)
+ {
+ //
+ // Apply the same to data compression level.
+ //
+
+ if (strcasecmp(value, "default") == 0)
+ {
+ control -> RemoteDataCompression =
+ control -> LocalDataCompression;
+
+ control -> RemoteDataCompressionLevel =
+ control -> LocalDataCompressionLevel;
+ }
+ else
+ {
+ control -> RemoteDataCompressionLevel = ValidateArg("remote", name, value);
+
+ if (control -> RemoteDataCompressionLevel > 0)
+ {
+ control -> RemoteDataCompression = 1;
+ }
+ else
+ {
+ control -> RemoteDataCompression = 0;
+ }
+
+ if (control -> LocalDataCompressionLevel < 0)
+ {
+ control -> LocalDataCompressionLevel = ValidateArg("remote", name, value);
+
+ if (control -> LocalDataCompressionLevel > 0)
+ {
+ control -> LocalDataCompression = 1;
+ }
+ else
+ {
+ control -> LocalDataCompression = 0;
+ }
+ }
+ }
+
+ hasData = 1;
+ }
+ else if (strcasecmp(name, "flush") == 0)
+ {
+ //
+ // This option has no effect in recent
+ // versions.
+ //
+
+ #ifdef DEBUG
+ *logofs << "Loop: Ignoring obsolete remote option '"
+ << name << "' with value '" << value
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring unknown remote option '"
+ << name << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring unknown remote option '"
+ << name << "' with value '" << value << "'.\n";
+ }
+
+ name = strtok(NULL, "=");
+
+ } // End of while (name) ...
+
+ //
+ // If we are client side, we need remote 'stream'
+ // and 'data' options. If we are server, we need
+ // all the above plus 'link' and some others.
+ //
+
+ char missing[DEFAULT_STRING_LENGTH];
+
+ *missing = '\0';
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (hasStream == 0)
+ {
+ strcpy(missing, "stream");
+ }
+ else if (hasData == 0)
+ {
+ strcpy(missing, "data");
+ }
+ }
+ else
+ {
+ //
+ // Don't complain if the optional 'flush',
+ // 'render' and 'taint' options are not
+ // provided.
+ //
+
+ if (hasLink == 0)
+ {
+ strcpy(missing, "link");
+ }
+ else if (hasCache == 0)
+ {
+ strcpy(missing, "cache");
+ }
+ else if (hasPack == 0)
+ {
+ strcpy(missing, "pack");
+ }
+ else if (hasDelta == 0)
+ {
+ strcpy(missing, "delta");
+ }
+ else if (hasStream == 0)
+ {
+ strcpy(missing, "stream");
+ }
+ else if (hasData == 0)
+ {
+ strcpy(missing, "data");
+ }
+ else if (hasType == 0)
+ {
+ strcpy(missing, "type");
+ }
+ else if (hasImages == 0)
+ {
+ strcpy(missing, "images");
+ }
+ }
+
+ if (WE_PROVIDE_CREDENTIALS == 0)
+ {
+ //
+ // Can be that user doesn't have requested to
+ // check the authorization cookie provided by
+ // the connecting peer.
+ //
+
+ if (hasCookie == 0 && *authCookie != '\0')
+ {
+ strcpy(missing, "cookie");
+ }
+ }
+
+ if (*missing != '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! The remote peer didn't specify the option '"
+ << missing << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The remote peer didn't specify the option '"
+ << missing << "'.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+//
+// Parse the cookie provided by the NX proxy
+// connection forwarder.
+//
+
+int ParseForwarderOptions(char *opts)
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to parse the forwarder options "
+ << "string '" << opts << "'.\n"
+ << logofs_flush;
+ #endif
+
+ char *name;
+ char *value;
+
+ int hasCookie = 0;
+
+ //
+ // Get rid of the terminating space.
+ //
+
+ if (*(opts + strlen(opts) - 1) == ' ')
+ {
+ *(opts + strlen(opts) - 1) = '\0';
+ }
+
+ name = strtok(opts, "=");
+
+ while (name)
+ {
+ value = strtok(NULL, ",");
+
+ if (CheckArg("forwarder", name, value) < 0)
+ {
+ return -1;
+ }
+
+ if (strcasecmp(name, "cookie") == 0)
+ {
+ if (strncasecmp(authCookie, value, strlen(authCookie)) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! The NX forwarder cookie '" << value
+ << "' doesn't match '" << authCookie << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The NX forwarder cookie '" << value
+ << "' doesn't match '" << authCookie << "'.\n";
+
+ return -1;
+ }
+
+ hasCookie = 1;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring unknown forwarder option '"
+ << name << "' with value '" << value << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring unknown forwarder option '"
+ << name << "' with value '" << value << "'.\n";
+ }
+
+ name = strtok(NULL, "=");
+
+ } // End of while (name) ...
+
+ if (hasCookie == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! The NX forwarder didn't provide "
+ << "the authentication cookie.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": The NX forwarder didn't provide "
+ << "the authentication cookie.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int SetCore()
+{
+ #ifdef COREDUMPS
+
+ rlimit rlim;
+
+ if (getrlimit(RLIMIT_CORE, &rlim))
+ {
+ #ifdef TEST
+ *logofs << "Loop: Cannot read RLIMIT_CORE. Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ if (rlim.rlim_cur < rlim.rlim_max)
+ {
+ rlim.rlim_cur = rlim.rlim_max;
+
+ if (setrlimit(RLIMIT_CORE, &rlim))
+ {
+ #ifdef TEST
+ *logofs << "Loop: Cannot set RLIMIT_CORE. Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return -2;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: RLIMIT_CORE is "<< rlim.rlim_max
+ << ".\n" << logofs_flush;
+ #endif
+
+ #endif // #ifdef COREDUMPS
+
+ return 1;
+}
+
+char *GetLastCache(char *listBuffer, const char *searchPath)
+{
+ if (listBuffer == NULL || searchPath == NULL ||
+ strncmp(listBuffer, "cachelist=", strlen("cachelist=")) != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Invalid parameters '" << listBuffer << "' and '"
+ << (searchPath != NULL ? searchPath : "")
+ << "'. Can't select any cache.\n" << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+
+ char *selectedName = new char[MD5_LENGTH * 2 + 3];
+
+ *selectedName = '\0';
+
+ const char *localPrefix;
+ const char *remotePrefix;
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ localPrefix = "C-";
+ remotePrefix = "S-";
+ }
+ else
+ {
+ localPrefix = "S-";
+ remotePrefix = "C-";
+ }
+
+ //
+ // Get rid of prefix.
+ //
+
+ listBuffer += strlen("cachelist=");
+
+ char *fileName;
+
+ fileName = strtok(listBuffer, ",");
+
+ //
+ // It is "/path/to/file" + "/" + "C-" + 32 + "\0".
+ //
+
+ char fullPath[strlen(searchPath) + MD5_LENGTH * 2 + 4];
+
+ time_t selectedTime = 0;
+
+ struct stat fileStat;
+
+ while (fileName)
+ {
+ if (strncmp(fileName, "none", strlen("none")) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: No cache files seem to be available.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] selectedName;
+
+ return NULL;
+ }
+ else if (strlen(fileName) != MD5_LENGTH * 2 + 2 ||
+ strncmp(fileName, remotePrefix, 2) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Bad cache file name '"
+ << fileName << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad cache file name '"
+ << fileName << "'.\n";
+
+ delete [] selectedName;
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Parsing remote cache name '"
+ << fileName << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Prefix, received as "S-", becomes
+ // "C-" and viceversa.
+ //
+
+ *fileName = *localPrefix;
+
+ strcpy(fullPath, searchPath);
+ strcat(fullPath, "/");
+ strcat(fullPath, fileName);
+
+ if (stat(fullPath, &fileStat) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Found a matching cache '"
+ << fullPath << "'.\n" << logofs_flush;
+ #endif
+
+ if (fileStat.st_mtime >= selectedTime)
+ {
+ strcpy(selectedName, fileName);
+
+ selectedTime = fileStat.st_mtime;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Can't get stats of file '"
+ << fullPath << "'.\n" << logofs_flush;
+ }
+ #endif
+
+ fileName = strtok(NULL, ",");
+ }
+
+ if (*selectedName != '\0')
+ {
+ return selectedName;
+ }
+ else
+ {
+ delete [] selectedName;
+
+ return NULL;
+ }
+}
+
+char *GetTempPath()
+{
+ if (*tempDir == '\0')
+ {
+ //
+ // Check the NX_TEMP environment, first,
+ // then the TEMP variable.
+ //
+
+ const char *tempEnv = getenv("NX_TEMP");
+
+ if (tempEnv == NULL || *tempEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for NX_TEMP.\n"
+ << logofs_flush;
+ #endif
+
+ tempEnv = getenv("TEMP");
+
+ if (tempEnv == NULL || *tempEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for TEMP.\n"
+ << logofs_flush;
+ #endif
+
+ tempEnv = "/tmp";
+ }
+ }
+
+ if (strlen(tempEnv) > DEFAULT_STRING_LENGTH - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "temporary directory '" << tempEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "temporary directory '" << tempEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(tempDir, tempEnv);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming temporary NX directory '"
+ << tempDir << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ char *tempPath = new char[strlen(tempDir) + 1];
+
+ if (tempPath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't allocate memory "
+ << "for the temp path.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the temp path.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(tempPath, tempDir);
+
+ return tempPath;
+}
+
+char *GetClientPath()
+{
+ if (*clientDir == '\0')
+ {
+ //
+ // Check the NX_CLIENT environment.
+ //
+
+ const char *clientEnv = getenv("NX_CLIENT");
+
+ if (clientEnv == NULL || *clientEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for NX_CLIENT.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Try to guess the location of the client.
+ //
+
+ clientEnv = "/usr/NX/bin/nxclient";
+
+ #ifdef __APPLE__
+
+ clientEnv = "/Applications/NX Client for OSX.app/Contents/MacOS/nxclient";
+
+ #endif
+
+ #ifdef __CYGWIN32__
+
+ clientEnv = "C:\\Program Files\\NX Client for Windows\\nxclient";
+
+ #endif
+ }
+
+ if (strlen(clientEnv) > DEFAULT_STRING_LENGTH - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "client directory '" << clientEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "client directory '" << clientEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(clientDir, clientEnv);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming NX client location '"
+ << clientDir << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ char *clientPath = new char[strlen(clientDir) + 1];
+
+ if (clientPath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't allocate memory "
+ << "for the client path.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the client path.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(clientPath, clientDir);
+
+ return clientPath;
+}
+
+char *GetSystemPath()
+{
+ if (*systemDir == '\0')
+ {
+ //
+ // Check the NX_SYSTEM environment.
+ //
+
+ const char *systemEnv = getenv("NX_SYSTEM");
+
+ if (systemEnv == NULL || *systemEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for NX_SYSTEM.\n"
+ << logofs_flush;
+ #endif
+
+ systemEnv = "/usr/NX";
+ }
+
+ if (strlen(systemEnv) > DEFAULT_STRING_LENGTH - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "system directory '" << systemEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "system directory '" << systemEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(systemDir, systemEnv);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming system NX directory '"
+ << systemDir << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ char *systemPath = new char[strlen(systemDir) + 1];
+
+ if (systemPath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't allocate memory "
+ << "for the system path.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the system path.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(systemPath, systemDir);
+
+ return systemPath;
+}
+
+char *GetHomePath()
+{
+ if (*homeDir == '\0')
+ {
+ //
+ // Check the NX_HOME environment.
+ //
+
+ const char *homeEnv = getenv("NX_HOME");
+
+ if (homeEnv == NULL || *homeEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for NX_HOME.\n"
+ << logofs_flush;
+ #endif
+
+ homeEnv = getenv("HOME");
+
+ if (homeEnv == NULL || *homeEnv == '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! No environment for HOME.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No environment for HOME.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ if (strlen(homeEnv) > DEFAULT_STRING_LENGTH - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "home directory '" << homeEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "home directory '" << homeEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(homeDir, homeEnv);
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming NX user's home directory '"
+ << homeDir << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ char *homePath = new char[strlen(homeDir) + 1];
+
+ if (homePath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't allocate memory "
+ << "for the home path.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the home path.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(homePath, homeDir);
+
+ return homePath;
+}
+
+char *GetRootPath()
+{
+ if (*rootDir == '\0')
+ {
+ //
+ // Check the NX_ROOT environment.
+ //
+
+ const char *rootEnv = getenv("NX_ROOT");
+
+ if (rootEnv == NULL || *rootEnv == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No environment for NX_ROOT.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // We will determine the root NX directory
+ // based on the NX_HOME or HOME directory
+ // settings.
+ //
+
+ const char *homeEnv = GetHomePath();
+
+ if (strlen(homeEnv) > DEFAULT_STRING_LENGTH -
+ strlen("/.nx") - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "home directory '" << homeEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "home directory '" << homeEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming NX root directory in "
+ << "the user's home '" << homeEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ strcpy(rootDir, homeEnv);
+ strcat(rootDir, "/.nx");
+
+ delete [] homeEnv;
+
+ //
+ // Create the NX root directory.
+ //
+
+ struct stat dirStat;
+
+ if ((stat(rootDir, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ if (mkdir(rootDir, 0700) < 0 && (EGET() != EEXIST))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't create directory '"
+ << rootDir << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create directory '"
+ << rootDir << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ HandleCleanup();
+ }
+ }
+ }
+ else
+ {
+ if (strlen(rootEnv) > DEFAULT_STRING_LENGTH - 1)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value for the NX "
+ << "root directory '" << rootEnv
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value for the NX "
+ << "root directory '" << rootEnv
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(rootDir, rootEnv);
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming NX root directory '"
+ << rootDir << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ char *rootPath = new char[strlen(rootDir) + 1];
+
+ if (rootPath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't allocate memory "
+ << "for the root path.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the root path.\n";
+
+ HandleCleanup();
+ }
+
+ strcpy(rootPath, rootDir);
+
+ return rootPath;
+}
+
+char *GetCachePath()
+{
+ char *rootPath = GetRootPath();
+
+ char *cachePath;
+
+ if (*sessionType != '\0')
+ {
+ cachePath = new char[strlen(rootPath) + strlen("/cache-") +
+ strlen(sessionType) + 1];
+ }
+ else
+ {
+ cachePath = new char[strlen(rootPath) + strlen("/cache") + 1];
+ }
+
+ strcpy(cachePath, rootPath);
+
+ if (*sessionType != '\0')
+ {
+ strcat(cachePath, "/cache-");
+
+ strcat(cachePath, sessionType);
+ }
+ else
+ {
+ strcat(cachePath, "/cache");
+ }
+
+ //
+ // Create the cache directory if needed.
+ //
+
+ struct stat dirStat;
+
+ if ((stat(cachePath, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ if (mkdir(cachePath, 0700) < 0 && (EGET() != EEXIST))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't create directory '" << cachePath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create directory '" << cachePath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ delete [] rootPath;
+ delete [] cachePath;
+
+ return NULL;
+ }
+ }
+
+ delete [] rootPath;
+
+ return cachePath;
+}
+
+char *GetImagesPath()
+{
+ char *rootPath = GetRootPath();
+
+ char *imagesPath = new char[strlen(rootPath) + strlen("/images") + 1];
+
+ strcpy(imagesPath, rootPath);
+
+ strcat(imagesPath, "/images");
+
+ //
+ // Create the cache directory if needed.
+ //
+
+ struct stat dirStat;
+
+ if ((stat(imagesPath, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ if (mkdir(imagesPath, 0700) < 0 && (EGET() != EEXIST))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't create directory '" << imagesPath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create directory '" << imagesPath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ delete [] rootPath;
+ delete [] imagesPath;
+
+ return NULL;
+ }
+ }
+
+ //
+ // Create 16 directories in the path to
+ // hold the images whose name begins with
+ // the corresponding hexadecimal digit.
+ //
+
+ char *digitPath = new char[strlen(imagesPath) + 5];
+
+ strcpy(digitPath, imagesPath);
+
+ //
+ // Image paths have format "[path][/I-c][\0]",
+ // where c is the first digit of the checksum.
+ //
+
+ for (char digit = 0; digit < 16; digit++)
+ {
+ sprintf(digitPath + strlen(imagesPath), "/I-%01X", digit);
+
+ if ((stat(digitPath, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ if (mkdir(digitPath, 0700) < 0 && (EGET() != EEXIST))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't create directory '" << digitPath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create directory '" << digitPath
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ delete [] rootPath;
+ delete [] imagesPath;
+ delete [] digitPath;
+
+ return NULL;
+ }
+ }
+ }
+
+ delete [] rootPath;
+ delete [] digitPath;
+
+ return imagesPath;
+}
+
+char *GetSessionPath()
+{
+ if (*sessionDir == '\0')
+ {
+ char *rootPath = GetRootPath();
+
+ strcpy(sessionDir, rootPath);
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ strcat(sessionDir, "/C-");
+ }
+ else
+ {
+ strcat(sessionDir, "/S-");
+ }
+
+ if (*sessionId == '\0')
+ {
+ char port[DEFAULT_STRING_LENGTH];
+
+ sprintf(port, "%d", proxyPort);
+
+ strcpy(sessionId, port);
+ }
+
+ strcat(sessionDir, sessionId);
+
+ struct stat dirStat;
+
+ if ((stat(sessionDir, &dirStat) == -1) && (EGET() == ENOENT))
+ {
+ if (mkdir(sessionDir, 0700) < 0 && (EGET() != EEXIST))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't create directory '" << sessionDir
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create directory '" << sessionDir
+ << ". Error is " << EGET() << " '" << ESTR() << "'.\n";
+
+ delete [] rootPath;
+
+ return NULL;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Root of NX session is '" << sessionDir
+ << "'.\n" << logofs_flush;
+ #endif
+
+ delete [] rootPath;
+ }
+
+ char *sessionPath = new char[strlen(sessionDir) + 1];
+
+ strcpy(sessionPath, sessionDir);
+
+ return sessionPath;
+}
+
+//
+// Identify requested link characteristics
+// and set control parameters accordingly.
+//
+
+int ParseLinkOption(const char *opt)
+{
+ //
+ // Normalize the user input.
+ //
+
+ if (strcasecmp(opt, "modem") == 0 ||
+ strcasecmp(opt, "33k") == 0 ||
+ strcasecmp(opt, "56k") == 0)
+ {
+ strcpy(linkSpeedName, "MODEM");
+ }
+ else if (strcasecmp(opt, "isdn") == 0 ||
+ strcasecmp(opt, "64k") == 0 ||
+ strcasecmp(opt, "128k") == 0)
+ {
+ strcpy(linkSpeedName, "ISDN");
+ }
+ else if (strcasecmp(opt, "adsl") == 0 ||
+ strcasecmp(opt, "256k") == 0 ||
+ strcasecmp(opt, "640k") == 0)
+ {
+ strcpy(linkSpeedName, "ADSL");
+ }
+ else if (strcasecmp(opt, "wan") == 0 ||
+ strcasecmp(opt, "1m") == 0 ||
+ strcasecmp(opt, "2m") == 0 ||
+ strcasecmp(opt, "34m") == 0)
+ {
+ strcpy(linkSpeedName, "WAN");
+ }
+ else if (strcasecmp(opt, "lan") == 0 ||
+ strcasecmp(opt, "10m") == 0 ||
+ strcasecmp(opt, "100m") == 0 ||
+ strcasecmp(opt, "local") == 0)
+ {
+ strcpy(linkSpeedName, "LAN");
+ }
+
+ if (strcasecmp(linkSpeedName, "modem") != 0 &&
+ strcasecmp(linkSpeedName, "isdn") != 0 &&
+ strcasecmp(linkSpeedName, "adsl") != 0 &&
+ strcasecmp(linkSpeedName, "wan") != 0 &&
+ strcasecmp(linkSpeedName, "lan") != 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int ParsePackOption(const char *opt)
+{
+ #ifdef DEBUG
+ *logofs << "Loop: Pack method is " << packMethod
+ << " quality is " << packQuality << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "Loop: Parsing pack method '" << opt
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (strcasecmp(opt, "0") == 0 ||
+ strcasecmp(opt, "none") == 0 ||
+ strcasecmp(opt, "nopack") == 0 ||
+ strcasecmp(opt, "no-pack") == 0)
+ {
+ packMethod = PACK_NONE;
+ }
+ else if (strcasecmp(opt, "8") == 0)
+ {
+ packMethod = PACK_MASKED_8_COLORS;
+ }
+ else if (strcasecmp(opt, "64") == 0)
+ {
+ packMethod = PACK_MASKED_64_COLORS;
+ }
+ else if (strcasecmp(opt, "256") == 0)
+ {
+ packMethod = PACK_MASKED_256_COLORS;
+ }
+ else if (strcasecmp(opt, "512") == 0)
+ {
+ packMethod = PACK_MASKED_512_COLORS;
+ }
+ else if (strcasecmp(opt, "4k") == 0)
+ {
+ packMethod = PACK_MASKED_4K_COLORS;
+ }
+ else if (strcasecmp(opt, "32k") == 0)
+ {
+ packMethod = PACK_MASKED_32K_COLORS;
+ }
+ else if (strcasecmp(opt, "64k") == 0)
+ {
+ packMethod = PACK_MASKED_64K_COLORS;
+ }
+ else if (strcasecmp(opt, "256k") == 0)
+ {
+ packMethod = PACK_MASKED_256K_COLORS;
+ }
+ else if (strcasecmp(opt, "2m") == 0)
+ {
+ packMethod = PACK_MASKED_2M_COLORS;
+ }
+ else if (strcasecmp(opt, "16m") == 0)
+ {
+ packMethod = PACK_MASKED_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "8-jpeg", strlen("8-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_8_COLORS;
+ }
+ else if (strncasecmp(opt, "64-jpeg", strlen("64-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_64_COLORS;
+ }
+ else if (strncasecmp(opt, "256-jpeg", strlen("256-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_256_COLORS;
+ }
+ else if (strncasecmp(opt, "512-jpeg", strlen("512-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_512_COLORS;
+ }
+ else if (strncasecmp(opt, "4k-jpeg", strlen("4k-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_4K_COLORS;
+ }
+ else if (strncasecmp(opt, "32k-jpeg", strlen("32k-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_32K_COLORS;
+ }
+ else if (strncasecmp(opt, "64k-jpeg", strlen("64k-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_64K_COLORS;
+ }
+ else if (strncasecmp(opt, "256k-jpeg", strlen("256k-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_256K_COLORS;
+ }
+ else if (strncasecmp(opt, "2m-jpeg", strlen("2m-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_2M_COLORS;
+ }
+ else if (strncasecmp(opt, "16m-jpeg", strlen("16m-jpeg")) == 0)
+ {
+ packMethod = PACK_JPEG_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "8-png", strlen("8-png")) == 0)
+ {
+ packMethod = PACK_PNG_8_COLORS;
+ }
+ else if (strncasecmp(opt, "64-png", strlen("64-png")) == 0)
+ {
+ packMethod = PACK_PNG_64_COLORS;
+ }
+ else if (strncasecmp(opt, "256-png", strlen("256-png")) == 0)
+ {
+ packMethod = PACK_PNG_256_COLORS;
+ }
+ else if (strncasecmp(opt, "512-png", strlen("512-png")) == 0)
+ {
+ packMethod = PACK_PNG_512_COLORS;
+ }
+ else if (strncasecmp(opt, "4k-png", strlen("4k-png")) == 0)
+ {
+ packMethod = PACK_PNG_4K_COLORS;
+ }
+ else if (strncasecmp(opt, "32k-png", strlen("32k-png")) == 0)
+ {
+ packMethod = PACK_PNG_32K_COLORS;
+ }
+ else if (strncasecmp(opt, "64k-png", strlen("64k-png")) == 0)
+ {
+ packMethod = PACK_PNG_64K_COLORS;
+ }
+ else if (strncasecmp(opt, "256k-png", strlen("256k-png")) == 0)
+ {
+ packMethod = PACK_PNG_256K_COLORS;
+ }
+ else if (strncasecmp(opt, "2m-png", strlen("2m-png")) == 0)
+ {
+ packMethod = PACK_PNG_2M_COLORS;
+ }
+ else if (strncasecmp(opt, "16m-png", strlen("16m-png")) == 0)
+ {
+ packMethod = PACK_PNG_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "16m-rgb", strlen("16m-rgb")) == 0 ||
+ strncasecmp(opt, "rgb", strlen("rgb")) == 0)
+ {
+ packMethod = PACK_RGB_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "16m-rle", strlen("16m-rle")) == 0 ||
+ strncasecmp(opt, "rle", strlen("rle")) == 0)
+ {
+ packMethod = PACK_RLE_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "16m-bitmap", strlen("16m-bitmap")) == 0 ||
+ strncasecmp(opt, "bitmap", strlen("bitmap")) == 0)
+ {
+ packMethod = PACK_BITMAP_16M_COLORS;
+ }
+ else if (strncasecmp(opt, "lossy", strlen("lossy")) == 0)
+ {
+ packMethod = PACK_LOSSY;
+ }
+ else if (strncasecmp(opt, "lossless", strlen("lossless")) == 0)
+ {
+ packMethod = PACK_LOSSLESS;
+ }
+ else if (strncasecmp(opt, "adaptive", strlen("adaptive")) == 0)
+ {
+ packMethod = PACK_ADAPTIVE;
+ }
+ else
+ {
+ return -1;
+ }
+
+ if (packMethod == PACK_NONE)
+ {
+ strcpy(packMethodName, "none");
+ }
+ else
+ {
+ strcpy(packMethodName, opt);
+ }
+
+ if (packMethod == PACK_RGB_16M_COLORS ||
+ packMethod == PACK_RLE_16M_COLORS ||
+ packMethod == PACK_BITMAP_16M_COLORS ||
+ (packMethod >= PACK_JPEG_8_COLORS &&
+ packMethod <= PACK_JPEG_16M_COLORS) ||
+ (packMethod >= PACK_PNG_8_COLORS &&
+ packMethod <= PACK_PNG_16M_COLORS) ||
+ packMethod == PACK_LOSSY ||
+ packMethod == PACK_LOSSLESS ||
+ packMethod == PACK_ADAPTIVE)
+ {
+ const char *dash = strrchr(opt, '-');
+
+ if (dash != NULL && strlen(dash) == 2 &&
+ *(dash + 1) >= '0' && *(dash + 1) <= '9')
+ {
+ packQuality = atoi(dash + 1);
+
+ #ifdef DEBUG
+ *logofs << "Loop: Using pack quality '"
+ << packQuality << "'.\n" << logofs_flush;
+ #endif
+ }
+ }
+ else
+ {
+ packQuality = 0;
+ }
+
+ return 1;
+}
+
+int ParsePackMethod(const int method, const int quality)
+{
+ switch (method)
+ {
+ case PACK_NONE:
+ {
+ strcpy(packMethodName, "none");
+
+ break;
+ }
+ case PACK_MASKED_8_COLORS:
+ {
+ strcpy(packMethodName, "8");
+
+ break;
+ }
+ case PACK_MASKED_64_COLORS:
+ {
+ strcpy(packMethodName, "64");
+
+ break;
+ }
+ case PACK_MASKED_256_COLORS:
+ {
+ strcpy(packMethodName, "256");
+
+ break;
+ }
+ case PACK_MASKED_512_COLORS:
+ {
+ strcpy(packMethodName, "512");
+
+ break;
+ }
+ case PACK_MASKED_4K_COLORS:
+ {
+ strcpy(packMethodName, "4k");
+
+ break;
+ }
+ case PACK_MASKED_32K_COLORS:
+ {
+ strcpy(packMethodName, "32k");
+
+ break;
+ }
+ case PACK_MASKED_64K_COLORS:
+ {
+ strcpy(packMethodName, "64k");
+
+ break;
+ }
+ case PACK_MASKED_256K_COLORS:
+ {
+ strcpy(packMethodName, "256k");
+
+ break;
+ }
+ case PACK_MASKED_2M_COLORS:
+ {
+ strcpy(packMethodName, "2m");
+
+ break;
+ }
+ case PACK_MASKED_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m");
+
+ break;
+ }
+ case PACK_JPEG_8_COLORS:
+ {
+ strcpy(packMethodName, "8-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_64_COLORS:
+ {
+ strcpy(packMethodName, "64-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_256_COLORS:
+ {
+ strcpy(packMethodName, "256-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_512_COLORS:
+ {
+ strcpy(packMethodName, "512-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_4K_COLORS:
+ {
+ strcpy(packMethodName, "4k-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_32K_COLORS:
+ {
+ strcpy(packMethodName, "32k-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_64K_COLORS:
+ {
+ strcpy(packMethodName, "64k-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_256K_COLORS:
+ {
+ strcpy(packMethodName, "256k-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_2M_COLORS:
+ {
+ strcpy(packMethodName, "2m-jpeg");
+
+ break;
+ }
+ case PACK_JPEG_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m-jpeg");
+
+ break;
+ }
+ case PACK_PNG_8_COLORS:
+ {
+ strcpy(packMethodName, "8-png");
+
+ break;
+ }
+ case PACK_PNG_64_COLORS:
+ {
+ strcpy(packMethodName, "64-png");
+
+ break;
+ }
+ case PACK_PNG_256_COLORS:
+ {
+ strcpy(packMethodName, "256-png");
+
+ break;
+ }
+ case PACK_PNG_512_COLORS:
+ {
+ strcpy(packMethodName, "512-png");
+
+ break;
+ }
+ case PACK_PNG_4K_COLORS:
+ {
+ strcpy(packMethodName, "4k-png");
+
+ break;
+ }
+ case PACK_PNG_32K_COLORS:
+ {
+ strcpy(packMethodName, "32k-png");
+
+ break;
+ }
+ case PACK_PNG_64K_COLORS:
+ {
+ strcpy(packMethodName, "64k-png");
+
+ break;
+ }
+ case PACK_PNG_256K_COLORS:
+ {
+ strcpy(packMethodName, "256k-png");
+
+ break;
+ }
+ case PACK_PNG_2M_COLORS:
+ {
+ strcpy(packMethodName, "2m-png");
+
+ break;
+ }
+ case PACK_PNG_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m-png");
+
+ break;
+ }
+ case PACK_RGB_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m-rgb");
+
+ break;
+ }
+ case PACK_RLE_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m-rle");
+
+ break;
+ }
+ case PACK_BITMAP_16M_COLORS:
+ {
+ strcpy(packMethodName, "16m-bitmap");
+
+ break;
+ }
+ case PACK_LOSSY:
+ {
+ strcpy(packMethodName, "lossy");
+
+ break;
+ }
+ case PACK_LOSSLESS:
+ {
+ strcpy(packMethodName, "lossless");
+
+ break;
+ }
+ case PACK_ADAPTIVE:
+ {
+ strcpy(packMethodName, "adaptive");
+
+ break;
+ }
+ default:
+ {
+ return -1;
+ }
+ }
+
+ if (quality < 0 || quality > 9)
+ {
+ return -1;
+ }
+
+ if (packMethod == PACK_RGB_16M_COLORS ||
+ packMethod == PACK_RLE_16M_COLORS ||
+ packMethod == PACK_BITMAP_16M_COLORS ||
+ (packMethod >= PACK_JPEG_8_COLORS &&
+ packMethod <= PACK_JPEG_16M_COLORS) ||
+ (packMethod >= PACK_PNG_8_COLORS &&
+ packMethod <= PACK_PNG_16M_COLORS) ||
+ packMethod == PACK_LOSSY ||
+ packMethod == PACK_LOSSLESS ||
+ packMethod == PACK_ADAPTIVE)
+ {
+ sprintf(packMethodName + strlen(packMethodName),
+ "-%d", quality);
+ }
+
+ packMethod = method;
+ packQuality = quality;
+
+ control -> PackMethod = packMethod;
+ control -> PackQuality = packQuality;
+
+ return 1;
+}
+
+int SetDirectories()
+{
+ //
+ // Determine the location of the user's NX
+ // directory and the other relevant paths.
+ // The functions below will check the pa-
+ // rameters passed to the program and will
+ // query the environment, if needed.
+ //
+
+ control -> HomePath = GetHomePath();
+ control -> RootPath = GetRootPath();
+ control -> SystemPath = GetSystemPath();
+ control -> TempPath = GetTempPath();
+ control -> ClientPath = GetClientPath();
+
+ return 1;
+}
+
+int SetLogs()
+{
+ //
+ // So far we used stderr (or stdout under
+ // WIN32). Now use the files selected by
+ // the user.
+ //
+
+ if (*statsFileName == '\0')
+ {
+ strcpy(statsFileName, "stats");
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming default statistics file '"
+ << statsFileName << "'.\n" << logofs_flush;
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Name selected for statistics is '"
+ << statsFileName << "'.\n" << logofs_flush;
+ }
+ #endif
+
+ if (OpenLogFile(statsFileName, statofs) < 0)
+ {
+ HandleCleanup();
+ }
+
+ #ifndef MIXED
+
+ if (*errorsFileName == '\0')
+ {
+ strcpy(errorsFileName, "errors");
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming default log file name '"
+ << errorsFileName << "'.\n" << logofs_flush;
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Name selected for log file is '"
+ << errorsFileName << "'.\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Share the bebug output with the nxssh binder
+ // process. The file must be made writable by
+ // everybody because the nxssh process is run by
+ // nxserver as the nx user.
+ //
+
+ #ifdef BINDER
+
+ strcpy(errorsFileName, "/tmp/errors");
+
+ ostream *tmpfs = new ofstream(errorsFileName, ios::out);
+
+ delete tmpfs;
+
+ chmod(errorsFileName, S_IRUSR | S_IWUSR | S_IRGRP |
+ S_IWGRP | S_IROTH | S_IWOTH);
+
+ #endif
+
+ if (OpenLogFile(errorsFileName, logofs) < 0)
+ {
+ HandleCleanup();
+ }
+
+ //
+ // By default the session log is the standard error
+ // of the process. It is anyway required to set the
+ // option when running inside SSH, otherwise the
+ // output will go to the same file as the SSH log,
+ // depending where the NX client has redirected the
+ // output.
+ //
+
+ if (*sessionFileName != '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: Name selected for session file is '"
+ << sessionFileName << "'.\n" << logofs_flush;
+ #endif
+
+ if (errofs != NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Unexpected value for stream errofs.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unexpected value for stream errofs.\n";
+ }
+
+ if (errsbuf != NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Unexpected value for buffer errsbuf.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unexpected value for buffer errsbuf.\n";
+ }
+
+ errofs = NULL;
+ errsbuf = NULL;
+
+ if (OpenLogFile(sessionFileName, errofs) < 0)
+ {
+ HandleCleanup();
+ }
+
+ //
+ // Redirect the standard error to the file.
+ //
+
+ errsbuf = cerr.rdbuf(errofs -> rdbuf());
+ }
+
+ #endif
+
+ return 1;
+}
+
+int SetPorts()
+{
+ //
+ // Depending on the proxy side, we need to determine on which
+ // port to listen for the given protocol or to which port we
+ // will have to forward the connection. Three possibilities
+ // are given for each supported protocol:
+ //
+ // Port <= 0: Disable port forwarding.
+ // Port == 1: Use the default port.
+ // Port > 1: Use the specified port.
+ //
+ // At the connectiong side the user should always explicitly
+ // set the ports where the connections will be forwarded. This
+ // is both for security reasons and because, when running both
+ // proxies on the same host, there is a concrete possibility
+ // that, by using the default ports, the connection will be
+ // forwarded to the same port where the peer proxy is listen-
+ // ing, causing a loop.
+ //
+
+ useCupsSocket = 0;
+ if (cupsPort.enabled()) {
+ if (control -> ProxyMode == proxy_client) {
+ cupsPort.setDefaultTCPPort(DEFAULT_NX_CUPS_PORT_OFFSET + proxyPort);
+ useCupsSocket = 1;
+ }
+ else
+ cupsPort.setDefaultTCPPort(631);
+ }
+
+#ifdef TEST
+ *logofs << "Loop: cups port: " << cupsPort << "\n"
+ << logofs_flush;
+#endif
+
+ useAuxSocket = 0;
+ if (auxPort.enabled()) {
+ if (control -> ProxyMode == proxy_client) {
+ auxPort.setDefaultTCPPort(DEFAULT_NX_AUX_PORT_OFFSET + proxyPort);
+ useAuxSocket = 1;
+ }
+ else {
+ auxPort.setDefaultTCPPort(1);
+
+ if (auxPort.getTCPPort() != 1) {
+
+#ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding auxiliary X11 "
+ << "port with new value '" << 1 << "'.\n"
+ << logofs_flush;
+#endif
+
+ cerr << "Warning" << ": Overriding auxiliary X11 "
+ << "port with new value '" << 1 << "'.\n";
+
+ auxPort.setSpec("1");
+ }
+ }
+ }
+
+#ifdef TEST
+ *logofs << "Loop: aux port: " << auxPort << "\n"
+ << logofs_flush;
+#endif
+
+ useSmbSocket = 0;
+ if (smbPort.enabled()) {
+ if (control -> ProxyMode == proxy_client) {
+ auxPort.setDefaultTCPPort(DEFAULT_NX_SMB_PORT_OFFSET + proxyPort);
+ useAuxSocket = 1;
+ }
+ else
+ auxPort.setDefaultTCPPort(139);
+ }
+
+
+#ifdef TEST
+ *logofs << "Loop: smb port: " << smbPort << "\n"
+ << logofs_flush;
+#endif
+
+ useMediaSocket = 0;
+ if (mediaPort.enabled()) {
+ if (control -> ProxyMode == proxy_client) {
+ mediaPort.setDefaultTCPPort(DEFAULT_NX_MEDIA_PORT_OFFSET + proxyPort);
+ useMediaSocket = 1;
+ }
+ else if (mediaPort.getTCPPort() == 1) {
+#ifdef PANIC
+ *logofs << "Loop: PANIC! No port specified for multimedia connections.\n"
+ << logofs_flush;
+#endif
+
+ cerr << "Error" << ": No port specified for multimedia connections.\n";
+
+ HandleCleanup();
+ }
+ }
+
+#ifdef TEST
+ *logofs << "Loop: Using multimedia port '" << mediaPort
+ << "'.\n" << logofs_flush;
+#endif
+
+ useHttpSocket = 0;
+ if (httpPort.enabled()) {
+ if (control -> ProxyMode == proxy_client) {
+ httpPort.setDefaultTCPPort(DEFAULT_NX_HTTP_PORT_OFFSET + proxyPort);
+ useHttpSocket = 1;
+ }
+ else
+ httpPort.setDefaultTCPPort(80);
+ }
+
+#ifdef TEST
+ *logofs << "Loop: Using HTTP port '" << httpPort
+ << "'.\n" << logofs_flush;
+#endif
+
+ if (ParseFontPath(fontPort) <= 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling font server connections.\n"
+ << logofs_flush;
+ #endif
+
+ *fontPort = '\0';
+
+ useFontSocket = 0;
+ }
+ else
+ {
+ //
+ // We don't know yet if the remote proxy supports
+ // the font server connections. If needed, we will
+ // disable the font server connections at later
+ // time.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ useFontSocket = 1;
+ }
+ else
+ {
+ useFontSocket = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Using font server port '" << fontPort
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ useSlaveSocket = 0;
+ if (slavePort.enabled()) {
+ useSlaveSocket = 1;
+ if (control -> ProxyMode == proxy_client)
+ slavePort.setDefaultTCPPort(DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET + proxyPort);
+ else
+ slavePort.setDefaultTCPPort(DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET + proxyPort);
+ }
+
+#ifdef TEST
+ *logofs << "Loop: Using slave port '" << slavePort
+ << "'.\n" << logofs_flush;
+#endif
+
+ return 1;
+}
+
+int SetDescriptors()
+{
+ unsigned int limit = 0;
+
+ #ifdef RLIMIT_NOFILE
+
+ rlimit limits;
+
+ if (getrlimit(RLIMIT_NOFILE, &limits) == 0)
+ {
+ if (limits.rlim_max == RLIM_INFINITY)
+ {
+ limit = 0;
+ }
+ else
+ {
+ limit = (unsigned int) limits.rlim_max;
+ }
+ }
+
+ #endif
+
+ #ifdef _SC_OPEN_MAX
+
+ if (limit == 0)
+ {
+ limit = sysconf(_SC_OPEN_MAX);
+ }
+
+ #endif
+
+ #ifdef FD_SETSIZE
+
+ if (limit > FD_SETSIZE)
+ {
+ limit = FD_SETSIZE;
+ }
+
+ #endif
+
+ #ifdef RLIMIT_NOFILE
+
+ if (limits.rlim_cur < limit)
+ {
+ limits.rlim_cur = limit;
+
+ setrlimit(RLIMIT_NOFILE, &limits);
+ }
+
+ #endif
+
+ if (limit == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Cannot determine number of available "
+ << "file descriptors.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot determine number of available "
+ << "file descriptors.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+//
+// Find the directory containing the caches
+// matching the session type.
+//
+
+int SetCaches()
+{
+ if ((control -> PersistentCachePath = GetCachePath()) == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error getting or creating the cache path.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error getting or creating the cache path.\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Path of cache files is '" << control -> PersistentCachePath
+ << "'.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// Initialize all configuration parameters.
+//
+
+int SetParameters()
+{
+ //
+ // Find out the type of session.
+ //
+
+ SetSession();
+
+ //
+ // Initialize the network and compression
+ // parameters according to the settings
+ // suggested by the user.
+ //
+
+ SetLink();
+
+ //
+ // Set compression according to link speed.
+ //
+
+ SetCompression();
+
+ //
+ // Be sure that we have a literal for current
+ // cache size. Value will reflect control's
+ // default unless we already parsed a 'cache'
+ // option. Server side has no control on size
+ // of cache but is informed at session nego-
+ // tiation about how much memory is going to
+ // be used.
+ //
+
+ SetStorage();
+
+ //
+ // Set size of shared memory segments.
+ //
+
+ SetShmem();
+
+ //
+ // Make adjustments to cache based
+ // on the pack method.
+ //
+
+ SetPack();
+
+ //
+ // Set disk-based image cache.
+ //
+
+ SetImages();
+
+ //
+ // Set CPU and bandwidth limits.
+ //
+
+ SetLimits();
+
+ return 1;
+}
+
+//
+// According to session literal determine
+// the type of traffic that is going to be
+// transported. Literals should be better
+// standardized in future NX versions.
+//
+
+int SetSession()
+{
+ if (strncmp(sessionType, "agent", strlen("agent")) == 0 ||
+ strncmp(sessionType, "desktop", strlen("desktop")) == 0 ||
+ strncmp(sessionType, "rootless", strlen("rootless")) == 0 ||
+ strncmp(sessionType, "console", strlen("console")) == 0 ||
+ strncmp(sessionType, "default", strlen("default")) == 0 ||
+ strncmp(sessionType, "gnome", strlen("gnome")) == 0 ||
+ strncmp(sessionType, "kde", strlen("kde")) == 0 ||
+ strncmp(sessionType, "cde", strlen("cde")) == 0 ||
+ strncmp(sessionType, "xdm", strlen("xdm")) == 0)
+ {
+ control -> SessionMode = session_agent;
+ }
+ else if (strncmp(sessionType, "win", strlen("win")) == 0 ||
+ strncmp(sessionType, "vnc", strlen("vnc")) == 0)
+ {
+ control -> SessionMode = session_agent;
+ }
+ else if (strncmp(sessionType, "shadow", strlen("shadow")) == 0)
+ {
+ control -> SessionMode = session_shadow;
+ }
+ else if (strncmp(sessionType, "proxy", strlen("proxy")) == 0 ||
+ strncmp(sessionType, "application", strlen("application")) == 0 ||
+ strncmp(sessionType, "raw", strlen("raw")) == 0)
+ {
+ control -> SessionMode = session_proxy;
+ }
+ else
+ {
+ //
+ // If the session type is not passed or
+ // it is not among the recognized strings,
+ // we assume that the proxy is connected
+ // to the agent.
+ //
+
+ //
+ // Since ProtoStep8 (#issue 108) and also
+ // with older "unix-" sessions
+ //
+
+ if (*sessionType != '\0')
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Unrecognized session type '"
+ << sessionType << "'. Assuming agent session.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unrecognized session type '"
+ << sessionType << "'. Assuming agent session.\n";
+ }
+
+ control -> SessionMode = session_agent;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Assuming session type '"
+ << DumpSession(control -> SessionMode) << "' with "
+ << "string '" << sessionType << "'.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // By default the policy is immediate. Agents
+ // will set a different policy, if they like.
+ // Anyway we need to check if the user has
+ // provided a custom flush policy.
+ //
+
+ if (usePolicy != -1)
+ {
+ if (usePolicy > 0)
+ {
+ control -> FlushPolicy = policy_deferred;
+ }
+ else
+ {
+ control -> FlushPolicy = policy_immediate;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: WARNING! Forcing flush policy to '"
+ << DumpPolicy(control -> FlushPolicy)
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ control -> FlushPolicy = policy_immediate;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting initial flush policy to '"
+ << DumpPolicy(control -> FlushPolicy)
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ //
+ // Check if the proxy library is run inside
+ // another program providing encryption, as
+ // it is the case of the SSH client.
+ //
+
+ if (useEncryption != -1)
+ {
+ if (useEncryption > 0)
+ {
+ control -> LinkEncrypted = 1;
+ }
+ else
+ {
+ control -> LinkEncrypted = 0;
+ }
+ }
+
+ if (control -> LinkEncrypted == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Proxy running as part of an "
+ << "encrypting client.\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Assuming proxy running as a "
+ << "standalone program.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // Check if the system administrator has
+ // enabled the respawn of the client at
+ // the end of session.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ struct stat fileStat;
+
+ char fileName[DEFAULT_STRING_LENGTH];
+
+ snprintf(fileName, DEFAULT_STRING_LENGTH - 1,
+ "%s/share/noexit", control -> SystemPath);
+
+ *(fileName + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ if (stat(fileName, &fileStat) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Enabling respawn of client at session shutdown.\n"
+ << logofs_flush;
+ #endif
+
+ control -> EnableRestartOnShutdown = 1;
+ }
+ }
+
+ return 1;
+}
+
+int SetStorage()
+{
+ //
+ // If differential compression is disabled
+ // we don't need a cache at all.
+ //
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ control -> ClientTotalStorageSize = 0;
+ control -> ServerTotalStorageSize = 0;
+ }
+
+ //
+ // Set a a cache size literal.
+ //
+
+ int size = control -> getUpperStorageSize();
+
+ if (size / 1024 > 0)
+ {
+ sprintf(cacheSizeName, "%dk", size / 1024);
+ }
+ else
+ {
+ sprintf(cacheSizeName, "%d", size);
+ }
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ control -> LocalTotalStorageSize =
+ control -> ClientTotalStorageSize;
+
+ control -> RemoteTotalStorageSize =
+ control -> ServerTotalStorageSize;
+ }
+ else
+ {
+ control -> LocalTotalStorageSize =
+ control -> ServerTotalStorageSize;
+
+ control -> RemoteTotalStorageSize =
+ control -> ClientTotalStorageSize;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Storage size limit is "
+ << control -> ClientTotalStorageSize
+ << " at client and "
+ << control -> ServerTotalStorageSize
+ << " at server.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "Loop: Storage local limit set to "
+ << control -> LocalTotalStorageSize
+ << " remote limit set to "
+ << control -> RemoteTotalStorageSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Never reserve for split store more than
+ // half the memory available for messages.
+ //
+
+ if (size > 0 && control ->
+ SplitTotalStorageSize > size / 2)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Reducing size of split store to "
+ << size / 2 << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ control -> SplitTotalStorageSize = size / 2;
+ }
+
+ //
+ // Don't load render from persistent
+ // cache if extension is hidden or
+ // not supported by agent.
+ //
+
+ if (control -> HideRender == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Not loading render extension "
+ << "from persistent cache.\n"
+ << logofs_flush;
+ #endif
+
+ control -> PersistentCacheLoadRender = 0;
+ }
+
+ return 1;
+}
+
+int SetShmem()
+{
+ //
+ // If not set, adjust the size of the shared
+ // memory segment according to size of the
+ // message cache.
+ //
+
+ if (*shsegSizeName == '\0')
+ {
+ int size = control -> getUpperStorageSize();
+
+ const int mega = 1048576;
+
+ if (size > 0)
+ {
+ if (size <= 1 * mega)
+ {
+ size = 0;
+ }
+ else if (size <= 2 * mega)
+ {
+ size = 524288;
+ }
+ else if (size < 4 * mega)
+ {
+ size = 1048576;
+ }
+ else
+ {
+ size = size / 4;
+ }
+
+ if (size > 4194304)
+ {
+ size = 4194304;
+ }
+
+ control -> ShmemClientSize = size;
+ control -> ShmemServerSize = size;
+ }
+ else
+ {
+ //
+ // The delta compression is disabled.
+ // Use a default segment size of 2 MB.
+ //
+
+ control -> ShmemServerSize = 2 * mega;
+ }
+ }
+
+ //
+ // Client side shared memory support is
+ // not useful and not implemented.
+ //
+
+ if (control -> ShmemServerSize >= 524288)
+ {
+ control -> ShmemServer = 1;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Set initial shared memory size "
+ << "to " << control -> ShmemServerSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Disabled use of the shared memory "
+ << "extension.\n" << logofs_flush;
+ #endif
+
+ control -> ShmemServer = 0;
+ }
+
+ // For android, no shared memory available
+ control -> ShmemServer = 0;
+ control -> ShmemClientSize = 0;
+
+ return 1;
+}
+
+//
+// Adjust the pack method according to the
+// type of the session.
+//
+
+int SetPack()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting pack with initial method "
+ << packMethod << " and quality " << packQuality
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if this is a proxy session and, in
+ // this case, set the pack method to none.
+ // Packed images are not supported by plain
+ // X applications.
+ //
+
+ if (control -> SessionMode == session_proxy)
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Disabling pack with proxy session.\n"
+ << logofs_flush;
+ #endif
+
+ packMethod = PACK_NONE;
+ }
+
+ //
+ // Adjust the internal settings according
+ // to the newly selected pack method.
+ //
+
+ ParsePackMethod(packMethod, packQuality);
+
+ //
+ // Don't load messages from persistent
+ // cache if packed images are disabled.
+ //
+
+ if (control -> PackMethod == PACK_NONE)
+ {
+ control -> PersistentCacheLoadPacked = 0;
+
+ #ifdef TEST
+ *logofs << "Loop: Not loading packed images "
+ << "from persistent cache.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+//
+// Set the disk-based image cache parameters
+// according to the user's wishes.
+//
+
+int SetImages()
+{
+ //
+ // Be sure we disable the image cache if we
+ // are connecting to plain X clients.
+ //
+
+ if (control -> SessionMode == session_proxy)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling image cache with "
+ << "session '" << DumpSession(control ->
+ SessionMode) << "'.\n" << logofs_flush;
+ #endif
+
+ sprintf(imagesSizeName, "0");
+
+ control -> ImageCacheEnableLoad = 0;
+ control -> ImageCacheEnableSave = 0;
+
+ return 1;
+ }
+
+ int size = control -> ImageCacheDiskLimit;
+
+ if (size / 1024 > 0)
+ {
+ sprintf(imagesSizeName, "%dk", size / 1024);
+ }
+ else
+ {
+ sprintf(imagesSizeName, "%d", size);
+ }
+
+ if (size > 0)
+ {
+ control -> ImageCacheEnableLoad = 1;
+ control -> ImageCacheEnableSave = 1;
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ if ((control -> ImageCachePath = GetImagesPath()) == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error getting or creating image cache path.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error getting or creating image cache path.\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Path of image cache files is '" << control -> ImageCachePath
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling the persistent image cache.\n"
+ << logofs_flush;
+ #endif
+
+ control -> ImageCacheEnableLoad = 0;
+ control -> ImageCacheEnableSave = 0;
+ }
+
+ return 1;
+}
+
+int SetVersion()
+{
+ //
+ // Normalize the different proxy versions.
+ //
+
+ int local = (control -> LocalVersionMajor << 24) |
+ (control -> LocalVersionMinor << 16) |
+ control -> LocalVersionPatch;
+
+ int remote = (control -> RemoteVersionMajor << 24) |
+ (control -> RemoteVersionMinor << 16) |
+ control -> RemoteVersionPatch;
+
+ int major = -1;
+ int minor = -1;
+ int patch = -1;
+
+ if (control -> RemoteVersionMajor <= 1)
+ {
+ //
+ // The remote proxy uses a different
+ // logic to determine the version so
+ // we default to the compatibility
+ // version.
+ //
+
+ major = control -> CompatVersionMajor;
+ minor = control -> CompatVersionMinor;
+ patch = control -> CompatVersionPatch;
+
+ #ifdef TEST
+ *logofs << "Loop: Using compatibility version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ else if (control -> LocalVersionMajor >
+ control -> RemoteVersionMajor)
+ {
+ //
+ // We use a more recent version. Let's
+ // negotiate the version based on the
+ // version supported by the remote.
+ //
+
+ major = control -> RemoteVersionMajor;
+ minor = control -> RemoteVersionMinor;
+ patch = control -> RemoteVersionPatch;
+
+ #ifdef TEST
+ *logofs << "Loop: Using remote version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // We support a major version that is
+ // equal or older than the remote. We
+ // assume the smaller version between
+ // the two, including the minor and
+ // the patch numbers.
+ //
+
+ if (local > remote)
+ {
+ major = control -> RemoteVersionMajor;
+ minor = control -> RemoteVersionMinor;
+ patch = control -> RemoteVersionPatch;
+
+ #ifdef TEST
+ *logofs << "Loop: Using remote version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ major = control -> LocalVersionMajor;
+ minor = control -> LocalVersionMinor;
+ patch = control -> LocalVersionPatch;
+
+ #ifdef TEST
+ *logofs << "Loop: Using local version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+ }
+
+ //
+ // Handle versions from 3.5.0. The protocol
+ // step 10 is the minimum supported version.
+ //
+
+ int step = 0;
+
+ if (major == 3)
+ {
+ if (minor >= 5)
+ {
+ step = 10;
+ }
+ }
+ else if (major > 3)
+ {
+ step = 10;
+ }
+
+ if (step == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Unable to set the protocol step value from "
+ << "the negotiated protocol version " << major << "." << minor
+ << "." << patch << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unable to set the protocol step value from "
+ << "the negotiated protocol version " << major << "." << minor
+ << "." << patch << ".\n";
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Incompatible remote version "
+ << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
+ << "." << control -> RemoteVersionPatch << " with local version "
+ << control -> LocalVersionMajor << "." << control -> LocalVersionMinor
+ << "." << control -> LocalVersionPatch << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Incompatible remote version "
+ << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
+ << "." << control -> RemoteVersionPatch << " with local version "
+ << control -> LocalVersionMajor << "." << control -> LocalVersionMinor
+ << "." << control -> LocalVersionPatch << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Using NX protocol step "
+ << step << ".\n" << logofs_flush;
+ #endif
+
+ control -> setProtoStep(step);
+
+ //
+ // Ignore the differences in patch version
+ // and print a warning if the local version
+ // is different or obsolete compared to
+ // the remote.
+ //
+
+ local &= 0xffff0000;
+ remote &= 0xffff0000;
+
+ if (local != remote)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Connected to remote version "
+ << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
+ << "." << control -> RemoteVersionPatch << " with local version "
+ << control -> LocalVersionMajor << "." << control -> LocalVersionMinor
+ << "." << control -> LocalVersionPatch << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Connected to remote version "
+ << control -> RemoteVersionMajor << "." << control -> RemoteVersionMinor
+ << "." << control -> RemoteVersionPatch << " with local version "
+ << control -> LocalVersionMajor << "." << control -> LocalVersionMinor
+ << "." << control -> LocalVersionPatch << ".\n" << logofs_flush;
+ }
+
+ if (local < remote)
+ {
+ cerr << "Warning" << ": Consider checking https://github.com/ArcticaProject/nx-libs/releases for updates.\n";
+ }
+
+ //
+ // Now that we are aware of the remote
+ // version, let's adjust the options to
+ // be compatible with the remote proxy.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ //
+ // Since ProtoStep8 (#issue 108)
+ //
+ // Now it's assumed that the remote is
+ // able to handle the selected pack
+ // method
+ //
+
+ #ifdef TEST
+ *logofs << __FILE__ << " : " << __LINE__ << " - "
+ << "step = " << control -> getProtoStep()
+ << " packMethod = " << packMethod
+ << " packQuality = " << packQuality
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Update the pack method name.
+ //
+
+ ParsePackMethod(packMethod, packQuality);
+ }
+
+ //
+ // At the moment the image cache is not used by the
+ // agent. Proxy versions older than 3.0.0 assumed
+ // that it was enabled and sent specific bits as part
+ // of the encoding. Conversely, it is advisable to
+ // disable the cache right now. By not enabling the
+ // the image cache, the house-keeping process will
+ // only take care of cleaning up the "cache-" direc-
+ // tories.
+ //
+
+ //
+ // Considering that compatibility with older versions
+ // has been set to cover as far as 3.5.0, the cache can
+ // be disabled at this point without any concern
+ //
+
+ // Since ProtoStep8 (#issue 108)
+ #ifdef TEST
+ *logofs << "Loop: Disabling image cache with protocol "
+ << "step '" << control -> getProtoStep()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ sprintf(imagesSizeName, "0");
+
+ control -> ImageCacheEnableLoad = 0;
+ control -> ImageCacheEnableSave = 0;
+
+ return 1;
+}
+
+//
+// Identify the requested link settings
+// and update the control parameters
+// accordingly.
+//
+
+int SetLink()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting link with initial value "
+ << linkSpeedName << ".\n" << logofs_flush;
+ #endif
+
+ if (*linkSpeedName == '\0')
+ {
+ strcpy(linkSpeedName, "lan");
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Link speed is " << linkSpeedName
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (strcasecmp(linkSpeedName, "modem") == 0)
+ {
+ SetLinkModem();
+ }
+ else if (strcasecmp(linkSpeedName, "isdn") == 0)
+ {
+ SetLinkIsdn();
+ }
+ else if (strcasecmp(linkSpeedName, "adsl") == 0)
+ {
+ SetLinkAdsl();
+ }
+ else if (strcasecmp(linkSpeedName, "wan") == 0)
+ {
+ SetLinkWan();
+ }
+ else if (strcasecmp(linkSpeedName, "lan") == 0)
+ {
+ SetLinkLan();
+ }
+ else
+ {
+ return -1;
+ }
+
+ //
+ // Set TCP_NODELAY according to the user's
+ // wishes.
+ //
+
+ if (useNoDelay != -1)
+ {
+ control -> OptionProxyClientNoDelay = useNoDelay;
+ control -> OptionProxyServerNoDelay = useNoDelay;
+ }
+
+ //
+ // Select the image compression method.
+ //
+
+ if (packMethod == -1)
+ {
+ packMethod = control -> PackMethod;
+ }
+
+ if (packQuality == -1)
+ {
+ packQuality = control -> PackQuality;
+ }
+
+ if (ParsePackMethod(packMethod, packQuality) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Unrecognized pack method id "
+ << packMethod << " with quality " << packQuality
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unrecognized pack method id "
+ << packMethod << " with quality " << packQuality
+ << ".\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Check if the user disabled the ability
+ // to generate simple replies at the client
+ // side.
+ //
+
+ if (control -> SessionMode == session_proxy)
+ {
+ if (useTaint != -1)
+ {
+ control -> TaintReplies = (useTaint == 1);
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Forcing taint of replies "
+ << "with a proxy session.\n"
+ << logofs_flush;
+ #endif
+
+ control -> TaintReplies = 1;
+ }
+ }
+ else
+ {
+ //
+ // There is no need to taint the
+ // replies if we have an agent.
+ //
+
+ control -> TaintReplies = 0;
+ }
+
+ //
+ // Be sure that the requests needing a reply
+ // are flushed immediately. Normal X clients
+ // use so many replies to make the queuing
+ // completely useless.
+ //
+
+ if (control -> SessionMode == session_proxy)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Forcing flush on priority "
+ << "with a proxy session.\n"
+ << logofs_flush;
+ #endif
+
+ control -> FlushPriority = 1;
+ }
+
+ return 1;
+}
+
+//
+// Parameters for MODEM 28.8/33.6/56 Kbps.
+//
+
+int SetLinkModem()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting parameters for MODEM.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LinkMode = LINK_TYPE_MODEM;
+
+ control -> TokenSize = 256;
+ control -> TokenLimit = 24;
+
+ control -> SplitMode = 1;
+ control -> SplitTotalSize = 128;
+ control -> SplitTotalStorageSize = 1048576;
+
+ control -> SplitTimeout = 50;
+ control -> MotionTimeout = 50;
+ control -> IdleTimeout = 50;
+
+ control -> PackMethod = PACK_ADAPTIVE;
+ control -> PackQuality = 3;
+
+ return 1;
+}
+
+//
+// Parameters for ISDN 64/128 Kbps.
+//
+
+int SetLinkIsdn()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting parameters for ISDN.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LinkMode = LINK_TYPE_ISDN;
+
+ control -> TokenSize = 384;
+ control -> TokenLimit = 24;
+
+ control -> SplitMode = 1;
+ control -> SplitTotalSize = 128;
+ control -> SplitTotalStorageSize = 1048576;
+
+ control -> SplitTimeout = 50;
+ control -> MotionTimeout = 20;
+ control -> IdleTimeout = 50;
+
+ control -> PackMethod = PACK_ADAPTIVE;
+ control -> PackQuality = 5;
+
+ return 1;
+}
+
+//
+// Parameters for ADSL 256 Kbps.
+//
+
+int SetLinkAdsl()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting parameters for ADSL.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LinkMode = LINK_TYPE_ADSL;
+
+ control -> TokenSize = 1536;
+ control -> TokenLimit = 24;
+
+ control -> SplitMode = 1;
+ control -> SplitTotalSize = 128;
+ control -> SplitTotalStorageSize = 1048576;
+
+ control -> SplitTimeout = 50;
+ control -> MotionTimeout = 10;
+ control -> IdleTimeout = 50;
+
+ control -> PackMethod = PACK_ADAPTIVE;
+ control -> PackQuality = 7;
+
+ return 1;
+}
+
+//
+// Parameters for XDSL/FDDI/ATM 1/2/34 Mbps WAN.
+//
+
+int SetLinkWan()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting parameters for WAN.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LinkMode = LINK_TYPE_WAN;
+
+ control -> TokenSize = 1536;
+ control -> TokenLimit = 24;
+
+ control -> SplitMode = 1;
+ control -> SplitTotalSize = 128;
+ control -> SplitTotalStorageSize = 1048576;
+
+ control -> SplitTimeout = 50;
+ control -> MotionTimeout = 5;
+ control -> IdleTimeout = 50;
+
+ control -> PackMethod = PACK_ADAPTIVE;
+ control -> PackQuality = 9;
+
+ return 1;
+}
+
+//
+// Parameters for LAN 10/100 Mbps.
+//
+
+int SetLinkLan()
+{
+ #ifdef TEST
+ *logofs << "Loop: Setting parameters for LAN.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LinkMode = LINK_TYPE_LAN;
+
+ control -> TokenSize = 1536;
+ control -> TokenLimit = 24;
+
+ control -> SplitMode = 1;
+ control -> SplitTotalSize = 128;
+ control -> SplitTotalStorageSize = 1048576;
+
+ control -> SplitTimeout = 50;
+ control -> MotionTimeout = 0;
+ control -> IdleTimeout = 50;
+
+ control -> PackMethod = PACK_ADAPTIVE;
+ control -> PackQuality = 9;
+
+ return 1;
+}
+
+//
+// Identify the requested link type and set
+// the control parameters accordingly.
+//
+
+int SetCompression()
+{
+ if (strcasecmp(linkSpeedName, "modem") == 0)
+ {
+ SetCompressionModem();
+ }
+ else if (strcasecmp(linkSpeedName, "isdn") == 0)
+ {
+ SetCompressionIsdn();
+ }
+ else if (strcasecmp(linkSpeedName, "adsl") == 0)
+ {
+ SetCompressionAdsl();
+ }
+ else if (strcasecmp(linkSpeedName, "wan") == 0)
+ {
+ SetCompressionWan();
+ }
+ else if (strcasecmp(linkSpeedName, "lan") == 0)
+ {
+ SetCompressionLan();
+ }
+ else
+ {
+ return -1;
+ }
+
+ if (control -> LocalDeltaCompression < 0)
+ {
+ control -> LocalDeltaCompression = 1;
+ }
+
+ //
+ // If we didn't set remote delta compression
+ // (as it should always be the case at client
+ // side) assume value of local side.
+ //
+
+ if (control -> RemoteDeltaCompression < 0)
+ {
+ control -> RemoteDeltaCompression =
+ control -> LocalDeltaCompression;
+ }
+
+ //
+ // If we didn't set remote compression levels
+ // assume values of local side.
+ //
+
+ if (control -> RemoteStreamCompression < 0)
+ {
+ control -> RemoteStreamCompressionLevel =
+ control -> LocalStreamCompressionLevel;
+
+ if (control -> RemoteStreamCompressionLevel > 0)
+ {
+ control -> RemoteStreamCompression = 1;
+ }
+ else
+ {
+ control -> RemoteStreamCompression = 0;
+ }
+ }
+
+ if (control -> RemoteDataCompression < 0)
+ {
+ control -> RemoteDataCompressionLevel =
+ control -> LocalDataCompressionLevel;
+
+ if (control -> RemoteDataCompressionLevel > 0)
+ {
+ control -> RemoteDataCompression = 1;
+ }
+ else
+ {
+ control -> RemoteDataCompression = 0;
+ }
+ }
+
+ return 1;
+}
+
+//
+// Compression for MODEM.
+//
+
+int SetCompressionModem()
+{
+ if (control -> LocalDataCompression < 0)
+ {
+ control -> LocalDataCompression = 1;
+ control -> LocalDataCompressionLevel = 1;
+ }
+
+ if (control -> LocalDataCompressionThreshold < 0)
+ {
+ control -> LocalDataCompressionThreshold = 32;
+ }
+
+ if (control -> LocalStreamCompression < 0)
+ {
+ control -> LocalStreamCompression = 1;
+ control -> LocalStreamCompressionLevel = 9;
+ }
+
+ return 1;
+}
+
+//
+// Compression for ISDN.
+//
+
+int SetCompressionIsdn()
+{
+ if (control -> LocalDataCompression < 0)
+ {
+ control -> LocalDataCompression = 1;
+ control -> LocalDataCompressionLevel = 1;
+ }
+
+ if (control -> LocalDataCompressionThreshold < 0)
+ {
+ control -> LocalDataCompressionThreshold = 32;
+ }
+
+ if (control -> LocalStreamCompression < 0)
+ {
+ control -> LocalStreamCompression = 1;
+ control -> LocalStreamCompressionLevel = 6;
+ }
+
+ return 1;
+}
+
+//
+// Compression for ADSL.
+//
+
+int SetCompressionAdsl()
+{
+ if (control -> LocalDataCompression < 0)
+ {
+ control -> LocalDataCompression = 1;
+ control -> LocalDataCompressionLevel = 1;
+ }
+
+ if (control -> LocalDataCompressionThreshold < 0)
+ {
+ control -> LocalDataCompressionThreshold = 32;
+ }
+
+ if (control -> LocalStreamCompression < 0)
+ {
+ control -> LocalStreamCompression = 1;
+ control -> LocalStreamCompressionLevel = 4;
+ }
+
+ return 1;
+}
+
+//
+// Compression for WAN.
+//
+
+int SetCompressionWan()
+{
+ if (control -> LocalDataCompression < 0)
+ {
+ control -> LocalDataCompression = 1;
+ control -> LocalDataCompressionLevel = 1;
+ }
+
+ if (control -> LocalDataCompressionThreshold < 0)
+ {
+ control -> LocalDataCompressionThreshold = 32;
+ }
+
+ if (control -> LocalStreamCompression < 0)
+ {
+ control -> LocalStreamCompression = 1;
+ control -> LocalStreamCompressionLevel = 1;
+ }
+
+ return 1;
+}
+
+//
+// Compression for LAN.
+//
+
+int SetCompressionLan()
+{
+ //
+ // Disable delta compression if not
+ // explicitly enabled.
+ //
+
+ if (control -> LocalDeltaCompression < 0)
+ {
+ control -> LocalDeltaCompression = 0;
+ }
+
+ if (control -> LocalDataCompression < 0)
+ {
+ control -> LocalDataCompression = 0;
+ control -> LocalDataCompressionLevel = 0;
+ }
+
+ if (control -> LocalDataCompressionThreshold < 0)
+ {
+ control -> LocalDataCompressionThreshold = 0;
+ }
+
+ if (control -> LocalStreamCompression < 0)
+ {
+ control -> LocalStreamCompression = 0;
+ control -> LocalStreamCompressionLevel = 0;
+ }
+
+ return 1;
+}
+
+int SetLimits()
+{
+ //
+ // Check if the user requested strict
+ // control flow parameters.
+ //
+
+ if (useStrict == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: LIMIT! Decreasing the token limit "
+ << "to " << control -> TokenLimit / 2
+ << " with option 'strict'.\n"
+ << logofs_flush;
+ #endif
+
+ control -> TokenLimit /= 2;
+ }
+
+ #ifdef STRICT
+
+ control -> TokenLimit = 1;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: WARNING! LIMIT! Setting the token limit "
+ << "to " << control -> TokenLimit
+ << " to simulate the proxy congestion.\n"
+ << logofs_flush;
+ #endif
+
+ #endif
+
+ //
+ // Reduce the size of the log file.
+ //
+
+ #ifdef QUOTA
+
+ control -> FileSizeLimit = 8388608;
+
+ #endif
+
+ //
+ // Check the bitrate limits.
+ //
+
+ if (control -> LocalBitrateLimit == -1)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ control -> LocalBitrateLimit =
+ control -> ClientBitrateLimit;
+ }
+ else
+ {
+ control -> LocalBitrateLimit =
+ control -> ServerBitrateLimit;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: LIMIT! Setting client bitrate limit "
+ << "to " << control -> ClientBitrateLimit
+ << " server bitrate limit to " << control ->
+ ServerBitrateLimit << " with local limit "
+ << control -> LocalBitrateLimit << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// These functions are used to parse literal
+// values provided by the user and set the
+// control parameters accordingly.
+//
+
+int ParseCacheOption(const char *opt)
+{
+ int size = ParseArg("", "cache", opt);
+
+ if (size < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value '"
+ << opt << "' for option 'cache'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value '"
+ << opt << "' for option 'cache'.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Setting size of cache to "
+ << size << " bytes.\n" << logofs_flush;
+ #endif
+
+ control -> ClientTotalStorageSize = size;
+ control -> ServerTotalStorageSize = size;
+
+ strcpy(cacheSizeName, opt);
+
+ if (size == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Disabling NX delta compression.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LocalDeltaCompression = 0;
+
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Disabling use of NX persistent cache.\n"
+ << logofs_flush;
+ #endif
+
+ control -> PersistentCacheEnableLoad = 0;
+ control -> PersistentCacheEnableSave = 0;
+ }
+
+ return 1;
+}
+
+int ParseImagesOption(const char *opt)
+{
+ int size = ParseArg("", "images", opt);
+
+ if (size < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value '"
+ << opt << "' for option 'images'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value '"
+ << opt << "' for option 'images'.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Setting size of images cache to "
+ << size << " bytes.\n" << logofs_flush;
+ #endif
+
+ control -> ImageCacheDiskLimit = size;
+
+ strcpy(imagesSizeName, opt);
+
+ return 1;
+}
+
+int ParseShmemOption(const char *opt)
+{
+ int size = ParseArg("", "shseg", opt);
+
+ if (size < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value '"
+ << opt << "' for option 'shseg'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value '"
+ << opt << "' for option 'shseg'.\n";
+
+ return -1;
+ }
+
+ control -> ShmemClientSize = size;
+ control -> ShmemServerSize = size;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Set shared memory size to "
+ << control -> ShmemServerSize << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ strcpy(shsegSizeName, opt);
+
+ return 1;
+}
+
+int ParseBitrateOption(const char *opt)
+{
+ int bitrate = ParseArg("", "limit", opt);
+
+ if (bitrate < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid value '"
+ << opt << "' for option 'limit'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid value '"
+ << opt << "' for option 'limit'.\n";
+
+ return -1;
+ }
+
+ strcpy(bitrateLimitName, opt);
+
+ if (bitrate == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Disabling bitrate limit on proxy link.\n"
+ << logofs_flush;
+ #endif
+
+ control -> LocalBitrateLimit = 0;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: Setting bitrate to " << bitrate
+ << " bits per second.\n" << logofs_flush;
+ #endif
+
+ //
+ // Internal representation is in bytes
+ // per second.
+ //
+
+ control -> LocalBitrateLimit = bitrate >> 3;
+ }
+
+ return 1;
+}
+
+int ParseHostOption(const char *opt, char *host, long &port)
+{
+ #ifdef TEST
+ *logofs << "Loop: Trying to parse options string '" << opt
+ << "' as a remote NX host.\n" << logofs_flush;
+ #endif
+
+ if (opt == NULL || *opt == '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! No host parameter provided.\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (strlen(opt) >= DEFAULT_STRING_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Host parameter exceeds length of "
+ << DEFAULT_STRING_LENGTH << " characters.\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Look for a host name followed
+ // by a colon followed by port.
+ //
+
+ int newPort = port;
+
+ const char *separator = strrchr(opt, ':');
+
+ if (separator != NULL)
+ {
+ const char *check = separator + 1;
+
+ while (*check != '\0' && *check != ',' &&
+ *check != '=' && isdigit(*check) != 0)
+ {
+ check++;
+ }
+
+ newPort = atoi(separator + 1);
+
+ if (newPort < 0 || *check != '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: Can't identify remote NX port in string '"
+ << separator << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+ else if (newPort < 0)
+ {
+ //
+ // Complain if port was not passed
+ // by other means.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Can't identify remote NX port in string '"
+ << opt << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ separator = opt + strlen(opt);
+ }
+
+ char newHost[DEFAULT_STRING_LENGTH] = { 0 };
+
+ strncpy(newHost, opt, strlen(opt) - strlen(separator));
+
+ *(newHost + strlen(opt) - strlen(separator)) = '\0';
+
+ const char *check = newHost;
+
+ while (*check != '\0' && *check != ',' &&
+ *check != '=')
+ {
+ check++;
+ }
+
+ if (*check != '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: Can't identify remote NX host in string '"
+ << newHost << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (*acceptHost != '\0')
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't manage to connect and accept connections "
+ << "at the same time.\n" << logofs_flush;
+
+ *logofs << "Loop: PANIC! Refusing remote NX host with string '"
+ << opt << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't manage to connect and accept connections "
+ << "at the same time.\n";
+
+ cerr << "Error" << ": Refusing remote NX host with string '"
+ << opt << "'.\n";
+
+ return -1;
+ }
+
+ if (*host != '\0' && strcmp(host, newHost) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding remote NX host '"
+ << host << "' with new value '" << newHost
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ strcpy(host, newHost);
+
+ if (port != -1 && port != newPort)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Overriding remote NX port '"
+ << port << "' with new value '" << newPort
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Parsed options string '" << opt
+ << "' with host '" << newHost << "' and port '"
+ << newPort << "'.\n" << logofs_flush;
+ #endif
+
+ port = newPort;
+
+ return 1;
+}
+
+int ParseFontPath(char *path)
+{
+ char oldPath[DEFAULT_STRING_LENGTH];
+
+ strcpy(oldPath, path);
+
+ if (path == NULL || *path == '\0' || strcmp(path, "0") == 0)
+ {
+ return 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Parsing font server option '" << path
+ << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Convert the value to our default port.
+ //
+
+ if (strcmp(fontPort, "1") == 0)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ snprintf(fontPort, DEFAULT_STRING_LENGTH - 1, "%d",
+ DEFAULT_NX_FONT_PORT_OFFSET + proxyPort);
+ }
+ else
+ {
+ //
+ // Let the client use the well-known
+ // "unix/:7100" font path.
+ //
+
+ snprintf(fontPort, DEFAULT_STRING_LENGTH - 1, "unix/:7100");
+ }
+ }
+
+ //
+ // Check if a simple numaric value was given.
+ //
+
+ if (atoi(path) > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Assuming numeric TCP port '" << atoi(path)
+ << "' for font server.\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ //
+ // Let's assume that a port specification "unix/:7100"
+ // corresponds to "$TEMP/.font-unix/fs7100" and a port
+ // "unix/:-1" corresponds to "$TEMP/.font-unix/fs-1".
+ //
+
+ if (strncmp("unix/:", path, 6) == 0)
+ {
+ snprintf(path, DEFAULT_STRING_LENGTH - 1, "%s/.font-unix/fs%s",
+ control -> TempPath, oldPath + 6);
+
+ *(path + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming Unix socket '" << path
+ << "' for font server.\n" << logofs_flush;
+ #endif
+ }
+ else if (strncmp("tcp/:", path, 5) == 0)
+ {
+ snprintf(path, DEFAULT_STRING_LENGTH - 1, "%d", atoi(oldPath + 5));
+
+ *(path + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ if (atoi(path) <= 0)
+ {
+ goto ParseFontPathError;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming TCP port '" << atoi(path)
+ << "' for font server.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // Accept an absolute file path as
+ // a valid Unix socket.
+ //
+
+ if (*path != '/')
+ {
+ goto ParseFontPathError;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Assuming Unix socket '" << path
+ << "' for font server.\n" << logofs_flush;
+ #endif
+ }
+
+ return 1;
+
+ParseFontPathError:
+
+ #ifdef TEST
+ *logofs << "Loop: Unable to determine the font server "
+ << "port in string '" << path << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+}
+
+int OpenLogFile(char *name, ostream *&stream)
+{
+ if (name == NULL || *name == '\0')
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! No name provided for output. Using standard error.\n"
+ << logofs_flush;
+ #endif
+
+ if (stream == NULL)
+ {
+ stream = &cerr;
+ }
+
+ return 1;
+ }
+
+ if (stream == NULL || stream == &cerr)
+ {
+ if (*name != '/' && *name != '.')
+ {
+ char *filePath = GetSessionPath();
+
+ if (filePath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Cannot determine directory of NX session file.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot determine directory of NX session file.\n";
+
+ return -1;
+ }
+
+ if (strlen(filePath) + strlen("/") +
+ strlen(name) + 1 > DEFAULT_STRING_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Full name of NX file '" << name
+ << " would exceed length of " << DEFAULT_STRING_LENGTH
+ << " characters.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Full name of NX file '" << name
+ << " would exceed length of " << DEFAULT_STRING_LENGTH
+ << " characters.\n";
+
+ return -1;
+ }
+
+ char *file = new char[strlen(filePath) + strlen("/") +
+ strlen(name) + 1];
+
+ //
+ // Transform name in a fully qualified name.
+ //
+
+ strcpy(file, filePath);
+ strcat(file, "/");
+ strcat(file, name);
+
+ strcpy(name, file);
+
+ delete [] filePath;
+ delete [] file;
+ }
+
+ mode_t fileMode = umask(0077);
+
+ for (;;)
+ {
+ if ((stream = new ofstream(name, ios::app)) != NULL)
+ {
+ break;
+ }
+
+ usleep(200000);
+ }
+
+ umask(fileMode);
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Bad stream provided for output.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad stream provided for output.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ReopenLogFile(char *name, ostream *&stream, int limit)
+{
+ if (*name != '\0' && limit >= 0)
+ {
+ struct stat fileStat;
+
+ if (limit > 0)
+ {
+ //
+ // This is used for the log file, if the
+ // size exceeds the limit.
+ //
+
+ if (stat(name, &fileStat) != 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Can't get stats of file '"
+ << name << "'. Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (fileStat.st_size < (long) limit)
+ {
+ return 0;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Deleting file '" << name
+ << "' with size " << fileStat.st_size
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Create a new stream over the previous
+ // file. Trying to delete the file fails
+ // to work on recent Cygwin installs.
+ //
+
+ *stream << flush;
+
+ delete stream;
+
+ mode_t fileMode = umask(0077);
+
+ for (;;)
+ {
+ if ((stream = new ofstream(name, ios::out)) != NULL)
+ {
+ break;
+ }
+
+ usleep(200000);
+ }
+
+ umask(fileMode);
+
+ #ifdef TEST
+ *logofs << "Loop: Reopened file '" << name
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+void PrintProcessInfo()
+{
+ if (agent == NULL)
+ {
+
+ cerr << endl;
+
+ PrintVersionInfo();
+
+ cerr << endl;
+
+ cerr << GetCopyrightInfo()
+ << endl
+ << GetOtherCopyrightInfo()
+ << endl
+ << "See https://github.com/ArcticaProject/nx-libs for more information." << endl << endl;
+ }
+
+ //
+ // People get confused by the fact that client
+ // mode is running on NX server and viceversa.
+ // Let's adopt an user-friendly naming conven-
+ // tion here.
+ //
+
+ cerr << "Info: Proxy running in "
+ << (control -> ProxyMode == proxy_client ? "client" : "server")
+ << " mode with pid '" << getpid() << "'.\n";
+
+ if (agent == NULL)
+ {
+ cerr << "Session" << ": Starting session at '"
+ << strTimestamp() << "'.\n";
+ }
+
+ #ifdef TEST
+
+ if (*errorsFileName != '\0')
+ {
+ cerr << "Info" << ": Using errors file '" << errorsFileName << "'.\n";
+ }
+
+ if (*statsFileName != '\0')
+ {
+ cerr << "Info" << ": Using stats file '" << statsFileName << "'.\n";
+ }
+
+ #endif
+}
+
+void PrintConnectionInfo()
+{
+ cerr << "Info" << ": Using "
+ << linkSpeedName << " link parameters "
+ << control -> TokenSize
+ << "/" << control -> TokenLimit
+ << "/" << control -> FlushPolicy + 1
+ << "/" << control -> FlushPriority
+ << ".\n";
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ cerr << "Info" << ": Using agent parameters "
+ << control -> PingTimeout
+ << "/" << control -> MotionTimeout
+ << "/" << control -> IdleTimeout
+ << "/" << control -> TaintReplies
+ << "/" << control -> HideRender
+ << ".\n";
+ }
+
+ if (control -> LocalDeltaCompression == 1)
+ {
+ cerr << "Info" << ": Using cache parameters "
+ << control -> MinimumMessageSize
+ << "/" << control -> MaximumMessageSize / 1024 << "KB"
+ << "/" << control -> ClientTotalStorageSize / 1024 << "KB"
+ << "/" << control -> ServerTotalStorageSize / 1024 << "KB"
+ << ".\n";
+ }
+
+ if (control -> ImageCacheEnableLoad == 1 ||
+ control -> ImageCacheEnableSave == 1)
+ {
+ cerr << "Info" << ": Using image streaming parameters "
+ << control -> SplitTimeout
+ << "/" << control -> SplitTotalSize
+ << "/" << control -> SplitTotalStorageSize / 1024 << "KB"
+ << "/" << control -> SplitDataThreshold
+ << "/" << control -> SplitDataPacketLimit
+ << ".\n";
+
+ cerr << "Info" << ": Using image cache parameters "
+ << control -> ImageCacheEnableLoad
+ << "/" << control -> ImageCacheEnableSave
+ << "/" << control -> ImageCacheDiskLimit / 1024 << "KB"
+ << ".\n";
+ }
+
+ cerr << "Info" << ": Using pack method '"
+ << packMethodName << "' with session '"
+ << sessionType << "'.\n";
+
+ if (*productName != '\0')
+ {
+ cerr << "Info" << ": Using product '" << productName
+ << "'.\n" << logofs_flush;
+ }
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ cerr << "Info" << ": Not using NX delta compression.\n";
+ }
+
+ if (control -> LocalDataCompression == 1 ||
+ control -> RemoteDataCompression == 1)
+ {
+ cerr << "Info" << ": Using ZLIB data compression "
+ << control -> LocalDataCompressionLevel
+ << "/" << control -> RemoteDataCompressionLevel
+ << "/" << control -> LocalDataCompressionThreshold
+ << ".\n";
+ }
+ else
+ {
+ cerr << "Info" << ": Not using ZLIB data compression.\n";
+ }
+
+ if (control -> LocalStreamCompression == 1 ||
+ control -> RemoteStreamCompression == 1)
+ {
+ cerr << "Info" << ": Using ZLIB stream compression "
+ << control -> LocalStreamCompressionLevel
+ << "/" << control -> RemoteStreamCompressionLevel
+ << ".\n";
+ }
+ else
+ {
+ cerr << "Info" << ": Not using ZLIB stream compression.\n";
+ }
+
+ if (control -> LocalBitrateLimit > 0)
+ {
+ cerr << "Info" << ": Using bandwidth limit of "
+ << bitrateLimitName << " bits per second.\n";
+ }
+
+ if (control -> PersistentCacheName != NULL)
+ {
+ cerr << "Info" << ": Using cache file '"
+ << control -> PersistentCachePath << "/"
+ << control -> PersistentCacheName << "'.\n";
+ }
+ else
+ {
+ if (control -> PersistentCacheEnableLoad == 0 ||
+ control -> LocalDeltaCompression == 0)
+ {
+ cerr << "Info" << ": Not using a persistent cache.\n";
+ }
+ else
+ {
+ cerr << "Info" << ": No suitable cache file found.\n";
+ }
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ (useUnixSocket > 0 || useTcpSocket > 0 ||
+ useAgentSocket > 0))
+ {
+ cerr << "Info" << ": Listening to X11 connections "
+ << "on display ':" << xPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server)
+ {
+ cerr << "Info" << ": Forwarding X11 connections "
+ << "to display '" << displayHost << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ useCupsSocket > 0 && cupsPort.enabled())
+ {
+ cerr << "Info" << ": Listening to CUPS connections "
+ << "on port '" << cupsPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server &&
+ cupsPort.enabled())
+ {
+ cerr << "Info" << ": Forwarding CUPS connections "
+ << "to port '" << cupsPort << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ useAuxSocket > 0 && auxPort.enabled())
+ {
+ cerr << "Info" << ": Listening to auxiliary X11 connections "
+ << "on port '" << auxPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server &&
+ auxPort.enabled())
+ {
+ cerr << "Info" << ": Forwarding auxiliary X11 connections "
+ << "to display '" << displayHost << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ useSmbSocket > 0 && smbPort.enabled())
+ {
+ cerr << "Info" << ": Listening to SMB connections "
+ << "on port '" << smbPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server &&
+ smbPort.enabled())
+ {
+ cerr << "Info" << ": Forwarding SMB connections "
+ << "to port '" << smbPort << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ useMediaSocket > 0 && mediaPort.enabled())
+ {
+ cerr << "Info" << ": Listening to multimedia connections "
+ << "on port '" << mediaPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server &&
+ mediaPort.enabled())
+ {
+ cerr << "Info" << ": Forwarding multimedia connections "
+ << "to port '" << mediaPort << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_client &&
+ useHttpSocket > 0 && httpPort.enabled())
+ {
+ cerr << "Info" << ": Listening to HTTP connections "
+ << "on port '" << httpPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_server &&
+ httpPort.enabled())
+ {
+ cerr << "Info" << ": Forwarding HTTP connections "
+ << "to port '" << httpPort << "'.\n";
+ }
+
+ if (control -> ProxyMode == proxy_server &&
+ useFontSocket > 0 && *fontPort != '\0')
+ {
+ cerr << "Info" << ": Listening to font server connections "
+ << "on port '" << fontPort << "'.\n";
+ }
+ else if (control -> ProxyMode == proxy_client &&
+ *fontPort != '\0')
+ {
+ cerr << "Info" << ": Forwarding font server connections "
+ << "to port '" << fontPort << "'.\n";
+ }
+
+ if (useSlaveSocket > 0 && slavePort.enabled())
+ {
+ cerr << "Info" << ": Listening to slave connections "
+ << "on port '" << slavePort << "'.\n";
+ }
+}
+
+void PrintVersionInfo()
+{
+ cerr << "NXPROXY - " << "Version "
+ << control -> LocalVersionMajor << "."
+ << control -> LocalVersionMinor << "."
+ << control -> LocalVersionPatch << "."
+ << control -> LocalVersionMaintenancePatch;
+
+ cerr << endl;
+}
+
+void PrintCopyrightInfo()
+{
+ cerr << endl;
+
+ PrintVersionInfo();
+
+ cerr << endl;
+
+ cerr << GetCopyrightInfo();
+
+ //
+ // Print third party's copyright info.
+ //
+
+ cerr << endl;
+
+ cerr << GetOtherCopyrightInfo();
+
+ cerr << endl;
+}
+
+void PrintOptionIgnored(const char *type, const char *name, const char *value)
+{
+ if (control -> ProxyMode == proxy_server)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring " << type
+ << " option '" << name << "' with value '"
+ << value << "' at " << "NX client side.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring " << type
+ << " option '" << name << "' with value '"
+ << value << "' at " << "NX client side.\n";
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Ignoring " << type
+ << " option '" << name << "' with value '"
+ << value << "' at " << "NX server side.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Ignoring " << type
+ << " option '" << name << "' with value '"
+ << value << "' at " << "NX server side.\n";
+ }
+}
+
+const char *GetOptions(const char *options)
+{
+ if (options != NULL)
+ {
+ if (strncasecmp(options, "nx/nx,", 6) != 0 &&
+ strncasecmp(options, "nx,", 3) != 0 &&
+ strncasecmp(options, "nx:", 3) != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: PANIC! Display options string '" << options
+ << "' must start with 'nx' or 'nx/nx' prefix.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Display options string '" << options
+ << "' must start with 'nx' or 'nx/nx' prefix.\n";
+
+ HandleCleanup();
+ }
+ }
+ else
+ {
+ options = getenv("DISPLAY");
+ }
+
+ return options;
+}
+
+const char *GetArg(int &argi, int argc, const char **argv)
+{
+ //
+ // Skip "-" and flag character.
+ //
+
+ const char *arg = argv[argi] + 2;
+
+ if (*arg == 0)
+ {
+ if (argi + 1 == argc)
+ {
+ return NULL;
+ }
+ else
+ {
+ argi++;
+
+ return (*argv[argi] == '-' ? NULL : argv[argi]);
+ }
+ }
+ else
+ {
+ return (*arg == '-' ? NULL : arg);
+ }
+}
+
+int CheckArg(const char *type, const char *name, const char *value)
+{
+ #ifdef TEST
+ *logofs << "Loop: Parsing " << type << " option '" << name
+ << "' with value '" << (value ? value : "(null)")
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (value == NULL || strstr(value, "=") != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error in " << type << " option '"
+ << name << "'. No value found.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error in " << type << " option '"
+ << name << "'. No value found.\n";
+
+ return -1;
+ }
+ else if (strstr(name, ",") != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Parse error at " << type << " option '"
+ << name << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Parse error at " << type << " option '"
+ << name << "'.\n";
+
+ return -1;
+ }
+ else if (strlen(value) >= DEFAULT_STRING_LENGTH)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Value '" << value << "' of "
+ << type << " option '" << name << "' exceeds length of "
+ << DEFAULT_STRING_LENGTH << " characters.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Value '" << value << "' of "
+ << type << " option '" << name << "' exceeds length of "
+ << DEFAULT_STRING_LENGTH << " characters.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ParseArg(const char *type, const char *name, const char *value)
+{
+ if (strcasecmp(value, "0") == 0)
+ {
+ return 0;
+ }
+
+ //
+ // Find the base factor.
+ //
+
+ double base;
+
+ const char *id = value + strlen(value) - 1;
+
+ if (strcasecmp(id, "g") == 0)
+ {
+ base = 1024 * 1024 * 1024;
+ }
+ else if (strcasecmp(id, "m") == 0)
+ {
+ base = 1024 * 1024;
+ }
+ else if (strcasecmp(id, "k") == 0)
+ {
+ base = 1024;
+ }
+ else if (strcasecmp(id, "b") == 0 || isdigit(*id) == 1)
+ {
+ base = 1;
+ }
+ else
+ {
+ return -1;
+ }
+
+ char *string = new char[strlen(value)];
+
+ strncpy(string, value, strlen(value) - 1);
+
+ *(string + (strlen(value) - 1)) = '\0';
+
+ #ifdef TEST
+
+ *logofs << "Loop: Parsing integer option '" << name
+ << "' from string '" << string << "' with base set to ";
+
+ switch (tolower(*id))
+ {
+ case 'k':
+ case 'm':
+ case 'g':
+ {
+ *logofs << (char) toupper(*id);
+ }
+ break;
+ }
+
+ *logofs << ".\n" << logofs_flush;
+
+ #endif
+
+ double result = atof(string) * base;
+
+ if (result < 0 || result > (((unsigned) -1) >> 1))
+ {
+ delete [] string;
+
+ return -1;
+ }
+
+ delete [] string;
+
+ #ifdef TEST
+ *logofs << "Loop: Integer option parsed to '"
+ << (int) result << "'.\n" << logofs_flush;
+ #endif
+
+ return (int) result;
+}
+
+void SetAndValidateChannelEndPointArg(const char *type, const char *name, const char *value,
+ ChannelEndPoint &endPoint) {
+ endPoint.setSpec(value);
+ if (!endPoint.validateSpec()) {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid " << type
+ << " option '" << name << "' with value '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid " << type
+ << " option '" << name << "' with value '"
+ << value << "'.\n";
+
+ HandleCleanup();
+ }
+}
+
+
+int ValidateArg(const char *type, const char *name, const char *value)
+{
+ int number = atoi(value);
+
+ if (number < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Invalid " << type
+ << " option '" << name << "' with value '"
+ << value << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid " << type
+ << " option '" << name << "' with value '"
+ << value << "'.\n";
+
+ HandleCleanup();
+ }
+
+ return number;
+}
+
+int LowercaseArg(const char *type, const char *name, char *value)
+{
+ char *next = value;
+
+ while (*next != '\0')
+ {
+ *next = tolower(*next);
+
+ next++;
+ }
+
+ return 1;
+}
+
+int CheckSignal(int signal)
+{
+ //
+ // Return 1 if the signal needs to be handled
+ // by the proxy, 2 if the signal just needs to
+ // be blocked to avoid interrupting a system
+ // call.
+ //
+
+ switch (signal)
+ {
+ case SIGCHLD:
+ case SIGUSR1:
+ case SIGUSR2:
+ case SIGHUP:
+ case SIGINT:
+ case SIGTERM:
+ case SIGPIPE:
+ case SIGALRM:
+ {
+ return 1;
+ }
+ case SIGVTALRM:
+ case SIGWINCH:
+ case SIGIO:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ {
+ return 2;
+ }
+ default:
+ {
+ #ifdef __CYGWIN32__
+
+ //
+ // This signal can be raised by the Cygwin
+ // library.
+ //
+
+ if (signal == 12)
+ {
+ return 1;
+ }
+
+ #endif
+
+ return 0;
+ }
+ }
+}
+
+static void PrintUsageInfo(const char *option, int error)
+{
+ if (error == 1)
+ {
+ cerr << "Error" << ": Invalid command line option '" << option << "'.\n";
+ }
+
+ cerr << GetUsageInfo();
+
+ if (error == 1)
+ {
+ cerr << "Error" << ": NX transport initialization failed.\n";
+ }
+}
+
+static void handleCheckSessionInLoop()
+{
+ //
+ // Check if we completed the shutdown procedure
+ // and the remote confirmed the shutdown. The
+ // tear down should be always initiated by the
+ // agent, but the X server side may unilateral-
+ // ly shut down the link without our permission.
+ //
+
+ if (proxy -> getShutdown() > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: End of NX transport requested "
+ << "by remote.\n" << logofs_flush;
+ #endif
+
+ handleTerminatingInLoop();
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Bytes received so far are "
+ << (unsigned long long) statistics -> getBytesIn()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (statistics -> getBytesIn() < 1024)
+ {
+ cerr << "Info" << ": Your session was closed before reaching "
+ << "a usable state.\n";
+ cerr << "Info" << ": This can be due to the local X server "
+ << "refusing access to the client.\n";
+ cerr << "Info" << ": Please check authorization provided "
+ << "by the remote X application.\n";
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Shutting down the NX transport.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ else if (proxy -> handlePing() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Failure handling the ping for "
+ << "proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+
+ //
+ // Check if the watchdog has exited and we didn't
+ // get the SIGCHLD. This can happen if the parent
+ // has overridden our signal handlers.
+ //
+
+ if (IsRunning(lastWatchdog) && CheckProcess(lastWatchdog, "watchdog") == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Watchdog is gone unnoticed. "
+ << "Setting the last signal to SIGTERM.\n"
+ << logofs_flush;
+ #endif
+
+ lastSignal = SIGTERM;
+
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Resetting pid of last "
+ << "watchdog process.\n" << logofs_flush;
+ #endif
+
+ SetNotRunning(lastWatchdog);
+ }
+
+ //
+ // Let the client proxy find out if the agent's
+ // channel is gone. This is the normal shutdown
+ // procedure in the case of an internal connect-
+ // ion to the agent.
+ //
+
+ int cleanup = 0;
+
+ if (control -> ProxyMode == proxy_client &&
+ agent != NULL && proxy -> getType(agentFD[1]) ==
+ channel_none && lastKill == 0 && lastDestroy == 1)
+ {
+ #ifdef TEST
+ *logofs << "Loop: End of NX transport requested "
+ << "by agent.\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "Loop: Bytes sent so far are "
+ << (unsigned long long) statistics -> getBytesOut()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (statistics -> getBytesOut() < 1024)
+ {
+ cerr << "Info" << ": Your session has died before reaching "
+ << "an usable state.\n";
+ cerr << "Info" << ": This can be due to the remote X server "
+ << "refusing access to the client.\n";
+ cerr << "Info" << ": Please check the authorization provided "
+ << "by your X application.\n";
+ }
+
+ cleanup = 1;
+ }
+
+ //
+ // Check if the user requested the end of the
+ // session by sending a signal to the proxy.
+ // All signals are handled in the main loop
+ // so we need to reset the value to get ready
+ // for the next iteration.
+ //
+
+ int signal = 0;
+
+ if (lastSignal != 0)
+ {
+ switch (lastSignal)
+ {
+ case SIGCHLD:
+ case SIGUSR1:
+ case SIGUSR2:
+ {
+ break;
+ }
+ default:
+ {
+ signal = lastSignal;
+
+ cleanup = 1;
+
+ break;
+ }
+ }
+
+ lastSignal = 0;
+ }
+
+ if (cleanup == 1)
+ {
+ //
+ // The first time termination signal is received
+ // disable all further connections, close down any
+ // X channel and wait for a second signal.
+ //
+
+ if (lastKill == 0)
+ {
+ //
+ // Don't print a message if cleanup is
+ // due to normal termination of agent.
+ //
+
+ if (signal != 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: End of NX transport requested by signal '"
+ << signal << "' '" << DumpSignal(signal)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ handleTerminatingInLoop();
+ }
+
+ //
+ // Disable any further connection.
+ //
+
+ CleanupListeners();
+
+ //
+ // Close all the remaining X channels and
+ // let proxies save their persistent cache
+ // on disk.
+ //
+
+ CleanupConnections();
+
+ //
+ // We'll need to wait for the X channels
+ // to be shut down before waiting for the
+ // cleanup signal.
+ //
+
+ lastKill = 1;
+ }
+ else if (lastKill == 2)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Shutting down the NX transport.\n"
+ << logofs_flush;
+ #endif
+
+ proxy -> handleShutdown();
+
+ HandleCleanup();
+ }
+ }
+
+ if (lastKill == 1 && proxy -> getChannels(channel_x11) == 0)
+ {
+ //
+ // Save the message stores to the
+ // persistent cache.
+ //
+
+ proxy -> handleSave();
+
+ //
+ // Run a watchdog process so we can finally
+ // give up at the time the watchdog exits.
+ //
+
+ if (IsNotRunning(lastWatchdog))
+ {
+ int timeout = control -> CleanupTimeout;
+
+ if (timeout > 0)
+ {
+ if (proxy -> getChannels() == 0)
+ {
+ timeout = 500;
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Starting watchdog process with timeout "
+ << "of " << timeout << " Ms.\n"
+ << logofs_flush;
+ #endif
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Starting watchdog process without "
+ << "a timeout.\n" << logofs_flush;
+ }
+ #endif
+
+ lastWatchdog = NXTransWatchdog(timeout);
+
+ if (IsFailed(lastWatchdog))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't start the NX watchdog "
+ << "process in shutdown.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't start the NX watchdog "
+ << "process in shutdown.\n";
+
+ HandleCleanup();
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Watchdog started with pid '"
+ << lastWatchdog << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Previous watchdog detected "
+ << "in shutdown with pid '" << lastWatchdog
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Previous watchdog detected "
+ << "in shutdown with pid '" << lastWatchdog
+ << "'.\n";
+
+ HandleCleanup();
+ }
+
+ if (control -> CleanupTimeout > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Waiting the cleanup timeout to complete.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Waiting the cleanup timeout to complete.\n";
+ }
+ else
+ {
+ //
+ // The NX server will kill the watchdog
+ // process after having shut down the
+ // service channels.
+ //
+
+ cerr << "Info" << ": Watchdog running with pid '" << lastWatchdog
+ << "'.\n";
+
+ #ifdef TEST
+ *logofs << "Loop: Waiting the watchdog process to complete.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Waiting the watchdog process to complete.\n";
+ }
+
+ lastKill = 2;
+ }
+}
+
+static void handleCheckBitrateInLoop()
+{
+ static long int slept = 0;
+
+ #ifdef TEST
+ *logofs << "Loop: Bitrate is " << statistics -> getBitrateInShortFrame()
+ << " B/s and " << statistics -> getBitrateInLongFrame()
+ << " B/s in " << control -> ShortBitrateTimeFrame / 1000
+ << "/" << control -> LongBitrateTimeFrame / 1000
+ << " seconds timeframes.\n" << logofs_flush;
+ #endif
+
+ //
+ // This can be improved. We may not jump out
+ // of the select often enough to guarantee
+ // the necessary accuracy.
+ //
+
+ if (control -> LocalBitrateLimit > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Calculating bandwidth usage with limit "
+ << control -> LocalBitrateLimit << ".\n"
+ << logofs_flush;
+ #endif
+
+ int reference = (statistics -> getBitrateInLongFrame() +
+ statistics -> getBitrateInShortFrame()) / 2;
+
+ if (reference > control -> LocalBitrateLimit)
+ {
+ double ratio = ((double) reference) /
+ ((double) control -> LocalBitrateLimit);
+
+ if (ratio > 1.2)
+ {
+ ratio = 1.2;
+ }
+
+ slept += (unsigned int) (pow(50000, ratio) / 1000);
+
+ if (slept > 2000)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Sleeping due to "
+ << "reference bitrate of " << reference
+ << " B/s.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Sleeping due to "
+ << "reference bitrate of " << reference
+ << " B/s.\n";
+
+ slept %= 2000;
+ }
+
+ T_timestamp idleTs = getNewTimestamp();
+
+ usleep((unsigned int) pow(50000, ratio));
+
+ int diffTs = diffTimestamp(idleTs, getNewTimestamp());
+
+ statistics -> addIdleTime(diffTs);
+
+ statistics -> subReadTime(diffTs);
+ }
+ }
+}
+
+#if defined(TEST) || defined(INFO)
+
+static void handleCheckStateInLoop(int &setFDs)
+{
+ int fdLength;
+ int fdPending;
+ int fdSplits;
+
+ for (int j = 0; j < setFDs; j++)
+ {
+ if (j != proxyFD)
+ {
+ fdPending = proxy -> getPending(j);
+
+ if (fdPending > 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Buffer for descriptor FD#"
+ << j << " has pending bytes to read.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ fdLength = proxy -> getLength(j);
+
+ if (fdLength > 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Buffer for descriptor FD#"
+ << j << " has " << fdLength << " bytes to write.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+ }
+
+ fdPending = proxy -> getPending(proxyFD);
+
+ if (fdPending > 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Buffer for proxy descriptor FD#"
+ << proxyFD << " has pending bytes to read.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ fdLength = proxy -> getFlushable(proxyFD);
+
+ if (fdLength > 0)
+ {
+ if (control -> FlushPolicy == policy_immediate &&
+ proxy -> getBlocked(proxyFD) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Buffer for proxy descriptor FD#"
+ << proxyFD << " has " << fdLength << " bytes "
+ << "to write with policy 'immediate'.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Loop: WARNING! Buffer for proxy descriptor FD#"
+ << proxyFD << " has " << fdLength << " bytes "
+ << "to write.\n" << logofs_flush;
+ #endif
+ }
+ }
+
+ fdSplits = proxy -> getSplitSize();
+
+ if (fdSplits > 0)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! Proxy descriptor FD#" << proxyFD
+ << " has " << fdSplits << " splits to send.\n"
+ << logofs_flush;
+ #endif
+ }
+}
+
+static void handleCheckSelectInLoop(int &setFDs, fd_set &readSet,
+ fd_set &writeSet, T_timestamp selectTs)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Maximum descriptors is ["
+ << setFDs << "] at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ int i;
+
+ if (setFDs > 0)
+ {
+ i = 0;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Selected for read are ";
+ #endif
+
+ for (int j = 0; j < setFDs; j++)
+ {
+ if (FD_ISSET(j, &readSet))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[" << j << "]" << logofs_flush;
+ #endif
+
+ i++;
+ }
+ }
+
+ if (i > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[none].\n" << logofs_flush;
+ #endif
+ }
+
+ i = 0;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Selected for write are ";
+ #endif
+
+ for (int j = 0; j < setFDs; j++)
+ {
+ if (FD_ISSET(j, &writeSet))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[" << j << "]" << logofs_flush;
+ #endif
+
+ i++;
+ }
+ }
+
+ if (i > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[none].\n" << logofs_flush;
+ #endif
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Select timeout is "
+ << selectTs.tv_sec << " S and "
+ << (double) selectTs.tv_usec / 1000
+ << " Ms.\n" << logofs_flush;
+ #endif
+}
+
+static void handleCheckResultInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs,
+ struct timeval &startTs)
+{
+ int diffTs = diffTimestamp(startTs, getNewTimestamp());
+
+ #if defined(TEST) || defined(INFO)
+
+ if (diffTs >= (control -> PingTimeout -
+ (control -> LatencyTimeout * 4)))
+ {
+ *logofs << "Loop: Select result is [" << resultFDs
+ << "] at " << strMsTimestamp() << " with no "
+ << "communication within " << diffTs
+ << " Ms.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Loop: Select result is [" << resultFDs
+ << "] error is [" << errorFDs << "] at "
+ << strMsTimestamp() << " after " << diffTs
+ << " Ms.\n" << logofs_flush;
+ }
+
+ #endif
+
+ int i;
+
+ if (resultFDs > 0)
+ {
+ i = 0;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Selected for read are ";
+ #endif
+
+ for (int j = 0; j < setFDs; j++)
+ {
+ if (FD_ISSET(j, &readSet))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[" << j << "]" << logofs_flush;
+ #endif
+
+ i++;
+ }
+ }
+
+ if (i > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[none].\n" << logofs_flush;
+ #endif
+ }
+
+ i = 0;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Selected for write are ";
+ #endif
+
+ for (int j = 0; j < setFDs; j++)
+ {
+ if (FD_ISSET(j, &writeSet))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[" << j << "]" << logofs_flush;
+ #endif
+
+ i++;
+ }
+ }
+
+ if (i > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "[none].\n" << logofs_flush;
+ #endif
+ }
+ }
+}
+
+#endif
+
+static void handleCheckSessionInConnect()
+{
+ #ifdef TEST
+ *logofs << "Loop: Going to check session in connect.\n"
+ << logofs_flush;
+ #endif
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ HandleAlert(FAILED_PROXY_CONNECTION_CLIENT_ALERT, 1);
+ }
+ else if (IsNotRunning(lastDialog))
+ {
+ HandleAlert(FAILED_PROXY_CONNECTION_SERVER_ALERT, 1);
+ }
+
+ handleAlertInLoop();
+}
+
+static void handleStatisticsInLoop()
+{
+ if (lastSignal == 0)
+ {
+ return;
+ }
+
+ int mode = NO_STATS;
+
+ if (control -> EnableStatistics == 1)
+ {
+ if (lastSignal == SIGUSR1)
+ {
+ //
+ // Print overall statistics.
+ //
+
+ mode = TOTAL_STATS;
+ }
+ else if (lastSignal == SIGUSR2)
+ {
+ //
+ // Print partial statistics.
+ //
+
+ mode = PARTIAL_STATS;
+ }
+
+ if (mode == TOTAL_STATS || mode == PARTIAL_STATS)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to request proxy statistics "
+ << "with signal '" << DumpSignal(lastSignal)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (proxy != NULL)
+ {
+ if (ReopenLogFile(statsFileName, statofs, 0) < 0)
+ {
+ HandleCleanup();
+ }
+
+ proxy -> handleStatistics(mode, statofs);
+ }
+ }
+ }
+}
+
+static void handleNegotiationInLoop(int &setFDs, fd_set &readSet,
+ fd_set &writeSet, T_timestamp &selectTs)
+{
+ int yield = 0;
+
+ while (yield == 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Going to run a new negotiation loop "
+ << "with stage " << control -> ProxyStage
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ switch (control -> ProxyStage)
+ {
+ case stage_undefined:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_undefined" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ control -> ProxyStage = stage_initializing;
+
+ break;
+ }
+ case stage_initializing:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_initializing" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ InitBeforeNegotiation();
+
+ control -> ProxyStage = stage_connecting;
+
+ break;
+ }
+ case stage_connecting:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_connecting" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ SetupProxyConnection();
+
+ control -> ProxyStage = stage_connected;
+
+ break;
+ }
+ case stage_connected:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_connected" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Server side proxy must always be the one that
+ // sends its version and options first, so, in
+ // some way, client side can be the the one that
+ // has the last word on the matter.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ //
+ // Check if we have been listening for a
+ // forwarder. In this case it will have to
+ // authenticate itself.
+ //
+
+ if (WE_LISTEN_FORWARDER)
+ {
+ control -> ProxyStage = stage_waiting_forwarder_version;
+
+ break;
+ }
+
+ control -> ProxyStage = stage_sending_proxy_options;
+ }
+ else
+ {
+ //
+ // The X client side is the side that has to wait
+ // for the authorization cookie and any remote
+ // option.
+ //
+
+ control -> ProxyStage = stage_waiting_proxy_version;
+ }
+
+ break;
+ }
+ case stage_sending_proxy_options:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_sending_proxy_options" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (SendProxyOptions(proxyFD) < 0)
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ control -> ProxyStage = stage_waiting_proxy_version;
+ }
+ else
+ {
+ control -> ProxyStage = stage_sending_proxy_caches;
+ }
+
+ break;
+ }
+ case stage_waiting_forwarder_version:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_waiting_forwarder_version" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int result = ReadForwarderVersion(proxyFD);
+
+ if (result == 0)
+ {
+ yield = 1;
+ }
+ else if (result == 1)
+ {
+ control -> ProxyStage = stage_waiting_forwarder_options;
+ }
+ else
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ break;
+ }
+ case stage_waiting_forwarder_options:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_waiting_forwarder_options" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int result = ReadForwarderOptions(proxyFD);
+
+ if (result == 0)
+ {
+ yield = 1;
+ }
+ else if (result == 1)
+ {
+ control -> ProxyStage = stage_sending_proxy_options;
+ }
+ else
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ break;
+ }
+ case stage_waiting_proxy_version:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_waiting_proxy_version" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int result = ReadProxyVersion(proxyFD);
+
+ if (result == 0)
+ {
+ yield = 1;
+ }
+ else if (result == 1)
+ {
+ control -> ProxyStage = stage_waiting_proxy_options;
+ }
+ else
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ break;
+ }
+ case stage_waiting_proxy_options:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_waiting_proxy_options" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int result = ReadProxyOptions(proxyFD);
+
+ if (result == 0)
+ {
+ yield = 1;
+ }
+ else if (result == 1)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ control -> ProxyStage = stage_waiting_proxy_caches;
+ }
+ else
+ {
+ control -> ProxyStage = stage_sending_proxy_options;
+ }
+ }
+ else
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ break;
+ }
+ case stage_sending_proxy_caches:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_sending_proxy_caches" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ if (SendProxyCaches(proxyFD) < 0)
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ control -> ProxyStage = stage_operational;
+ }
+ else
+ {
+ control -> ProxyStage = stage_waiting_proxy_caches;
+ }
+
+ break;
+ }
+ case stage_waiting_proxy_caches:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_waiting_proxy_caches" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ int result = ReadProxyCaches(proxyFD);
+
+ if (result == 0)
+ {
+ yield = 1;
+ }
+ else if (result == 1)
+ {
+ if (control -> ProxyMode == proxy_server)
+ {
+ control -> ProxyStage = stage_sending_proxy_caches;
+ }
+ else
+ {
+ control -> ProxyStage = stage_operational;
+ }
+ }
+ else
+ {
+ goto handleNegotiationInLoopError;
+ }
+
+ break;
+ }
+ case stage_operational:
+ {
+ #ifdef TEST
+ *logofs << "Loop: Handling negotiation with '"
+ << "stage_operational" << "'.\n"
+ << logofs_flush;
+ #endif
+
+ InitAfterNegotiation();
+
+ yield = 1;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Unmanaged case '" << control -> ProxyStage
+ << "' while handling negotiation.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unmanaged case '" << control -> ProxyStage
+ << "' while handling negotiation.\n";
+
+ HandleCleanup();
+ }
+ }
+ }
+
+ //
+ // Check if the user requested the end of
+ // the session.
+ //
+
+ if (CheckAbort() != 0)
+ {
+ HandleCleanup();
+ }
+
+ //
+ // Select the proxy descriptor so that we
+ // can proceed negotiating the session.
+ //
+
+ FD_SET(proxyFD, &readSet);
+
+ if (proxyFD >= setFDs)
+ {
+ setFDs = proxyFD + 1;
+ }
+
+ setMinTimestamp(selectTs, control -> PingTimeout);
+
+ #ifdef TEST
+ *logofs << "Loop: Selected proxy FD#" << proxyFD << " in negotiation "
+ << "phase with timeout of " << selectTs.tv_sec << " S and "
+ << selectTs.tv_usec << " Ms.\n" << logofs_flush;
+ #endif
+
+ return;
+
+handleNegotiationInLoopError:
+
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Failure negotiating the session in stage '"
+ << control -> ProxyStage << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure negotiating the session in stage '"
+ << control -> ProxyStage << "'.\n";
+
+
+ if (control -> ProxyMode == proxy_server &&
+ control -> ProxyStage == stage_waiting_proxy_version)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Wrong version or invalid session "
+ << "authentication cookie.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Wrong version or invalid session "
+ << "authentication cookie.\n";
+ }
+
+ handleTerminatingInLoop();
+
+ HandleCleanup();
+}
+
+static void handleTerminatingInLoop()
+{
+ if (getpid() == lastProxy)
+ {
+ if (control -> ProxyStage < stage_terminating)
+ {
+ if (agent == NULL)
+ {
+ cerr << "Session" << ": Terminating session at '"
+ << strTimestamp() << "'.\n";
+ }
+
+ control -> ProxyStage = stage_terminating;
+ }
+ }
+}
+
+static void handleTerminatedInLoop()
+{
+ if (getpid() == lastProxy)
+ {
+ if (control -> ProxyStage < stage_terminated)
+ {
+ if (agent == NULL)
+ {
+ cerr << "Session" << ": Session terminated at '"
+ << strTimestamp() << "'.\n";
+ }
+
+ control -> ProxyStage = stage_terminated;
+ }
+ }
+}
+
+static void handleAlertInLoop()
+{
+ if (lastAlert.code == 0)
+ {
+ return;
+ }
+
+ //
+ // Since ProtoStep7 (#issue 108)
+ //
+ // Now the remote proxy should always
+ // be able to handle the alert
+ //
+
+ if (lastAlert.local == 0)
+ {
+ if (proxy != NULL)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Requesting a remote alert with code '"
+ << lastAlert.code << "'.\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAlert(lastAlert.code) < 0)
+ {
+ HandleShutdown();
+ }
+ }
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Handling a local alert with code '"
+ << lastAlert.code << "'.\n" << logofs_flush;
+ #endif
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ //
+ // If we are at X client side and server
+ // proxy is not responding, we don't have
+ // any possibility to interact with user.
+ //
+
+ if (lastAlert.code != CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT &&
+ lastAlert.code != RESTART_DEAD_PROXY_CONNECTION_CLIENT_ALERT &&
+ lastAlert.code != FAILED_PROXY_CONNECTION_CLIENT_ALERT)
+ {
+ //
+ // Let the server proxy show the dialog.
+ //
+
+ if (proxy != NULL &&
+ proxy -> handleAlert(lastAlert.code) < 0)
+ {
+ HandleShutdown();
+ }
+ }
+ }
+ else
+ {
+ char caption[DEFAULT_STRING_LENGTH];
+
+ strcpy(caption, ALERT_CAPTION_PREFIX);
+
+ int length = strlen(sessionId);
+
+ //
+ // Get rid of the trailing MD5 from session id.
+ //
+
+ if (length > (MD5_LENGTH * 2 + 1) &&
+ *(sessionId + (length - (MD5_LENGTH * 2 + 1))) == '-')
+ {
+ strncat(caption, sessionId, length - (MD5_LENGTH * 2 + 1));
+ }
+ else
+ {
+ strcat(caption, sessionId);
+ }
+
+ //
+ // Use the display to which we are forwarding
+ // the remote X connections.
+ //
+
+ char *display = displayHost;
+
+ int replace = 1;
+ int local = 1;
+
+ const char *message;
+ const char *type;
+
+ switch (lastAlert.code)
+ {
+ case CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT:
+ {
+ message = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_STRING;
+ type = CLOSE_DEAD_X_CONNECTION_CLIENT_ALERT_TYPE;
+
+ break;
+ }
+ case CLOSE_DEAD_X_CONNECTION_SERVER_ALERT:
+ {
+ message = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_STRING;
+ type = CLOSE_DEAD_X_CONNECTION_SERVER_ALERT_TYPE;
+
+ break;
+ }
+ case CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT:
+ {
+ message = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING;
+ type = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE;
+
+ break;
+ }
+ case RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT:
+ {
+ message = RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_STRING;
+ type = RESTART_DEAD_PROXY_CONNECTION_SERVER_ALERT_TYPE;
+
+ break;
+ }
+ case CLOSE_UNRESPONSIVE_X_SERVER_ALERT:
+ {
+ message = CLOSE_UNRESPONSIVE_X_SERVER_ALERT_STRING;
+ type = CLOSE_UNRESPONSIVE_X_SERVER_ALERT_TYPE;
+
+ break;
+ }
+ case WRONG_PROXY_VERSION_ALERT:
+ {
+ message = WRONG_PROXY_VERSION_ALERT_STRING;
+ type = WRONG_PROXY_VERSION_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_PROXY_CONNECTION_SERVER_ALERT:
+ {
+ message = FAILED_PROXY_CONNECTION_SERVER_ALERT_STRING;
+ type = FAILED_PROXY_CONNECTION_SERVER_ALERT_TYPE;
+
+ break;
+ }
+ case MISSING_PROXY_CACHE_ALERT:
+ {
+ message = MISSING_PROXY_CACHE_ALERT_STRING;
+ type = MISSING_PROXY_CACHE_ALERT_TYPE;
+
+ break;
+ }
+ case ABORT_PROXY_CONNECTION_ALERT:
+ {
+ message = ABORT_PROXY_CONNECTION_ALERT_STRING;
+ type = ABORT_PROXY_CONNECTION_ALERT_TYPE;
+
+ break;
+ }
+ case DISPLACE_MESSAGE_ALERT:
+ {
+ message = DISPLACE_MESSAGE_ALERT_STRING;
+ type = DISPLACE_MESSAGE_ALERT_TYPE;
+
+ break;
+ }
+ case GREETING_MESSAGE_ALERT:
+ {
+ message = GREETING_MESSAGE_ALERT_STRING;
+ type = GREETING_MESSAGE_ALERT_TYPE;
+
+ break;
+ }
+ case START_RESUME_SESSION_ALERT:
+ {
+ message = START_RESUME_SESSION_ALERT_STRING;
+ type = START_RESUME_SESSION_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_DISPLAY_ALERT:
+ {
+ message = FAILED_RESUME_DISPLAY_ALERT_STRING;
+ type = FAILED_RESUME_DISPLAY_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_DISPLAY_BROKEN_ALERT:
+ {
+ message = FAILED_RESUME_DISPLAY_BROKEN_STRING;
+ type = FAILED_RESUME_DISPLAY_BROKEN_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_VISUALS_ALERT:
+ {
+ message = FAILED_RESUME_VISUALS_ALERT_STRING;
+ type = FAILED_RESUME_VISUALS_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_COLORMAPS_ALERT:
+ {
+ message = FAILED_RESUME_COLORMAPS_ALERT_STRING;
+ type = FAILED_RESUME_COLORMAPS_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_PIXMAPS_ALERT:
+ {
+ message = FAILED_RESUME_PIXMAPS_ALERT_STRING;
+ type = FAILED_RESUME_PIXMAPS_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_DEPTHS_ALERT:
+ {
+ message = FAILED_RESUME_DEPTHS_ALERT_STRING;
+ type = FAILED_RESUME_DEPTHS_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_RENDER_ALERT:
+ {
+ message = FAILED_RESUME_RENDER_ALERT_STRING;
+ type = FAILED_RESUME_RENDER_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_RESUME_FONTS_ALERT:
+ {
+ message = FAILED_RESUME_FONTS_ALERT_STRING;
+ type = FAILED_RESUME_FONTS_ALERT_TYPE;
+
+ break;
+ }
+ case INTERNAL_ERROR_ALERT:
+ {
+ message = INTERNAL_ERROR_ALERT_STRING;
+ type = INTERNAL_ERROR_ALERT_TYPE;
+
+ break;
+ }
+ case ABORT_PROXY_NEGOTIATION_ALERT:
+ {
+ message = ABORT_PROXY_NEGOTIATION_ALERT_STRING;
+ type = ABORT_PROXY_NEGOTIATION_ALERT_TYPE;
+
+ break;
+ }
+ case ABORT_PROXY_SHUTDOWN_ALERT:
+ {
+ message = ABORT_PROXY_SHUTDOWN_ALERT_STRING;
+ type = ABORT_PROXY_SHUTDOWN_ALERT_TYPE;
+
+ break;
+ }
+ case FAILED_XDMCP_CONNECTION_ALERT:
+ {
+ message = FAILED_XDMCP_CONNECTION_ALERT_STRING;
+ type = FAILED_XDMCP_CONNECTION_ALERT_TYPE;
+
+ break;
+ }
+ default:
+ {
+ if (lastAlert.code > LAST_PROTO_STEP_7_ALERT)
+ {
+ #ifdef WARNING
+ *logofs << "Loop: WARNING! An unrecognized alert type '"
+ << lastAlert.code << "' was requested.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": An unrecognized alert type '"
+ << lastAlert.code << "' was requested.\n";
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Loop: WARNING! Ignoring obsolete alert type '"
+ << lastAlert.code << "'.\n" << logofs_flush;
+ }
+ #endif
+
+ message = NULL;
+ type = NULL;
+
+ replace = 0;
+
+ break;
+ }
+ }
+
+ if (replace == 1 && IsRunning(lastDialog))
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Killing the previous dialog with pid '"
+ << lastDialog << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // The client ignores the TERM signal
+ // on Windows.
+ //
+
+ #ifdef __CYGWIN32__
+
+ KillProcess(lastDialog, "dialog", SIGKILL, 0);
+
+ #else
+
+ KillProcess(lastDialog, "dialog", SIGTERM, 0);
+
+ #endif
+
+ SetNotRunning(lastDialog);
+
+ if (proxy != NULL)
+ {
+ proxy -> handleResetAlert();
+ }
+ }
+
+ if (message != NULL && type != NULL)
+ {
+ lastDialog = NXTransDialog(caption, message, 0, type, local, display);
+
+ if (IsFailed(lastDialog))
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Can't start the NX dialog process.\n"
+ << logofs_flush;
+ #endif
+
+ SetNotRunning(lastDialog);
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "Loop: Dialog started with pid '"
+ << lastDialog << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "Loop: No new dialog required for code '"
+ << lastAlert.code << "'.\n" << logofs_flush;
+ }
+ #endif
+ }
+ }
+
+ //
+ // Reset state.
+ //
+
+ lastAlert.code = 0;
+ lastAlert.local = 0;
+}
+
+static inline void handleSetAgentInLoop(int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs)
+{
+ #ifdef TEST
+ *logofs << "Loop: Preparing the masks for the agent descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ agent -> saveChannelState();
+
+ agent -> saveReadMask(&readSet);
+ agent -> saveWriteMask(&writeSet);
+
+ if (control -> ProxyStage >= stage_operational)
+ {
+ if (agent -> remoteCanRead(&readSet) ||
+ agent -> remoteCanWrite(&writeSet) ||
+ agent -> localCanRead() ||
+ agent -> proxyCanRead())
+ {
+ #ifdef TEST
+ *logofs << "Loop: Setting a null timeout with agent descriptors ready.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Force a null timeout so we'll bail out
+ // of the select immediately. We will ac-
+ // comodate the result code later.
+ //
+
+ selectTs.tv_sec = 0;
+ selectTs.tv_usec = 0;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Loop: Clearing the read and write agent descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ agent -> clearReadMask(&readSet);
+ agent -> clearWriteMask(&writeSet);
+}
+
+static inline void handleAgentInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting proxy and local agent descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if I/O is possible on the local
+ // agent or the proxy descriptor.
+ //
+
+ if (resultFDs >= 0)
+ {
+ //
+ // Save if the proxy can read from the
+ // the agent descriptor.
+ //
+
+ agent -> saveChannelState();
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Values were resultFDs " << resultFDs
+ << " errorFDs " << errorFDs << " setFDs "
+ << setFDs << ".\n" << logofs_flush;
+ #endif
+
+ if (agent -> localCanRead() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting agent descriptor FD#" << agent ->
+ getLocalFd() << " as ready to read.\n"
+ << logofs_flush;
+ #endif
+
+ agent -> setLocalRead(&readSet, &resultFDs);
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (agent -> proxyCanRead(&readSet) == 0 &&
+ agent -> proxyCanRead() == 1)
+ {
+ *logofs << "Loop: WARNING! Can read from proxy FD#"
+ << proxyFD << " but the descriptor "
+ << "is not selected.\n" << logofs_flush;
+ }
+
+ if (agent -> proxyCanRead(&readSet) == 1)
+ {
+ *logofs << "Loop: Setting proxy descriptor FD#" << agent ->
+ getProxyFd() << " as ready to read.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Values are now resultFDs " << resultFDs
+ << " errorFDs " << errorFDs << " setFDs "
+ << setFDs << ".\n" << logofs_flush;
+ #endif
+ }
+}
+
+static inline void handleAgentLateInLoop(int &resultFDs, int &errorFDs, int &setFDs, fd_set &readSet,
+ fd_set &writeSet, struct timeval &selectTs)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting remote agent descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // We reset the masks before calling our select.
+ // We now set the descriptors that are ready but
+ // only if they were set in the original mask.
+ // We do this after having executed our loop as
+ // we may have produced more data and the agent
+ // descriptors may have become readable or writ-
+ // able in the meanwhile.
+ //
+
+ if (resultFDs >= 0)
+ {
+ //
+ // Save if the proxy can read from the
+ // the agent descriptor.
+ //
+
+ agent -> saveChannelState();
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Values were resultFDs " << resultFDs
+ << " errorFDs " << errorFDs << " setFDs "
+ << setFDs << ".\n" << logofs_flush;
+ #endif
+
+ if (agent -> remoteCanRead(agent ->
+ getSavedReadMask()) == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting agent descriptor FD#" << agent ->
+ getRemoteFd() << " as ready to read.\n"
+ << logofs_flush;
+ #endif
+
+ agent -> setRemoteRead(&readSet, &resultFDs);
+ }
+
+ if (agent -> remoteCanWrite(agent ->
+ getSavedWriteMask()) == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Setting agent descriptor FD#" << agent ->
+ getRemoteFd() << " as ready to write.\n"
+ << logofs_flush;
+ #endif
+
+ agent -> setRemoteWrite(&writeSet, &resultFDs);
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Loop: Values are now resultFDs " << resultFDs
+ << " errorFDs " << errorFDs << " setFDs "
+ << setFDs << ".\n" << logofs_flush;
+ #endif
+ }
+}
+
+static inline void handleReadableInLoop(int &resultFDs, fd_set &readSet)
+{
+ if (resultFDs > 0)
+ {
+ T_channel_type type = channel_none;
+
+ const char *label = NULL;
+ int domain = -1;
+ int fd = -1;
+
+ if (tcpFD != -1 && FD_ISSET(tcpFD, &readSet))
+ {
+ type = channel_x11;
+ label = "X";
+ domain = AF_INET;
+ fd = tcpFD;
+
+ resultFDs--;
+ }
+
+ if (unixFD != -1 && FD_ISSET(unixFD, &readSet))
+ {
+ type = channel_x11;
+ label = "X";
+ domain = AF_UNIX;
+ fd = unixFD;
+
+ resultFDs--;
+ }
+
+ if (cupsFD != -1 && FD_ISSET(cupsFD, &readSet))
+ {
+ type = channel_cups;
+ label = "CUPS";
+ domain = AF_INET;
+ fd = cupsFD;
+
+ resultFDs--;
+ }
+
+ if (auxFD != -1 && FD_ISSET(auxFD, &readSet))
+ {
+ //
+ // Starting from version 1.5.0 we create real X
+ // connections for the keyboard channel, so they
+ // can use the fake authorization cookie. This
+ // means that there is not such a thing like a
+ // channel_aux anymore.
+ //
+
+ type = channel_x11;
+ label = "auxiliary X11";
+ domain = AF_INET;
+ fd = auxFD;
+
+ resultFDs--;
+ }
+
+ if (smbFD != -1 && FD_ISSET(smbFD, &readSet))
+ {
+ type = channel_smb;
+ label = "SMB";
+ domain = AF_INET;
+ fd = smbFD;
+
+ resultFDs--;
+ }
+
+ if (mediaFD != -1 && FD_ISSET(mediaFD, &readSet))
+ {
+ type = channel_media;
+ label = "media";
+ domain = AF_INET;
+ fd = mediaFD;
+
+ resultFDs--;
+ }
+
+ if (httpFD != -1 && FD_ISSET(httpFD, &readSet))
+ {
+ type = channel_http;
+ label = "HTTP";
+ domain = AF_INET;
+ fd = httpFD;
+
+ resultFDs--;
+ }
+
+ if (fontFD != -1 && FD_ISSET(fontFD, &readSet))
+ {
+ type = channel_font;
+ label = "font server";
+ domain = AF_INET;
+ fd = fontFD;
+
+ resultFDs--;
+ }
+
+ if (slaveFD != -1 && FD_ISSET(slaveFD, &readSet))
+ {
+ type = channel_slave;
+ label = "slave";
+ domain = AF_INET;
+ fd = slaveFD;
+
+ resultFDs--;
+ }
+
+ if (type != channel_none)
+ {
+ int newFD = AcceptConnection(fd, domain, label);
+
+ if (newFD != -1)
+ {
+ if (proxy -> handleNewConnection(type, newFD) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Loop: PANIC! Error creating new " << label
+ << " connection.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error creating new " << label
+ << " connection.\n";
+
+ close(newFD);
+
+ //
+ // Don't kill the proxy in the case of an error.
+ //
+ // HandleCleanup();
+ //
+ }
+ else if (proxy -> getReadable(newFD) > 0)
+ {
+ //
+ // Add the descriptor, so we can try
+ // to read immediately.
+ //
+
+ #ifdef TEST
+ *logofs << "Loop: Trying to read immediately "
+ << "from descriptor FD#" << newFD
+ << ".\n" << logofs_flush;
+ #endif
+
+ FD_SET(newFD, &readSet);
+
+ resultFDs++;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Loop: Nothing to read immediately "
+ << "from descriptor FD#" << newFD
+ << ".\n" << logofs_flush;
+ }
+ #endif
+ }
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Going to check the readable descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleRead(resultFDs, readSet) < 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Failure reading from descriptors "
+ << "for proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+}
+
+static inline void handleWritableInLoop(int &resultFDs, fd_set &writeSet)
+{
+ #ifdef DEBUG
+ *logofs << "Loop: Going to check the writable descriptors.\n"
+ << logofs_flush;
+ #endif
+
+ if (resultFDs > 0 && proxy -> handleFlush(resultFDs, writeSet) < 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Failure writing to descriptors "
+ << "for proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+}
+
+static inline void handleFlushInLoop()
+{
+ #ifdef DEBUG
+ *logofs << "Loop: Going to flush any data to the proxy.\n"
+ << logofs_flush;
+ #endif
+
+ if (agent == NULL || control ->
+ FlushPolicy == policy_immediate)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (usePolicy == -1 && control ->
+ ProxyMode == proxy_client)
+ {
+ *logofs << "Loop: WARNING! Flushing the proxy link "
+ << "on behalf of the agent.\n" << logofs_flush;
+ }
+
+ #endif
+
+ if (proxy -> handleFlush() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Failure flushing the proxy FD#"
+ << proxyFD << ".\n" << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+ }
+}
+
+static inline void handleRotateInLoop()
+{
+ #ifdef DEBUG
+ *logofs << "Loop: Going to rotate channels "
+ << "for proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ proxy -> handleRotate();
+}
+
+static inline void handleEventsInLoop()
+{
+ #ifdef DEBUG
+ *logofs << "Loop: Going to check channel events "
+ << "for proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleEvents() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Loop: Failure handling channel events "
+ << "for proxy FD#" << proxyFD << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleShutdown();
+ }
+}
+
+static void handleLogReopenInLoop(T_timestamp &lTs, T_timestamp &nTs)
+{
+ //
+ // If need to limit the size of the
+ // log file, check the size at each
+ // loop.
+ //
+
+ #ifndef QUOTA
+
+ if (diffTimestamp(lTs, nTs) > control -> FileSizeCheckTimeout)
+
+ #endif
+ {
+ #ifdef DEBUG
+ *logofs << "Loop: Checking size of log file '"
+ << errorsFileName << "'.\n" << logofs_flush;
+ #endif
+
+ #ifndef MIXED
+
+ if (ReopenLogFile(errorsFileName, logofs, control -> FileSizeLimit) < 0)
+ {
+ HandleShutdown();
+ }
+
+ #endif
+
+ //
+ // Reset to current timestamp.
+ //
+
+ lTs = nTs;
+ }
+}
+
+static inline void handleSetReadInLoop(fd_set &readSet, int &setFDs, struct timeval &selectTs)
+{
+ proxy -> setReadDescriptors(&readSet, setFDs, selectTs);
+}
+
+static inline void handleSetWriteInLoop(fd_set &writeSet, int &setFDs, struct timeval &selectTs)
+{
+ proxy -> setWriteDescriptors(&writeSet, setFDs, selectTs);
+}
+
+static void handleSetListenersInLoop(fd_set &readSet, int &setFDs)
+{
+ //
+ // Set descriptors of listening sockets.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (useTcpSocket == 1)
+ {
+ FD_SET(tcpFD, &readSet);
+
+ if (tcpFD >= setFDs)
+ {
+ setFDs = tcpFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener tcpFD " << tcpFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useUnixSocket == 1)
+ {
+ FD_SET(unixFD, &readSet);
+
+ if (unixFD >= setFDs)
+ {
+ setFDs = unixFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener unixFD " << unixFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useCupsSocket == 1)
+ {
+ FD_SET(cupsFD, &readSet);
+
+ if (cupsFD >= setFDs)
+ {
+ setFDs = cupsFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener cupsFD " << cupsFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useAuxSocket == 1)
+ {
+ FD_SET(auxFD, &readSet);
+
+ if (auxFD >= setFDs)
+ {
+ setFDs = auxFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener auxFD " << auxFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useSmbSocket == 1)
+ {
+ FD_SET(smbFD, &readSet);
+
+ if (smbFD >= setFDs)
+ {
+ setFDs = smbFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener smbFD " << smbFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useMediaSocket == 1)
+ {
+ FD_SET(mediaFD, &readSet);
+
+ if (mediaFD >= setFDs)
+ {
+ setFDs = mediaFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener mediaFD " << mediaFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ if (useHttpSocket == 1)
+ {
+ FD_SET(httpFD, &readSet);
+
+ if (httpFD >= setFDs)
+ {
+ setFDs = httpFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener httpFD " << httpFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+ else
+ {
+ if (useFontSocket == 1)
+ {
+ FD_SET(fontFD, &readSet);
+
+ if (fontFD >= setFDs)
+ {
+ setFDs = fontFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener fontFD " << fontFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+ if (useSlaveSocket == 1)
+ {
+ FD_SET(slaveFD, &readSet);
+
+ if (slaveFD >= setFDs)
+ {
+ setFDs = slaveFD + 1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Loop: Selected listener slaveFD " << slaveFD
+ << " with setFDs " << setFDs << ".\n"
+ << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/src/MD5.c b/nxcomp/src/MD5.c
new file mode 100644
index 000000000..7255ca5c0
--- /dev/null
+++ b/nxcomp/src/MD5.c
@@ -0,0 +1,403 @@
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "MD5.h"
+
+#include <string.h>
+
+/*
+ * Try to determine the CPU endianess
+ * at compile time.
+ */
+
+#if defined(__linux) || defined(__CYGWIN32__)
+
+#include <endian.h>
+
+#if (__BYTE_ORDER == __LITTLE_ENDIAN)
+#define ARCH_IS_BIG_ENDIAN 0
+#else
+#define ARCH_IS_BIG_ENDIAN 1
+#endif
+
+#endif /* #if defined(__linux) || defined(__CYGWIN32__) */
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/nxcomp/src/Makefile.am b/nxcomp/src/Makefile.am
new file mode 100644
index 000000000..2264cb347
--- /dev/null
+++ b/nxcomp/src/Makefile.am
@@ -0,0 +1,146 @@
+NULL =
+
+lib_LTLIBRARIES = libXcomp.la
+
+libXcomp_la_SOURCES = \
+ ActionCache.cpp \
+ Agent.cpp \
+ Alpha.cpp \
+ Auth.cpp \
+ Bitmap.cpp \
+ BlockCache.cpp \
+ BlockCacheSet.cpp \
+ ChangeGC.cpp \
+ ChangeProperty.cpp \
+ ChannelCache.cpp \
+ Channel.cpp \
+ ChannelEndPoint.cpp \
+ CharCache.cpp \
+ Children.cpp \
+ ClearArea.cpp \
+ ClientCache.cpp \
+ ClientChannel.cpp \
+ ClientProxy.cpp \
+ ClientReadBuffer.cpp \
+ ClientStore.cpp \
+ Colormap.cpp \
+ ConfigureWindow.cpp \
+ Control.cpp \
+ CopyArea.cpp \
+ CreateGC.cpp \
+ CreatePixmap.cpp \
+ DecodeBuffer.cpp \
+ EncodeBuffer.cpp \
+ FillPoly.cpp \
+ Fork.cpp \
+ GenericChannel.cpp \
+ GenericReadBuffer.cpp \
+ GenericReply.cpp \
+ GenericRequest.cpp \
+ GetImage.cpp \
+ GetImageReply.cpp \
+ GetProperty.cpp \
+ GetPropertyReply.cpp \
+ ImageText16.cpp \
+ ImageText8.cpp \
+ IntCache.cpp \
+ InternAtom.cpp \
+ Jpeg.cpp \
+ Keeper.cpp \
+ List.cpp \
+ ListFontsReply.cpp \
+ Loop.cpp \
+ Message.cpp \
+ MD5.c \
+ Misc.cpp \
+ OpcodeStore.cpp \
+ Pack.c \
+ Pgn.cpp \
+ Pipe.cpp \
+ PolyArc.cpp \
+ PolyFillArc.cpp \
+ PolyFillRectangle.cpp \
+ PolyLine.cpp \
+ PolyPoint.cpp \
+ PolySegment.cpp \
+ PolyText16.cpp \
+ PolyText8.cpp \
+ Proxy.cpp \
+ ProxyReadBuffer.cpp \
+ PutImage.cpp \
+ PutPackedImage.cpp \
+ QueryFontReply.cpp \
+ ReadBuffer.cpp \
+ RenderAddGlyphs.cpp \
+ RenderChangePicture.cpp \
+ RenderComposite.cpp \
+ RenderCompositeGlyphs.cpp \
+ RenderCreateGlyphSet.cpp \
+ RenderCreatePicture.cpp \
+ RenderExtension.cpp \
+ RenderFillRectangles.cpp \
+ RenderFreeGlyphSet.cpp \
+ RenderFreePicture.cpp \
+ RenderGenericRequest.cpp \
+ RenderPictureClip.cpp \
+ RenderPictureFilter.cpp \
+ RenderPictureTransform.cpp \
+ RenderTrapezoids.cpp \
+ RenderTriangles.cpp \
+ Rgb.cpp \
+ Rle.cpp \
+ SendEvent.cpp \
+ SequenceQueue.cpp \
+ ServerCache.cpp \
+ ServerChannel.cpp \
+ ServerProxy.cpp \
+ ServerReadBuffer.cpp \
+ ServerStore.cpp \
+ SetClipRectangles.cpp \
+ SetUnpackAlpha.cpp \
+ SetUnpackColormap.cpp \
+ SetUnpackGeometry.cpp \
+ ShapeExtension.cpp \
+ Socket.cpp \
+ Split.cpp \
+ StaticCompressor.cpp \
+ Statistics.cpp \
+ Timestamp.cpp \
+ TranslateCoords.cpp \
+ Transport.cpp \
+ Unpack.cpp \
+ Vars.c \
+ Version.c \
+ WriteBuffer.cpp \
+ XidCache.cpp \
+ Z.cpp \
+ $(NULL)
+
+libXcomp_la_LIBADD = \
+ @JPEG_LIBS@ \
+ @PNG_LIBS@ \
+ @Z_LIBS@ \
+ $(NULL)
+
+AM_CXXFLAGS = \
+ $(BASE_CXXFLAGS) \
+ $(JPEG_CFLAGS) \
+ $(PNG_CFLAGS) \
+ $(Z_CFLAGS) \
+ $(NULL)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/include \
+ $(NULL)
+
+libXcomp_la_LDFLAGS = -version-number @LT_COMP_VERSION@ -no-undefined
+
+libXcompincludedir = $(includedir)/nx
+libXcompinclude_HEADERS = \
+ $(top_srcdir)/include/MD5.h \
+ $(top_srcdir)/include/NX.h \
+ $(top_srcdir)/include/NXalert.h \
+ $(top_srcdir)/include/NXpack.h \
+ $(top_srcdir)/include/NXproto.h \
+ $(top_srcdir)/include/NXvars.h \
+ $(NULL)
diff --git a/nxcomp/src/Message.cpp b/nxcomp/src/Message.cpp
new file mode 100644
index 000000000..b75d90c24
--- /dev/null
+++ b/nxcomp/src/Message.cpp
@@ -0,0 +1,2343 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cstdio>
+#include <unistd.h>
+#include <cstring>
+
+#include <algorithm>
+
+#include "Misc.h"
+
+//
+// We need channel's cache data.
+//
+
+#include "Message.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level. You also
+// need to define DUMP in Misc.cpp
+// if DUMP is defined here.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Define this to log when messages
+// are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// Keep track of how many bytes are
+// occupied by cache.
+//
+
+int MessageStore::totalLocalStorageSize_ = 0;
+int MessageStore::totalRemoteStorageSize_ = 0;
+
+//
+// These are used for reference count.
+//
+
+#ifdef REFERENCES
+
+int Message::references_ = 0;
+int MessageStore::references_ = 0;
+
+#endif
+
+//
+// Here are the methods to handle cached messages.
+//
+
+MessageStore::MessageStore(StaticCompressor *compressor)
+
+ : compressor_(compressor)
+{
+ //
+ // Public members.
+ //
+
+ enableCache = MESSAGE_ENABLE_CACHE;
+ enableData = MESSAGE_ENABLE_DATA;
+ enableSplit = MESSAGE_ENABLE_SPLIT;
+ enableCompress = MESSAGE_ENABLE_COMPRESS;
+
+ dataLimit = MESSAGE_DATA_LIMIT;
+ dataOffset = MESSAGE_DATA_OFFSET;
+
+ cacheSlots = MESSAGE_CACHE_SLOTS;
+ cacheThreshold = MESSAGE_CACHE_THRESHOLD;
+ cacheLowerThreshold = MESSAGE_CACHE_LOWER_THRESHOLD;
+
+ #ifdef TEST
+ *logofs << "MessageStore: Static compressor is at "
+ << compressor_ << ".\n" << logofs_flush;
+ #endif
+
+ md5_state_ = new md5_state_t();
+
+ #ifdef DEBUG
+ *logofs << "MessageStore: Created MD5 state for object at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ lastAdded = cacheSlots;
+ lastHit = 0;
+ lastRemoved = 0;
+ lastRated = nothing;
+ lastAction = is_discarded;
+
+ //
+ // Private members.
+ //
+
+ localStorageSize_ = 0;
+ remoteStorageSize_ = 0;
+
+ #ifdef TEST
+ *logofs << "MessageStore: Size of total cache is "
+ << totalLocalStorageSize_ << " bytes at local side and "
+ << totalRemoteStorageSize_ << " bytes at remote side.\n"
+ << logofs_flush;
+ #endif
+
+ messages_ = new T_messages();
+ checksums_ = new T_checksums();
+
+ temporary_ = NULL;
+
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "MessageStore: Created new store at "
+ << this << "out of " << references_
+ << " allocated stores.\n" << logofs_flush;
+
+ #endif
+}
+
+MessageStore::~MessageStore()
+{
+ //
+ // The virtual destructor of specialized class
+ // must get rid of both messages in container
+ // and temporary.
+ //
+
+ #ifdef DEBUG
+ *logofs << "MessageStore: Deleting MD5 state for object at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ delete md5_state_;
+
+ delete messages_;
+ delete checksums_;
+
+ //
+ // Update the static members tracking
+ // size of total memory allocated for
+ // all stores.
+ //
+
+ totalLocalStorageSize_ -= localStorageSize_;
+ totalRemoteStorageSize_ -= remoteStorageSize_;
+
+ #ifdef TEST
+ *logofs << "MessageStore: Size of total cache is "
+ << totalLocalStorageSize_ << " bytes at local side and "
+ << totalRemoteStorageSize_ << " bytes at remote side.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef REFERENCES
+
+ references_--;
+
+ *logofs << "MessageStore: Deleted store at "
+ << this << " out of " << references_
+ << " allocated stores.\n" << logofs_flush;
+
+ #endif
+}
+
+//
+// Here are the methods to parse and cache
+// messages in the message stores.
+//
+
+int MessageStore::parse(Message *message, int split, const unsigned char *buffer,
+ unsigned int size, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian)
+{
+ //
+ // Save the message size as received on the link.
+ // This information will be used to create an ap-
+ // propriate buffer at the time the message will
+ // be unparsed.
+ //
+
+ message -> size_ = size;
+ message -> i_size_ = identitySize(buffer, size);
+ message -> c_size_ = 0;
+
+ validateSize(size);
+
+ if (checksumAction == use_checksum)
+ {
+ beginChecksum(message);
+
+ parseIdentity(message, buffer, size, bigEndian);
+
+ identityChecksum(message, buffer, size, bigEndian);
+
+ parseData(message, split, buffer, size, checksumAction, dataAction, bigEndian);
+
+ endChecksum(message);
+ }
+ else
+ {
+ parseIdentity(message, buffer, size, bigEndian);
+
+ parseData(message, split, buffer, size, checksumAction, dataAction, bigEndian);
+ }
+
+ return 1;
+}
+
+int MessageStore::parse(Message *message, const unsigned char *buffer,
+ unsigned int size, const unsigned char *compressedData,
+ const unsigned int compressedDataSize,
+ T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian)
+{
+ int offset = identitySize(buffer, size);
+
+ message -> size_ = size;
+ message -> i_size_ = offset;
+ message -> c_size_ = compressedDataSize + offset;
+
+ validateSize(message -> size_ - offset, compressedDataSize);
+
+ if (checksumAction == use_checksum)
+ {
+ beginChecksum(message);
+
+ parseIdentity(message, buffer, size, bigEndian);
+
+ identityChecksum(message, buffer, size, bigEndian);
+
+ parseData(message, buffer, size, compressedData, compressedDataSize,
+ checksumAction, dataAction, bigEndian);
+
+ endChecksum(message);
+ }
+ else
+ {
+ parseIdentity(message, buffer, size, bigEndian);
+
+ parseData(message, buffer, size, compressedData, compressedDataSize,
+ checksumAction, dataAction, bigEndian);
+ }
+
+ return 1;
+}
+
+int MessageStore::parseData(Message *message, int split, const unsigned char *buffer,
+ unsigned int size, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian)
+{
+ if ((int) size > message -> i_size_)
+ {
+ unsigned int dataSize = size - message -> i_size_;
+
+ if (checksumAction == use_checksum)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Calculating checksum of object at "
+ << message << " with data size " << dataSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ dataChecksum(message, buffer, size, bigEndian);
+ }
+
+ if (dataAction == discard_data)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Discarded " << dataSize
+ << " bytes of plain data. Real size is "
+ << message -> size_ << " compressed size is "
+ << message -> c_size_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ //
+ // Accept anyway data beyond the
+ // expected limit.
+ //
+
+ #ifdef TEST
+
+ if (dataSize > (unsigned int) dataLimit)
+ {
+ *logofs << name() << ": WARNING! Data is " << dataSize
+ << " bytes. Ignoring the established limit.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ if (dataSize != message -> data_.size())
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Data will be resized from "
+ << message -> data_.size() << " to hold a plain buffer of "
+ << dataSize << " bytes.\n" << logofs_flush;
+ #endif
+
+ message -> data_.clear();
+
+ message -> data_.resize(dataSize);
+ }
+
+ if (split == 0)
+ {
+ memcpy(message -> data_.begin(), buffer + message -> i_size_, dataSize);
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << name() << ": Not copied " << dataSize
+ << " bytes of fake data for the split message.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed " << dataSize
+ << " bytes of plain data. Real size is "
+ << message -> size_ << " compressed size is "
+ << message -> c_size_ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+//
+// Store the data part in compressed format.
+//
+
+int MessageStore::parseData(Message *message, const unsigned char *buffer,
+ unsigned int size, const unsigned char *compressedData,
+ const unsigned int compressedDataSize,
+ T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian)
+{
+ if ((int) size > message -> i_size_)
+ {
+ unsigned int dataSize = size - message -> i_size_;
+
+ if (checksumAction == use_checksum)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Calculating checksum of object at "
+ << message << " with data size " << dataSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ dataChecksum(message, buffer, size, bigEndian);
+ }
+
+ if (dataAction == discard_data)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Discarded " << dataSize
+ << " bytes of compressed data. Real size is "
+ << message -> size_ << " compressed size is "
+ << message -> c_size_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ #ifdef WARNING
+ if (dataSize > (unsigned int) dataLimit)
+ {
+ *logofs << name() << ": WARNING! Data is " << dataSize
+ << " bytes. Ignoring the established limit!\n"
+ << logofs_flush;
+ }
+ #endif
+
+ dataSize = compressedDataSize;
+
+ if (dataSize != message -> data_.size())
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Data will be resized from "
+ << message -> data_.size() << " to hold a compressed buffer of "
+ << dataSize << " bytes.\n" << logofs_flush;
+ #endif
+
+ message -> data_.clear();
+
+ message -> data_.resize(compressedDataSize);
+ }
+
+ memcpy(message -> data_.begin(), compressedData, compressedDataSize);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed " << dataSize
+ << " bytes of compressed data. Real size is "
+ << message -> size_ << " compressed size is "
+ << message -> c_size_ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+int MessageStore::unparseData(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian)
+{
+ //
+ // Copy data, if any, to the buffer.
+ //
+
+ if ((int) size > message -> i_size_)
+ {
+ //
+ // Check if message has been stored
+ // in compressed format.
+ //
+
+ if (message -> c_size_ == 0)
+ {
+ memcpy(buffer + message -> i_size_, message -> data_.begin(), size - message -> i_size_);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed " << message -> size_ - message -> i_size_
+ << " bytes of data to a buffer of " << message -> size_ - message -> i_size_
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Using static compressor at " << (void *) compressor_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (compressor_ ->
+ decompressBuffer(buffer + message -> i_size_,
+ size - message -> i_size_,
+ message -> data_.begin(),
+ message -> c_size_ - message -> i_size_) < 0)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Data decompression failed.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Data decompression failed.\n";
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed " << message -> c_size_ - message -> i_size_
+ << " bytes of compressed data to a buffer of "
+ << message -> size_ - message -> i_size_ << ".\n" << logofs_flush;
+ #endif
+ }
+ }
+
+ //
+ // We could write size to the buffer but this
+ // is something the channel class is doing by
+ // itself.
+ //
+ // PutUINT(size >> 2, buffer + 2, bigEndian);
+ //
+
+ return 1;
+}
+
+void MessageStore::dumpData(const Message *message) const
+{
+ #ifdef DUMP
+
+ *logofs << name() << ": Dumping enumerated data:\n" << logofs_flush;
+
+ DumpData(message -> data_.begin(), message -> data_.size());
+
+ #endif
+
+ #ifdef DUMP
+
+ *logofs << name() << ": Dumping checksum data:\n" << logofs_flush;
+
+ DumpData(message -> md5_digest_, MD5_LENGTH);
+
+ #endif
+}
+
+T_checksum MessageStore::getChecksum(const unsigned char *buffer,
+ unsigned int size, int bigEndian)
+{
+ Message *message = getTemporary();
+
+ message -> size_ = size;
+ message -> i_size_ = identitySize(buffer, size);
+ message -> c_size_ = 0;
+
+ validateSize(size);
+
+ beginChecksum(message);
+
+ //
+ // We don't need to extract the identity
+ // data from the buffer.
+ //
+ // parseIdentity(message, buffer, size, bigEndian);
+ //
+
+ identityChecksum(message, buffer, size, bigEndian);
+
+ parseData(message, 0, buffer, size, use_checksum, discard_data, bigEndian);
+
+ endChecksum(message);
+
+ //
+ // The caller will have to explicitly
+ // deallocated the memory after use.
+ //
+
+ T_checksum checksum = new md5_byte_t[MD5_LENGTH];
+
+ memcpy(checksum, message -> md5_digest_, MD5_LENGTH);
+
+ return checksum;
+}
+
+int MessageStore::clean(T_checksum_action checksumAction)
+{
+ int position = lastRemoved + 1;
+
+ if (position >= cacheSlots)
+ {
+ position = 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Searching a message to remove "
+ << "starting at position " << position
+ << " with " << checksums_ -> size()
+ << " elements in cache.\n"
+ << logofs_flush;
+ #endif
+
+ while (position != lastRemoved)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Examining position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ if ((*messages_)[position] != NULL)
+ {
+ if (getRating((*messages_)[position], rating_for_clean) == 0)
+ {
+ break;
+ }
+ else
+ {
+ untouch((*messages_)[position]);
+ }
+ }
+
+ if (++position == cacheSlots)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Rolled position at "
+ << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ position = 0;
+ }
+ }
+
+ //
+ // If no message is a good candidate,
+ // then try the object at the next slot
+ // in respect to last element removed.
+ //
+
+ if (position == lastRemoved)
+ {
+ position = lastRemoved + 1;
+
+ if (position >= cacheSlots)
+ {
+ position = 0;
+ }
+
+ if ((*messages_)[position] == NULL ||
+ (*messages_)[position] -> locks_ != 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": WARNING! No message found "
+ << "to be actually removed.\n"
+ << logofs_flush;
+ #endif
+
+ return nothing;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": WARNING! Assuming object "
+ << "at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ return position;
+}
+
+//
+// This is the insertion method used at local side
+// side. Cache at remote side side will be kept in
+// sync by telling the to other party where to
+// store the message.
+//
+
+int MessageStore::findOrAdd(Message *message, T_checksum_action checksumAction,
+ T_data_action dataAction, int &added, int &locked)
+{
+ if (checksumAction != use_checksum)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Internal error in context [A]. "
+ << "Cannot find or add message to repository "
+ << "without using checksum.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Internal error in context [A]. "
+ << "Cannot find or add message to repository "
+ << "without using checksum.\n";
+
+ HandleAbort();
+ }
+
+ //
+ // Set added to true only if message
+ // is inserted in cache.
+ //
+
+ added = 0;
+ locked = 0;
+
+ //
+ // First of all figure out where to
+ // store this object.
+ //
+
+ #ifdef DEBUG
+ *logofs << name() << ": Searching an empty slot "
+ << "with last rated " << lastRated << " and "
+ << "last added " << lastAdded << ".\n"
+ << logofs_flush;
+ #endif
+
+ int position = lastRated;
+
+ if (position == nothing)
+ {
+ position = lastAdded + 1;
+
+ if (position >= cacheSlots)
+ {
+ position = 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Searching an empty slot "
+ << "starting at position " << position
+ << " with " << checksums_ -> size()
+ << " elements in cache.\n"
+ << logofs_flush;
+ #endif
+
+ while (position != lastAdded)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Examining position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ if ((*messages_)[position] == NULL)
+ {
+ break;
+ }
+ else if (getRating((*messages_)[position], rating_for_insert) == 0)
+ {
+ break;
+ }
+ else
+ {
+ untouch((*messages_)[position]);
+ }
+
+ if (++position == cacheSlots)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Rolled position at "
+ << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ position = 0;
+ }
+ }
+ }
+ #ifdef DEBUG
+ else
+ {
+ *logofs << name() << ": Using last rated position "
+ << position << ".\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // If we made an extensive check but did not
+ // find neither a free slot or a message to
+ // replace, assume slot at next position in
+ // respect to last added. This can happen if
+ // all objects in repository have got an hit
+ // recently.
+ //
+
+ if (position == lastAdded)
+ {
+ position = lastAdded + 1;
+
+ if (position >= cacheSlots)
+ {
+ position = 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": WARNING! Assuming slot "
+ << "at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ #ifdef DEBUG
+ else
+ {
+ *logofs << name() << ": Found candidate slot "
+ << "at position " << position << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // Save the search result so if the message
+ // is found in cache, we can use the slot
+ // at next run.
+ //
+
+ lastRated = position;
+
+ if ((*messages_)[position] != NULL &&
+ (*messages_)[position] -> locks_ != 0)
+ {
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Insertion at position "
+ << position << " would replace a locked message. "
+ << "Forcing channel to discard the message.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << name() << ": Invalidating rating of object "
+ << "at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+
+ return (lastRated = nothing);
+ }
+
+ if (checksumAction == use_checksum)
+ {
+ T_checksum checksum = getChecksum(message);
+
+ #ifdef TEST
+ *logofs << name() << ": Searching checksum ["
+ << DumpChecksum(checksum) << "] in repository.\n"
+ << logofs_flush;
+
+ #endif
+
+ pair<T_checksums::iterator, bool> result;
+
+ result = checksums_ -> insert(T_checksums::value_type(checksum, position));
+
+ //
+ // Message was found in cache or
+ // insertion couldn't take place.
+ //
+
+ if (result.second == 0)
+ {
+ if (result.first == checksums_ -> end())
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Failed to insert object "
+ << "in the cache.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to insert object of type "
+ << name() << " in the cache.\n";
+
+ return nothing;
+ }
+
+ //
+ // Message is in cache.
+ //
+
+ #ifdef TEST
+ *logofs << name() << ": Object is already in cache "
+ << "at position " << (result.first) -> second
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+
+ //
+ // Message is locked, probably because
+ // it has not completely recomposed at
+ // remote side after a split.
+ //
+
+ if ((*messages_)[(result.first) -> second] -> locks_ != 0)
+ {
+ #ifdef TEST
+ *logofs << name() << ": WARNING! Object at position "
+ << (result.first) -> second << " is locked.\n"
+ << logofs_flush;
+ #endif
+
+ locked = 1;
+ }
+
+ //
+ // Object got a hit, so prevent
+ // its removal.
+ //
+
+ if (lastRated == (result.first) -> second)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Resetting rating of object "
+ << "at position " << (result.first) -> second
+ << ".\n" << logofs_flush;
+ #endif
+
+ lastRated = nothing;
+ }
+
+ return (result.first) -> second;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Could not find message in cache.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // Message not found in hash table (or insertion
+ // of checksum in hash table was not requested).
+ // Message was added to cache.
+ //
+
+ added = 1;
+
+ //
+ // Log data about the missed message.
+ //
+
+ #ifdef TEST
+
+ if (opcode() == X_PutImage || opcode() == X_NXPutPackedImage)
+ {
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Dumping identity of "
+ << "missed image object of type " << name()
+ << ".\n" << logofs_flush;
+ #endif
+
+ dumpIdentity(message);
+ }
+
+ #endif
+
+ if ((*messages_)[position] != NULL)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": The message replaces "
+ << "the old one at position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ remove(position, checksumAction, dataAction);
+ }
+
+ (*messages_)[position] = message;
+
+ //
+ // We used the slot. Perform a new
+ // search at next run.
+ //
+
+ lastRated = nothing;
+
+ #ifdef TEST
+ *logofs << name() << ": Stored message object of size "
+ << plainSize(position) << " (" << message -> size_
+ << "/" << message -> c_size_ << ") at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ unsigned int localSize;
+ unsigned int remoteSize;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ += localSize;
+ remoteStorageSize_ += remoteSize;
+
+ totalLocalStorageSize_ += localSize;
+ totalRemoteStorageSize_ += remoteSize;
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+
+ //
+ // Set hits and timestamp at insertion in cache.
+ //
+
+ message -> hits_ = control -> StoreHitsAddBonus;
+ message -> last_ = (getTimestamp()).tv_sec;
+
+ message -> locks_ = 0;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Set last hit of object at "
+ << strMsTimestamp() << " with a bonus of "
+ << message -> hits_ << ".\n" << logofs_flush;
+ #endif
+
+ return position;
+}
+
+//
+// Add a parsed message to repository. It is normally used
+// at decoding side or at encoding side when we load store
+// from disk. To handle messages coming from network, the
+// encoding side uses the optimized method findOrAdd().
+//
+
+int MessageStore::add(Message *message, const int position,
+ T_checksum_action checksumAction, T_data_action dataAction)
+{
+ if (position < 0 || position >= cacheSlots)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Cannot add a message "
+ << "at non existing position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot add a message "
+ << "at non existing position " << position
+ << ".\n";
+
+ HandleAbort();
+ }
+
+ if ((*messages_)[position] != NULL)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": The message will replace "
+ << "the old one at position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ remove(position, checksumAction, dataAction);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Inserting object in repository at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ (*messages_)[position] = message;
+
+ //
+ // Get the object's checksum value
+ // and insert it in the table.
+ //
+
+ if (checksumAction == use_checksum)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Inserting object's checksum in repository.\n";
+ #endif
+
+ T_checksum checksum = getChecksum(message);
+
+ checksums_ -> insert(T_checksums::value_type(checksum, position));
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Stored message object of size "
+ << plainSize(position) << " (" << message -> size_
+ << "/" << message -> c_size_ << ") at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ unsigned int localSize;
+ unsigned int remoteSize;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ += localSize;
+ remoteStorageSize_ += remoteSize;
+
+ totalLocalStorageSize_ += localSize;
+ totalRemoteStorageSize_ += remoteSize;
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+
+ //
+ // Set hits and timestamp at insertion in cache.
+ //
+
+ message -> hits_ = control -> StoreHitsAddBonus;
+ message -> last_ = (getTimestamp()).tv_sec;
+
+ message -> locks_ = 0;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Set last hit of object at "
+ << strMsTimestamp() << " with a bonus of "
+ << message -> hits_ << ".\n" << logofs_flush;
+ #endif
+
+ return position;
+}
+
+//
+// The following functions don't modify data,
+// so they are supposed to be called only at
+// the encoding side.
+//
+
+void MessageStore::updateData(const int position, unsigned int dataSize,
+ unsigned int compressedDataSize)
+{
+ Message *message = (*messages_)[position];
+
+ validateSize(dataSize, compressedDataSize);
+
+ if (compressedDataSize != 0)
+ {
+ unsigned int localSize;
+ unsigned int remoteSize;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ -= localSize;
+ remoteStorageSize_ -= remoteSize;
+
+ totalLocalStorageSize_ -= localSize;
+ totalRemoteStorageSize_ -= remoteSize;
+
+ message -> c_size_ = compressedDataSize + message -> i_size_;
+
+ #ifdef TEST
+
+ if (message -> size_ != (int) (dataSize + message -> i_size_))
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Size of object looks "
+ << message -> size_ << " bytes while it "
+ << "should be " << dataSize + message -> i_size_
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size of object looks "
+ << message -> size_ << " bytes while it "
+ << "should be " << dataSize + message -> i_size_
+ << ".\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ += localSize;
+ remoteStorageSize_ += remoteSize;
+
+ totalLocalStorageSize_ += localSize;
+ totalRemoteStorageSize_ += remoteSize;
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+ }
+}
+
+void MessageStore::updateData(const T_checksum checksum, unsigned int compressedDataSize)
+{
+ #ifdef TEST
+ *logofs << name() << ": Searching checksum ["
+ << DumpChecksum(checksum) << "] in repository.\n"
+ << logofs_flush;
+ #endif
+
+ T_checksums::iterator found = checksums_ -> find(checksum);
+
+ if (found != checksums_ -> end())
+ {
+ Message *message = (*messages_)[found -> second];
+
+ #ifdef TEST
+ *logofs << name() << ": Message found in cache at "
+ << "position " << found -> second << " with size "
+ << message -> size_ << " and compressed size "
+ << message -> c_size_ << ".\n" << logofs_flush;
+ #endif
+
+ updateData(found -> second, message -> size_ -
+ message -> i_size_, compressedDataSize);
+ }
+ #ifdef TEST
+ else if (checksums_ -> size() > 0)
+ {
+ *logofs << name() << ": WARNING! Can't locate the "
+ << "checksum [" << DumpChecksum(checksum)
+ << "] for the update.\n" << logofs_flush;
+ }
+ #endif
+}
+
+//
+// This function replaces the data part of the message
+// and updates the information about its size. Split
+// messages are advertised to the decoding side with
+// their uncompressed size, data is then compressed
+// before sending the first chunk. This function is
+// called by the decoding side after the split message
+// is fully recomposed to replace the dummy data and
+// set the real size.
+//
+
+void MessageStore::updateData(const int position, const unsigned char *newData,
+ unsigned int dataSize, unsigned int compressedDataSize)
+{
+ Message *message = (*messages_)[position];
+
+ validateSize(dataSize, compressedDataSize);
+
+ #ifdef TEST
+
+ if (message -> size_ != (int) (dataSize + message -> i_size_))
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Data of object looks "
+ << dataSize << " bytes while it " << "should be "
+ << message -> size_ - message -> i_size_
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Data of object looks "
+ << dataSize << " bytes while it " << "should be "
+ << message -> size_ - message -> i_size_
+ << ".\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ //
+ // A compressed data size of 0 means that
+ // message's data was not compressed.
+ //
+
+ if (compressedDataSize != 0)
+ {
+ unsigned int localSize;
+ unsigned int remoteSize;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ -= localSize;
+ remoteStorageSize_ -= remoteSize;
+
+ totalLocalStorageSize_ -= localSize;
+ totalRemoteStorageSize_ -= remoteSize;
+
+ if (message -> c_size_ != (int) compressedDataSize +
+ message -> i_size_)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Resizing data of message at "
+ << "position " << position << " from " << message ->
+ c_size_ << " to " << compressedDataSize +
+ message -> i_size_ << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ message -> data_.clear();
+
+ message -> data_.resize(compressedDataSize);
+ }
+
+ memcpy(message -> data_.begin(), newData, compressedDataSize);
+
+ #ifdef TEST
+ *logofs << name() << ": Data of message at position "
+ << position << " has size " << message -> data_.size()
+ << " and capacity " << message -> data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+
+ message -> c_size_ = compressedDataSize + message -> i_size_;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ += localSize;
+ remoteStorageSize_ += remoteSize;
+
+ totalLocalStorageSize_ += localSize;
+ totalRemoteStorageSize_ += remoteSize;
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << name() << ": No changes to data size for message "
+ << "at position " << position << ".\n" << logofs_flush;
+ #endif
+
+ memcpy(message -> data_.begin(), newData, dataSize);
+ }
+}
+
+int MessageStore::remove(const int position, T_checksum_action checksumAction,
+ T_data_action dataAction)
+{
+ Message *message;
+
+ if (position < 0 || position >= cacheSlots ||
+ (message = (*messages_)[position]) == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Cannot remove "
+ << "a non existing message at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot remove "
+ << "a non existing message at position "
+ << position << ".\n";
+
+ HandleAbort();
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (opcode() == X_PutImage || opcode() == X_NXPutPackedImage)
+ {
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Discarding image object "
+ << "of type " << name() << " at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+ }
+
+ #endif
+
+ //
+ // The checksum is only stored at the encoding
+ // side.
+ //
+
+ if (checksumAction == use_checksum)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Removing checksum for object at "
+ << "position " << position << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // TODO: If we had stored the iterator and
+ // not the pointer to the message, we could
+ // have removed the message without having
+ // to look up the checksum.
+ //
+
+ T_checksum checksum = getChecksum(message);
+
+ #ifdef TEST
+ *logofs << name() << ": Searching checksum ["
+ << DumpChecksum(checksum) << "] in repository.\n"
+ << logofs_flush;
+ #endif
+
+ T_checksums::iterator found = checksums_ -> find(checksum);
+
+ if (found == checksums_ -> end())
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! No checksum found for "
+ << "object at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No checksum found for "
+ << "object at position " << position << ".\n";
+
+ HandleAbort();
+ }
+
+ #ifdef TEST
+
+ else if (position != found -> second)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Value of position for object "
+ << "doesn't match position " << position << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Value of position for object "
+ << "doesn't match position " << position << ".\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ checksums_ -> erase(found);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Removing message at position "
+ << position << " of size " << plainSize(position)
+ << " (" << message -> size_ << "/" << message -> c_size_
+ << ").\n" << logofs_flush;
+ #endif
+
+ unsigned int localSize;
+ unsigned int remoteSize;
+
+ storageSize(message, localSize, remoteSize);
+
+ localStorageSize_ -= localSize;
+ remoteStorageSize_ -= remoteSize;
+
+ totalLocalStorageSize_ -= localSize;
+ totalRemoteStorageSize_ -= remoteSize;
+
+ recycle(message);
+
+ (*messages_)[position] = NULL;
+
+ #ifdef DEBUG
+
+ printStorageSize();
+
+ #endif
+
+ return position;
+}
+
+//
+// This should only be called at encoding side.
+// The decoding side can't rely on the counter
+// as it is decremented by the encoding side
+// every time the repository is searched for a
+// message to be removed.
+//
+
+int MessageStore::getRating(Message *message, T_rating type) const
+{
+ if (message -> locks_ != 0)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Rate set to -1 as locks of object are "
+ << (int) message -> locks_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ else if ((type == rating_for_clean ||
+ (int) checksums_ -> size() == cacheSlots) &&
+ message -> hits_ <= control -> StoreHitsLoadBonus)
+ {
+ //
+ // We don't have any free slot or we exceeded the
+ // available storage size. This is likely to happen
+ // after having loaded objects from persistent cache.
+ // It's not a bad idea to discard some messages that
+ // were restored but never referenced.
+ //
+
+ #ifdef TEST
+
+ if (type == rating_for_clean)
+ {
+ *logofs << name() << ": Rate set to 0 with hits "
+ << message -> hits_ << " as maximum storage size "
+ << "was exceeded.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << name() << ": Rate set to 0 with hits "
+ << message -> hits_ << " as there are no available "
+ << "slots in store.\n" << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+ else if (type == rating_for_clean &&
+ (getTimestamp()).tv_sec - message -> last_ >=
+ control -> StoreTimeLimit)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Rate set to 0 as last hit of object was "
+ << (getTimestamp()).tv_sec - message -> last_
+ << " seconds ago with limit set to " << control ->
+ StoreTimeLimit << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef TEST
+ if (message -> hits_ < 0)
+ {
+ *logofs << name() << ": PANIC! Rate of object shouldn't be "
+ << message -> hits_ << ".\n" << logofs_flush;
+
+ cerr << "Error" << ": Rate of object of type " << name()
+ << " shouldn't be " << message -> hits_ << ".\n";
+
+ HandleAbort();
+ }
+ #endif
+
+ #ifdef TEST
+ *logofs << name() << ": Rate of object is " << message -> hits_
+ << " with last hit " << (getTimestamp()).tv_sec -
+ message -> last_ << " seconds ago.\n"
+ << logofs_flush;
+ #endif
+
+ return message -> hits_;
+ }
+}
+
+int MessageStore::touch(Message *message) const
+{
+ message -> last_ = (getTimestamp()).tv_sec;
+
+ message -> hits_ += control -> StoreHitsTouch;
+
+ if (message -> hits_ > control -> StoreHitsLimit)
+ {
+ message -> hits_ = control -> StoreHitsLimit;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Increased hits of object to "
+ << message -> hits_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ return message -> hits_;
+}
+
+int MessageStore::untouch(Message *message) const
+{
+ message -> hits_ -= control -> StoreHitsUntouch;
+
+ if (message -> hits_ < 0)
+ {
+ message -> hits_ = 0;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decreased hits of object to "
+ << message -> hits_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return message -> hits_;
+}
+
+int MessageStore::lock(const int position) const
+{
+ Message *message = (*messages_)[position];
+
+ if (message == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't lock the null "
+ << "object at position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Increasing locks of object to "
+ << (int) message -> locks_ + 1 << ".\n"
+ << logofs_flush;
+ #endif
+
+ return ++(message -> locks_);
+}
+
+int MessageStore::unlock(const int position) const
+{
+ Message *message = (*messages_)[position];
+
+ if (message == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't unlock the null "
+ << "object at position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decreasing locks of object to "
+ << (int) message -> locks_ - 1 << ".\n"
+ << logofs_flush;
+ #endif
+
+ return --(message -> locks_);
+}
+
+int MessageStore::saveStore(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian)
+{
+ Message *message;
+
+ #ifdef TEST
+ *logofs << name() << ": Opcode of this store is "
+ << (unsigned int) opcode() << " default size of "
+ << "identity is " << dataOffset << ".\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char *identityBuffer = new unsigned char[dataOffset];
+ unsigned char *sizeBuffer = new unsigned char[4 * 2];
+ unsigned char *positionBuffer = new unsigned char[4];
+ unsigned char *opcodeBuffer = new unsigned char[4];
+
+ #ifdef DUMP
+
+ char *md5ClientDump = new char[dataOffset * 2 + 128];
+
+ #endif
+
+ unsigned char value;
+
+ int offset;
+
+ int failed = 0;
+
+ for (int position = 0; position < cacheSlots; position++)
+ {
+ message = (*messages_)[position];
+
+ //
+ // Don't save split messages.
+ //
+
+ if (message != NULL && message -> locks_ == 0)
+ {
+ //
+ // Use the total size if offset is
+ // beyond the real end of message.
+ //
+
+ offset = dataOffset;
+
+ if (offset > message -> size_)
+ {
+ offset = message -> size_;
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Going to save message at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ value = 1;
+
+ PutULONG(position, positionBuffer, bigEndian);
+ PutULONG(opcode(), opcodeBuffer, bigEndian);
+
+ md5_append(md5StateClient, positionBuffer, 4);
+ md5_append(md5StateClient, opcodeBuffer, 4);
+
+ #ifdef DUMP
+
+ *logofs << "Name=" << name() << logofs_flush;
+
+ sprintf(md5ClientDump," Pos=%d Op=%d\n", position, opcode());
+
+ *logofs << md5ClientDump << logofs_flush;
+
+ #endif
+
+ if (PutData(cachefs, &value, 1) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << 1
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, &value, 1);
+
+ PutULONG(message -> size_, sizeBuffer, bigEndian);
+ PutULONG(message -> c_size_, sizeBuffer + 4, bigEndian);
+
+ //
+ // Note that the identity size is not saved with
+ // the message and will be determined from the
+ // data read when restoring the identity.
+ //
+
+ if (PutData(cachefs, sizeBuffer, 4 * 2) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << 4 * 2
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, sizeBuffer, 4 * 2);
+ md5_append(md5StateClient, sizeBuffer, 4 * 2);
+
+ #ifdef DUMP
+
+ sprintf(md5ClientDump, "size = %d c_size = %d\n",
+ message -> size_, message -> c_size_);
+
+ *logofs << md5ClientDump << logofs_flush;
+
+ #endif
+
+ //
+ // Prepare a clean buffer for unparse.
+ //
+
+ CleanData(identityBuffer, offset);
+
+ unparseIdentity(message, identityBuffer, offset, bigEndian);
+
+ if (PutData(cachefs, identityBuffer, offset) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << offset
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, identityBuffer, offset);
+ md5_append(md5StateClient, identityBuffer, offset);
+
+ #ifdef DUMP
+
+ for (int i = 0; i < offset; i++)
+ {
+ sprintf(md5ClientDump + (i * 2), "%02X", identityBuffer[i]);
+ }
+
+ *logofs << "Identity = " << md5ClientDump << "\n" << logofs_flush;
+
+ #endif
+
+ //
+ // Set the real identity size before
+ // saving the data.
+ //
+
+ offset = message -> i_size_;
+
+ if (offset > message -> size_)
+ {
+ offset = message -> size_;
+ }
+
+ if (checksumAction == use_checksum)
+ {
+ if (PutData(cachefs, message -> md5_digest_, MD5_LENGTH) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << MD5_LENGTH
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, message -> md5_digest_, MD5_LENGTH);
+ }
+ else if (dataAction == use_data)
+ {
+ int dataSize = (message -> c_size_ == 0 ?
+ message -> size_ - offset :
+ message -> c_size_ - offset);
+ if (dataSize > 0)
+ {
+ if (PutData(cachefs, message -> data_.begin(), dataSize) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << dataSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, message -> data_.begin(), dataSize);
+ }
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << name() << ": Not saving message at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ value = 0;
+
+ if (PutData(cachefs, &value, 1) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure writing " << 1
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, &value, 1);
+ }
+ }
+
+ if (failed == 1)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Write to persistent cache file failed.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Write to persistent cache file failed.\n";
+ }
+
+ delete [] identityBuffer;
+ delete [] sizeBuffer;
+ delete [] positionBuffer;
+ delete [] opcodeBuffer;
+
+ #ifdef DUMP
+
+ delete [] md5ClientDump;
+
+ #endif
+
+ return (failed == 0 ? 1 : -1);
+}
+
+int MessageStore::loadStore(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction,
+ int bigEndian)
+{
+ Message *message;
+
+ #ifdef TEST
+ *logofs << name() << ": Opcode of this store is "
+ << (unsigned int) opcode() << " default size of "
+ << "identity is " << dataOffset << " slots are "
+ << cacheSlots << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // If packed images or the render extension has been
+ // disabled we don't need to restore these messages
+ // in the cache. Encoding of RENDER in 1.4.0 is also
+ // changed so we want to skip messages saved using
+ // the old format. We want to restore all the other
+ // messages so we'll need to skip these one by one.
+ //
+
+ int skip = 0;
+
+ if ((opcode() == X_NXPutPackedImage &&
+ control -> PersistentCacheLoadPacked == 0) ||
+ (opcode() == X_NXInternalRenderExtension &&
+ control -> PersistentCacheLoadRender == 0))
+ {
+ #ifdef TEST
+ *logofs << name() << ": All messages for OPCODE#"
+ << (unsigned int) opcode() << " will be discarded.\n"
+ << logofs_flush;
+ #endif
+
+ skip = 1;
+ }
+
+ unsigned char *identityBuffer = new unsigned char[dataOffset];
+ unsigned char *sizeBuffer = new unsigned char[4 * 2];
+
+ unsigned char value;
+
+ int offset;
+
+ int failed = 0;
+
+ for (int position = 0; position < cacheSlots; position++)
+ {
+ if (GetData(cachefs, &value, 1) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << 1
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, &value, 1);
+
+ if (value == 1)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Going to load message at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ if (GetData(cachefs, sizeBuffer, 4 * 2) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << 4 * 2
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, sizeBuffer, 4 * 2);
+
+ message = getTemporary();
+
+ if (message == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't access temporary storage "
+ << "for message in context [B].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't access temporary storage "
+ << "for message in context [B].\n";
+
+ failed = 1;
+
+ break;
+ }
+
+ message -> size_ = GetULONG(sizeBuffer, bigEndian);
+ message -> c_size_ = GetULONG(sizeBuffer + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Size is " << message -> size_
+ << " compressed size is " << message -> c_size_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Use the total size if offset is
+ // beyond the real end of message.
+ //
+
+ offset = dataOffset;
+
+ if (offset > message -> size_)
+ {
+ offset = message -> size_;
+ }
+
+ if (GetData(cachefs, identityBuffer, offset) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << offset
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, identityBuffer, offset);
+
+ //
+ // Get the real identity size based on the value
+ // reported by the message store. The dataOffset
+ // value is guaranteed to be greater or equal to
+ // the maximum identity size of the messages in
+ // the major store.
+ //
+
+ offset = identitySize(identityBuffer, offset);
+
+ if (offset > message -> size_)
+ {
+ offset = message -> size_;
+ }
+
+ message -> i_size_ = offset;
+
+ //
+ // Get identity of message from the buffer we just
+ // created. Don't calculate neither checksum nor
+ // data, restore them from stream. Don't pass the
+ // message's size but the default size of identity.
+ //
+
+ parseIdentity(message, identityBuffer, offset, bigEndian);
+
+ if (checksumAction == use_checksum)
+ {
+ if (message -> md5_digest_ == NULL)
+ {
+ message -> md5_digest_ = new md5_byte_t[MD5_LENGTH];
+ }
+
+ if (GetData(cachefs, message -> md5_digest_, MD5_LENGTH) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << MD5_LENGTH
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ //
+ // Add message's checksum to checksum that will
+ // be saved together with this cache. Checksum
+ // will be verified when cache file is restored
+ // to ensure file is not corrupted.
+ //
+
+ md5_append(md5StateStream, message -> md5_digest_, MD5_LENGTH);
+
+ if (skip == 1)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Discarding message for OPCODE#"
+ << (unsigned int) opcode() << ".\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ }
+ else if (dataAction == use_data)
+ {
+ //
+ // Restore the data part.
+ //
+
+ int dataSize = (message -> c_size_ == 0 ?
+ message -> size_ - offset :
+ message -> c_size_ - offset);
+
+ if (dataSize < 0 || dataSize > control -> MaximumMessageSize)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Bad data size "
+ << dataSize << " loading persistent cache.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad data size " << dataSize
+ << " loading persistent cache.\n";
+
+ failed = 1;
+
+ break;
+ }
+ else if (dataSize > 0)
+ {
+ //
+ // If need to skip the message let anyway
+ // it to be part of the calculated MD5.
+ //
+
+ if (skip == 1)
+ {
+ unsigned char *dummy = new unsigned char[dataSize];
+
+ if (dummy == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't allocate dummy buffer "
+ << "of size " << dataSize << " loading cache.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate dummy buffer "
+ << "of size " << dataSize << " loading cache.\n";
+
+ failed = 1;
+
+ break;
+ }
+
+ if (GetData(cachefs, dummy, dataSize) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << dataSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ md5_append(md5StateStream, dummy, dataSize);
+
+ delete [] dummy;
+
+ #ifdef TEST
+ *logofs << name() << ": Discarding message for OPCODE#"
+ << (unsigned int) opcode() << ".\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ message -> data_.clear();
+
+ message -> data_.resize(dataSize);
+
+ if (GetData(cachefs, message -> data_.begin(), dataSize) < 0)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": PANIC! Failure reading " << dataSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ failed = 1;
+
+ break;
+ }
+
+ //
+ // Add message's data to cache checksum.
+ //
+
+ md5_append(md5StateStream, message -> data_.begin(), dataSize);
+ }
+ }
+ else
+ {
+ //
+ // We are here if data part is zero.
+ //
+
+ if (skip == 1)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Discarding message for OPCODE#"
+ << (unsigned int) opcode() << ".\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ }
+ }
+
+ int added;
+
+ added = add(message, position, checksumAction, dataAction);
+
+ if (added != position)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't store message "
+ << "in the cache at position " << position
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't store message "
+ << "in the cache at position " << position
+ << ".\n";
+
+ failed = 1;
+
+ break;
+ }
+ else
+ {
+ //
+ // Replace default value of hits set by add
+ // function. Messages read from cache start
+ // with a lower bonus than fresh messages
+ // inserted.
+ //
+
+ message -> hits_ = control -> StoreHitsLoadBonus;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Updated last hit of object at "
+ << strMsTimestamp() << " with a bonus of "
+ << message -> hits_ << ".\n" << logofs_flush;
+ #endif
+
+ resetTemporary();
+ }
+ }
+ else if ((*messages_)[position] != NULL)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Going to remove message at position "
+ << position << ".\n" << logofs_flush;
+ #endif
+
+ int removed;
+
+ removed = remove(position, checksumAction, dataAction);
+
+ if (removed != position)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Can't remove message from cache "
+ << "at position " << position << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't remove message from cache "
+ << "at position " << position << ".\n";
+
+ failed = 1;
+
+ break;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << name() << ": Not loading message at position "
+ << position << ".\n" << logofs_flush;
+ }
+ #endif
+ }
+
+ #ifdef WARNING
+
+ if (failed == 1)
+ {
+ *logofs << name() << ": WARNING! Read from persistent cache file failed.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ delete [] identityBuffer;
+ delete [] sizeBuffer;
+
+ return (failed == 0 ? 1 : -1);
+}
+
+void MessageStore::storageSize(const Message *message, unsigned int &local,
+ unsigned int &remote) const
+{
+ local = remote = storage();
+
+ //
+ // Encoding side includes 48 bytes for
+ // the map of checksums and 24 bytes
+ // of adjustment for total overhead.
+ //
+
+ local += MD5_LENGTH + 48 + 24;
+
+ //
+ // At decoding side we include size of
+ // data part and 24 bytes of adjustment
+ // for total overhead.
+ //
+
+ if (message -> c_size_ == 0)
+ {
+ remote += message -> size_ + 24;
+ }
+ else
+ {
+ remote += message -> c_size_ + 24;
+ }
+
+ //
+ // Check if we are the encoding or the
+ // decoding side and, if needed, swap
+ // the values.
+ //
+
+ if (message -> md5_digest_ == NULL)
+ {
+ unsigned int t = local;
+
+ local = remote;
+
+ remote = t;
+ }
+}
+
+void MessageStore::printStorageSize()
+{
+ #ifdef TEST
+
+ *logofs << name() << ": There are "
+ << checksums_ -> size() << " checksums in this store "
+ << "out of " << cacheSlots << " slots.\n"
+ << logofs_flush;
+
+ *logofs << name() << ": Size of this store is "
+ << localStorageSize_ << " bytes at local side and "
+ << remoteStorageSize_ << " bytes at remote side.\n"
+ << logofs_flush;
+
+ *logofs << name() << ": Size of total cache is "
+ << totalLocalStorageSize_ << " bytes at local side and "
+ << totalRemoteStorageSize_ << " bytes at remote side.\n"
+ << logofs_flush;
+
+ #endif
+}
diff --git a/nxcomp/src/Message.h b/nxcomp/src/Message.h
new file mode 100644
index 000000000..30883f101
--- /dev/null
+++ b/nxcomp/src/Message.h
@@ -0,0 +1,1089 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Message_H
+#define Message_H
+
+#include <X11/Xproto.h>
+
+#include "NXproto.h"
+
+#include "Misc.h"
+#include "Control.h"
+
+#include "Types.h"
+#include "Timestamp.h"
+
+#include "ActionCache.h"
+
+#include "StaticCompressor.h"
+
+//
+// Forward class declarations.
+//
+
+class ChannelCache;
+
+class EncodeBuffer;
+class DecodeBuffer;
+
+class WriteBuffer;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to know how many messages
+// are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// Set default values. We limit the maximum
+// size of a request to 262144 but we need to
+// consider the replies, whose size may be up
+// to 4MB.
+//
+
+#define MESSAGE_ENABLE_CACHE 0
+#define MESSAGE_ENABLE_DATA 0
+#define MESSAGE_ENABLE_SPLIT 0
+#define MESSAGE_ENABLE_COMPRESS 0
+
+#define MESSAGE_DATA_LIMIT 4194304 - 4
+#define MESSAGE_DATA_OFFSET 4
+
+#define MESSAGE_CACHE_SLOTS 6000
+#define MESSAGE_CACHE_THRESHOLD 50
+#define MESSAGE_CACHE_LOWER_THRESHOLD 5
+
+//
+// Base message class.
+//
+
+class Message
+{
+ friend class MessageStore;
+ friend class RenderExtensionStore;
+
+ public:
+
+ Message()
+ {
+ hits_ = 0;
+ last_ = 0;
+ locks_ = 0;
+
+ size_ = 0;
+ c_size_ = 0;
+
+ md5_digest_ = NULL;
+
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "Message: Created new message at "
+ << this << " out of " << references_
+ << " allocated messages.\n"
+ << logofs_flush;
+
+ #endif
+ }
+
+ Message(const Message &message)
+ {
+ size_ = message.size_;
+ c_size_ = message.c_size_;
+ i_size_ = message.i_size_;
+
+ hits_ = message.hits_;
+ last_ = message.last_;
+ locks_ = message.locks_;
+
+ data_ = message.data_;
+
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "Message: Creating new copied message at "
+ << this << " out of " << references_
+ << " allocated messages.\n"
+ << logofs_flush;
+ #endif
+
+ if (message.md5_digest_ != NULL)
+ {
+ md5_digest_ = new md5_byte_t[MD5_LENGTH];
+
+ memcpy(md5_digest_, message.md5_digest_, MD5_LENGTH);
+
+ #ifdef DEBUG
+ *logofs << "Message: Created MD5 digest for object at "
+ << this << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ md5_digest_ = NULL;
+ }
+ }
+
+ ~Message()
+ {
+ #ifdef DEBUG
+ if (md5_digest_ != NULL)
+ {
+ *logofs << "Message: Deleted MD5 digest for object at "
+ << this << ".\n" << logofs_flush;
+ }
+ #endif
+
+ delete [] md5_digest_;
+
+ #ifdef REFERENCES
+
+ references_--;
+
+ *logofs << "Message: Deleted message at "
+ << this << " out of " << references_
+ << " allocated messages.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // This is the original message size
+ // including the data part regardless
+ // data is still stored in the object.
+ //
+
+ int size_;
+
+ //
+ // This is the size of the identity.
+ //
+
+ int i_size_;
+
+ //
+ // This is the size, including identity,
+ // after message has been 'updated' to
+ // reflect storage of data in compressed
+ // format.
+ //
+
+ int c_size_;
+
+ protected:
+
+ //
+ // This is the data part.
+ //
+
+ T_data data_;
+
+ //
+ // Time of last hit.
+ //
+
+ time_t last_;
+
+ //
+ // This is the number of cache hits
+ // registered for the object.
+ //
+
+ short int hits_;
+
+ //
+ // This is used to mark messages
+ // that have been split.
+ //
+
+ short int locks_;
+
+ //
+ // This is the MD5 checksum.
+ //
+
+ md5_byte_t *md5_digest_;
+
+ //
+ // Keep a reference counter
+ // of allocated objects.
+ //
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+//
+// Repository of messages.
+//
+
+class MessageStore
+{
+ public:
+
+ //
+ // Enable or disable cache of messages in store.
+ //
+
+ int enableCache;
+
+ //
+ // Does message have a distinct data part.
+ //
+
+ int enableData;
+
+ //
+ // Enable or disable split of data part.
+ //
+
+ int enableSplit;
+
+ //
+ // Enable or disable compression of data part.
+ //
+
+ int enableCompress;
+
+ //
+ // Set starting point of data part in the message.
+ //
+
+ int dataOffset;
+
+ //
+ // Set maximum size for the data part of each message.
+ //
+
+ int dataLimit;
+
+ //
+ // Set maximum elements in cache.
+ //
+
+ int cacheSlots;
+
+ //
+ // Set the percentage of total cache memory which
+ // a given type of message is allowed to occupy.
+ // When threshold is exceeded store is cleaned to
+ // make room for a new message of the same type.
+ //
+
+ int cacheThreshold;
+
+ //
+ // Don't clean the store if percentage of cache
+ // memory occupied by messages of this type is
+ // below the threshold.
+ //
+
+ int cacheLowerThreshold;
+
+ //
+ // Last operation performed on cache.
+ //
+
+ T_store_action lastAction;
+
+ //
+ // Position of last element stored in cache.
+ //
+
+ short int lastAdded;
+
+ //
+ // Positions of last element found in cache.
+ //
+
+ short int lastHit;
+
+ //
+ // Position of last element erased.
+ //
+
+ short int lastRemoved;
+
+ //
+ // Used to encode the the action to
+ // perform on the store and the slot
+ // involved.
+ //
+
+ ActionCache lastActionCache;
+
+ //
+ // Position in cache where next insertion
+ // is going to take place.
+ //
+
+ short int lastRated;
+
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ MessageStore(StaticCompressor *compressor = NULL);
+
+ virtual ~MessageStore();
+
+ virtual const char *name() const = 0;
+
+ virtual unsigned char opcode() const = 0;
+
+ virtual unsigned int storage() const = 0;
+
+ //
+ // These are members that must be specialized.
+ //
+
+ public:
+
+ virtual Message *create() const = 0;
+
+ virtual Message *create(const Message &message) const = 0;
+
+ virtual void destroy(Message *message) const = 0;
+
+ void validateSize(int size)
+ {
+ if (size < control -> MinimumMessageSize ||
+ size > control -> MaximumMessageSize)
+ {
+ *logofs << name() << ": PANIC! Invalid size " << size
+ << " for message.\n" << logofs_flush;
+
+ cerr << "Error" << ": Invalid size " << size
+ << " for message opcode " << opcode() << ".\n";
+
+ HandleAbort();
+ }
+ }
+
+ void validateSize(int dataSize, int compressedDataSize)
+ {
+ if (dataSize < 0 || dataSize > control ->
+ MaximumMessageSize - 4 || compressedDataSize < 0 ||
+ compressedDataSize >= dataSize)
+ {
+ *logofs << name() << ": PANIC! Invalid data size "
+ << dataSize << " and compressed data size "
+ << compressedDataSize << " for message.\n"
+ << logofs_flush;
+
+ cerr << "Error" << ": Invalid data size "
+ << dataSize << " and compressed data size "
+ << compressedDataSize << " for message "
+ << "opcode " << (unsigned) opcode() << ".\n";
+
+ HandleAbort();
+ }
+ }
+
+ //
+ // Determine if the message can be stored
+ // in the cache.
+ //
+
+ virtual int validateMessage(const unsigned char *buffer, int size)
+ {
+ return (size >= control -> MinimumMessageSize &&
+ size <= control -> MaximumMessageSize);
+ }
+
+ //
+ // Get data offset based on major and minor
+ // opcode of the message.
+ //
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size)
+ {
+ return dataOffset;
+ }
+
+ //
+ // Encode identity and data using the
+ // specific message encoding.
+ //
+ // Some messages do not implement these
+ // methods because the encoding is done
+ // directly in the channel loop. Should
+ // move the encoding methods in in the
+ // message classes.
+ //
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+ {
+ return 1;
+ }
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+ {
+ return 1;
+ }
+
+ //
+ // Encode differences between message
+ // in cache and the one to be encoded.
+ //
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage, ChannelCache *channelCache) const
+ {
+ }
+
+ //
+ // Decode differences and update the
+ // cached version of the same message.
+ //
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+ {
+ }
+
+ //
+ // Post process the message information
+ // contained in the store by either up-
+ // dating the size record or the actual
+ // data part once the message has been
+ // completely sent to our peer.
+ //
+
+ void updateData(const int position, unsigned int dataSize,
+ unsigned int compressedDataSize);
+
+ void updateData(const T_checksum checksum, unsigned int compressedDataSize);
+
+ void updateData(const int position, const unsigned char *newData,
+ unsigned int dataSize, unsigned int compressedDataSize);
+
+ //
+ // These members, used internally
+ // in the message store class, are
+ // mandatory.
+ //
+
+ protected:
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual void dumpIdentity(const Message *message) const = 0;
+
+ //
+ // Design should preserve these from being
+ // virtual.
+ //
+
+ int parseData(Message *message, int split, const unsigned char *buffer,
+ unsigned int size, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian);
+
+ int parseData(Message *message, const unsigned char *buffer,
+ unsigned int size, const unsigned char *compressedData,
+ const unsigned int compressedDataSize, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian);
+
+ int unparseData(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian);
+
+ //
+ // Manage efficient allocation of messages
+ // in the heap.
+ //
+
+ void recycle(Message *message)
+ {
+ #ifdef TEST
+
+ if (message == NULL)
+ {
+ *logofs << name() << ": PANIC! Cannot recycle a null message.\n"
+ << logofs_flush;
+
+ cerr << "Error" << ": Cannot recycle a null message.\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ if (temporary_ == NULL)
+ {
+ //
+ // Be careful when reusing the message as
+ // it can contain valid data that must be
+ // explicitly deallocated if not needed.
+ // Note also that you cannot count on the
+ // initialization made in costructor.
+ //
+
+ temporary_ = message;
+ }
+ else
+ {
+ destroy(message);
+ }
+ }
+
+ void beginChecksum(Message *message)
+ {
+ if (message -> md5_digest_ == NULL)
+ {
+ message -> md5_digest_ = new md5_byte_t[MD5_LENGTH];
+
+ #ifdef DEBUG
+ *logofs << name() << ": Created MD5 digest structure "
+ << "for object at " << message << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ #ifdef DEBUG
+ else
+ {
+ *logofs << name() << ": Using existing MD5 digest structure "
+ << "for object at " << message << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ #ifdef DEBUG
+ *logofs << name() << ": Prepared MD5 digest for object at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ md5_init(md5_state_);
+ }
+
+ void endChecksum(Message *message)
+ {
+ md5_finish(md5_state_, message -> md5_digest_);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Calculated checksum for object at "
+ << message << ".\n" << logofs_flush;
+ #endif
+ }
+
+ void dataChecksum(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian)
+ {
+ //
+ // Messages that have a data part starting
+ // at an offset possibly beyond the end of
+ // the message, must include the size in
+ // the identity checksum.
+ //
+
+ if ((int) size > message -> i_size_)
+ {
+ md5_append(md5_state_, buffer + message -> i_size_,
+ size - message -> i_size_);
+ }
+ }
+
+ //
+ // Repository handling methods.
+ //
+
+ public:
+
+ //
+ // Extract identity and data from buffer.
+ // The size field will be updated at the
+ // time of data parsing.
+ //
+
+ int parse(Message *message, int split, const unsigned char *buffer, unsigned int size,
+ T_checksum_action checksumAction, T_data_action dataAction, int bigEndian);
+
+ int parse(Message *message, const unsigned char *buffer, unsigned int size,
+ const unsigned char *compressedData, const unsigned int compressedDataSize,
+ T_checksum_action checksumAction, T_data_action dataAction, int bigEndian);
+
+ //
+ // From identity and data write the
+ // final message to the raw buffer.
+ //
+
+ int unparse(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian)
+ {
+ return (unparseData(message, buffer, size, bigEndian) &&
+ unparseIdentity(message, buffer, size, bigEndian));
+ }
+
+ void dump(const Message *message) const
+ {
+ dumpIdentity(message);
+
+ dumpData(message);
+ }
+
+ void dumpData(const Message *message) const;
+
+ //
+ // This returns the original message size as it
+ // was received on the link. It takes in account
+ // the data part, regardless data is still stored
+ // in the message object. This information will
+ // be used at the time message is unparsed.
+ //
+
+ int plainSize(const int position) const
+ {
+ return (*messages_)[position] -> size_;
+ }
+
+ //
+ // This returns either the size of identity plus
+ // the compressed data part or 0 if message is
+ // stored in uncompressed format.
+ //
+
+ int compressedSize(const int position) const
+ {
+ return (*messages_)[position] -> c_size_;
+ }
+
+ //
+ // Returns a pointer to message
+ // given its position in cache.
+ //
+
+ Message *get(const int position) const
+ {
+ if (position < 0 || position >= cacheSlots)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Requested position "
+ << position << " is not inside the "
+ << "container.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Requested position "
+ << position << " is not inside the"
+ << "container.\n";
+
+ HandleAbort();
+ }
+ else if ((*messages_)[position] == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Message at position "
+ << position << " is NULL.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Message at position "
+ << position << " is NULL.\n";
+
+ HandleAbort();
+ }
+
+ return (*messages_)[position];
+ }
+
+ //
+ // This is the method called at encoding
+ // side to add a message to cache.
+ //
+
+ int findOrAdd(Message *message, T_checksum_action checksumAction,
+ T_data_action dataAction, int &added, int &locked);
+
+ //
+ // Utility interfaces to message insertion
+ // and deletion.
+ //
+
+ int add(Message *message, const int position,
+ T_checksum_action checksumAction, T_data_action dataAction);
+
+ int remove(const int position, T_checksum_action checksumAction,
+ T_data_action dataAction);
+
+ //
+ // Make space in the repository by remove
+ // the first suitable message object.
+ //
+
+ int clean(T_checksum_action checksumAction);
+
+ //
+ // Increase or decrease the "rating" of
+ // the message object.
+ //
+
+ int touch(Message *message) const;
+ int untouch(Message *message) const;
+
+ int getTouches(const int position) const
+ {
+ Message *message = (*messages_)[position];
+
+ if (message == NULL)
+ {
+ return 0;
+ }
+
+ return message -> hits_;
+ }
+
+ //
+ // Gives a 'weight' to the cached message. A zero
+ // value means object can be safely removed. A value
+ // greater than zero means it is advisable to retain
+ // the object. A negative result means it is mandato-
+ // ry to keep object in cache.
+ //
+
+ int getRating(Message *message, T_rating type) const;
+
+ //
+ // Increase or decrease locks of message at given
+ // position. A locked message will not be removed
+ // from the message store until the lock counter
+ // is zero.
+ //
+
+ int lock(const int position) const;
+ int unlock(const int position) const;
+
+ int getLocks(const int position) const
+ {
+ Message *message = (*messages_)[position];
+
+ if (message == NULL)
+ {
+ return 0;
+ }
+
+ return message -> locks_;
+ }
+
+ T_checksum const getChecksum(const int position) const
+ {
+ return getChecksum(get(position));
+ }
+
+ T_checksum const getChecksum(const Message *message) const
+ {
+ if (message -> md5_digest_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << name() << ": PANIC! Checksum not initialized "
+ << "for object at " << message << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Checksum not initialized "
+ << "for object at " << message << ".\n";
+
+ HandleAbort();
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Got checksum for object at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return message -> md5_digest_;
+ }
+
+ //
+ // Calculate the checksum on the fly based the
+ // opcode in the buffer. Useful in the case a
+ // message was not processed or was not stored
+ // in the cache. The returned checksum must be
+ // explicitly deallocated by the caller, after
+ // use.
+ //
+
+ T_checksum getChecksum(const unsigned char *buffer,
+ unsigned int size, int bigEndian);
+
+ const unsigned char *getData(const Message *message) const
+ {
+ return message -> data_.begin();
+ }
+
+ int plainSize(const Message *message) const
+ {
+ return message -> size_;
+ }
+
+ int identitySize(Message *message)
+ {
+ return message -> i_size_;
+ }
+
+ int compressedSize(const Message *message) const
+ {
+ return message -> c_size_;
+ }
+
+ Message *getTemporary()
+ {
+ if (temporary_ == NULL)
+ {
+ temporary_ = create();
+ }
+
+ return temporary_;
+ }
+
+ void resetTemporary()
+ {
+ temporary_ = NULL;
+ }
+
+ //
+ // On side where we don't have checksums, we
+ // count how many messages are in the array.
+ // This is obviously expensive and should be
+ // only performed when reporting statistics.
+ //
+
+ int getSize() const
+ {
+ int size = checksums_ -> size();
+
+ if (size == 0)
+ {
+ for (int i = 0; i < cacheSlots; i++)
+ {
+ if ((*messages_)[i] != NULL)
+ {
+ size++;
+ }
+ }
+ }
+
+ return size;
+ }
+
+ int getLocalStorageSize() const
+ {
+ return localStorageSize_;
+ }
+
+ int getRemoteStorageSize() const
+ {
+ return remoteStorageSize_;
+ }
+
+ int getLocalTotalStorageSize() const
+ {
+ return totalLocalStorageSize_;
+ }
+
+ int getRemoteTotalStorageSize() const
+ {
+ return totalRemoteStorageSize_;
+ }
+
+ static int getCumulativeTotalStorageSize()
+ {
+ return (totalLocalStorageSize_ > totalRemoteStorageSize_ ?
+ totalLocalStorageSize_ : totalRemoteStorageSize_);
+ }
+
+ int saveStore(ostream *cachefs, md5_state_t *md5_state_stream,
+ md5_state_t *md5_state_client, T_checksum_action checksumAction,
+ T_data_action dataAction, int bigEndian);
+
+ int loadStore(istream *cachefs, md5_state_t *md5_state_stream,
+ T_checksum_action checksumAction, T_data_action dataAction,
+ int bigEndian);
+
+ protected:
+
+ //
+ // Estimate the memory requirements of given
+ // instance of message. Size includes memory
+ // allocated from heap to store checksum and
+ // data.
+ //
+
+ void storageSize(const Message *message, unsigned int &local,
+ unsigned int &remote) const;
+
+ //
+ // Just used for debug.
+ //
+
+ void printStorageSize();
+
+ //
+ // Repositories where to save cached messages.
+ // First is a vector of pointers, the second
+ // is a hash table used for fast lookups.
+ //
+
+ T_messages *messages_;
+ T_checksums *checksums_;
+
+ //
+ // A message object to be used as a temporary.
+ // Reuse the temporary object if possible, if
+ // not, create a new instance.
+ //
+
+ Message *temporary_;
+
+ //
+ // Used to calculate message's checksum.
+ //
+
+ md5_state_t *md5_state_;
+
+ private:
+
+ //
+ // Used to compress data payload.
+ //
+
+ StaticCompressor *compressor_;
+
+ //
+ // Keep track of how many bytes
+ // are taken by cache.
+ //
+
+ int localStorageSize_;
+ int remoteStorageSize_;
+
+ static int totalLocalStorageSize_;
+ static int totalRemoteStorageSize_;
+
+ //
+ // Used to track object allocation and deallocation.
+ //
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+//
+// This is an ancillary class of the message
+// store, used to encode extensions based on
+// the minor opcode.
+//
+
+class MinorMessageStore
+{
+ public:
+
+ virtual ~MinorMessageStore()
+ {
+ }
+
+ virtual const char *name() const = 0;
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size) = 0;
+
+ virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+ {
+ return 1;
+ }
+
+ virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const
+ {
+ return 1;
+ }
+
+ virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+ {
+ }
+
+ virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+ {
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+ {
+ }
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+ {
+ }
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, md5_state_t *md5_state,
+ int bigEndian) const = 0;
+};
+
+#endif /* Message_H */
+
diff --git a/nxcomp/src/Misc.cpp b/nxcomp/src/Misc.cpp
new file mode 100644
index 000000000..b40e6409e
--- /dev/null
+++ b/nxcomp/src/Misc.cpp
@@ -0,0 +1,1934 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cstdio>
+#include <cctype>
+#include <cstdlib>
+#include <unistd.h>
+#include <csignal>
+
+#include <errno.h>
+#include <string.h>
+
+#include "NXproto.h"
+
+#include "MD5.h"
+
+#include "Misc.h"
+#include "Proxy.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#define OPCODES
+#undef TEST
+#undef DEBUG
+
+//
+// By default nxproxy binds to all network interfaces, setting
+// DEFAULT_LOOPBACK_BIND to 1 enables binding to the loopback
+// device only.
+//
+
+const int DEFAULT_LOOPBACK_BIND = 0;
+
+//
+// TCP port offset applied to any NX port specification.
+//
+
+const int DEFAULT_NX_PROXY_PORT_OFFSET = 4000;
+
+//
+// Default TCP port used by client proxy to listen to
+// X clients and by server proxy to connect to remote.
+//
+
+const int DEFAULT_NX_PROXY_PORT = 8;
+
+//
+// Default X display number that client proxy imitates.
+//
+
+const int DEFAULT_NX_X_PORT = 8;
+
+//
+// Default ports used for listening for cups, samba, http,
+// multimedia and auxiliary X connections. Arbitrary ports
+// can be used by passing the service's port at the proxy
+// startup. By default ports are determined by adding the
+// offset below to the offset of the proxied display. For
+// example, if the proxy is impersonating the display :8,
+// SMB tunnels can be created by connecting to port 3008.
+//
+// Considering that the NX server uses to start the first
+// session at display offset 1000, we must lower the CUPS
+// and SMB ports to avoid interference with normal X ses-
+// sions run on the server.
+//
+// Font server connections are used to let the X server on
+// the client connect to a font server on the NX server.
+//
+// Slave channels can be originated by both sides so we need
+// different offsets in the case the user runs both proxies
+// on the same host.
+//
+
+const int DEFAULT_NX_CUPS_PORT_OFFSET = 2000;
+const int DEFAULT_NX_SMB_PORT_OFFSET = 3000;
+const int DEFAULT_NX_MEDIA_PORT_OFFSET = 7000;
+const int DEFAULT_NX_AUX_PORT_OFFSET = 8000;
+const int DEFAULT_NX_HTTP_PORT_OFFSET = 9000;
+const int DEFAULT_NX_FONT_PORT_OFFSET = 10000;
+
+const int DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET = 11000;
+const int DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET = 12000;
+
+//
+// Usage info and copyright.
+//
+
+static const char UsageInfo[] =
+"\n\
+ Usage: nxproxy [OPTIONS] host:port\n\
+\n\
+ -C Specify that nxproxy has to run on the 'X client'\n\
+ side, listening for connections and impersonating\n\
+ an X server.\n\
+\n\
+ -S Specify that nxproxy has to run in 'X server' mode,\n\
+ thus forwarding the connections to daemons running\n\
+ on the client.\n\
+\n\
+ -h Print this message.\n\
+\n\
+ -v Print version information.\n\
+\n\
+ host:port Put at the end, specifies the host and port of the\n\
+ listening proxy.\n\
+\n\
+ name=value Set the NX option to the provided value.\n\
+\n\
+ Multiple NX options can be specified in the DISPLAY environment\n\
+ or on the command line, by using the nx/nx,option=value notation.\n\
+\n\
+ Options:\n\
+\n\
+ link=s An indication of the link speed that is going to be\n\
+ used between the proxies. Usually the compression\n\
+ and the other link parameters depend on this setting.\n\
+ The value can be either 'modem', 'isdn', 'adsl',\n\
+ 'wan', 'lan', 'local' or a bandwidth specification,\n\
+ like for example '56k', '1m', '100m', etc.\n\
+\n\
+ type=s Type of session, for example 'windows', 'unix-kde'.\n\
+ 'unix-application', etc.\n\
+\n\
+ display=s Specify the real display where X connections have\n\
+ to be forwarded by the proxy running on the client.\n\
+\n\
+ listen=n Local port used for accepting the proxy connection.\n\
+\n\
+ loopback=b Bind to the loopback device only.\n\
+\n\
+ accept=s Name or IP of host that can connect to the proxy.\n\
+\n\
+ connect=s Name or IP of host that the proxy will connect to.\n\
+\n\
+ port=n Remote port used for the connection.\n\
+\n\
+ retry=n Number of connection atempts.\n\
+\n\
+ root=s The root directory for the session. Usually is the\n\
+ C-* or S-* in the .nx directory in the user's home,\n\
+ with '*' being the virtual display.\n\
+\n\
+ session=s Name of the session file. The default is the name\n\
+ 'session' in the session directory.\n\
+\n\
+ errors=s Name of the log file used by the proxy. The default\n\
+ is the name 'errors' in the session directory.\n\
+\n\
+ stats=s Name of the file where are written the proxy stat-\n\
+ istics. The default is a file 'stats' in the session\n\
+ directory. The proxy replaces the data in the file\n\
+ whenever it receives a SIGUSR1 or SIGUSR2 signal:\n\
+\n\
+ SIGUSR1 Gives total statistics, i.e. statistics\n\
+ collected since the beginning of the\n\
+ session.\n\
+\n\
+ SIGUSR2 Gives partial statistics, i.e. statist-\n\
+ ics collected since the last time this\n\
+ signal was received.\n\
+\n\
+ cookie=s Use the provided cookie for authenticating to the\n\
+ remote proxy. The same cookie is used as the fake\n\
+ value used for the X authorization. The fake cookie\n\
+ is replaced on the X server side with the real cookie\n\
+ to be used for the display, so that the real cookie\n\
+ doesn't have to travel over the net. When not using\n\
+ a proxy cookie, any host will be able to connect to\n\
+ the proxy. See also the 'accept' parameter.\n\
+\n\
+ nodelay=b A boolean indicating if TCP_NODELAY has to be set\n\
+ on the proxy link. Old Linux kernels had problems\n\
+ with handling TCP_NODELAY on PPP links.\n\
+\n\
+ policy=b Let or not the agent decide when it is the best time\n\
+ to flush the proxy link. If set to 0, the proxy will\n\
+ flush any encoded data immediately. The option has\n\
+ only effect on the X client side proxy.\n\
+\n\
+ render=b Enable or disable use of the RENDER extension.\n\
+\n\
+ taint=b Try to suppress trivial sources of X roundtrips by\n\
+ generating the reply on the X client side.\n\
+\n\
+ delta=b Enable X differential compression.\n\
+\n\
+ data=n Enable or disable the ZLIB data compression. It is\n\
+ possible to specify a value between 0 and 9. Usual-\n\
+ ly the value is chosen automatically based on the\n\
+ requested link setting.\n\
+\n\
+ stream=n Enable or disable the ZLIB stream compression. The\n\
+ value, between 0 and 9, is usually determined accor-\n\
+ ding to the requested link setting.\n\
+\n\
+ limit=n Specify a bitrate limit allowed for this session.\n\
+\n\
+ memory=n Trigger memory optimizations used to keep small the\n\
+ size of X buffers. This is useful on embedded plat-\n\
+ forms, or where memory is scarce.\n\
+\n\
+ cache=n Size of the in-memory X message cache. Setting the\n\
+ value to 0 will disable the memory cache as well\n\
+ as the NX differential compression.\n\
+\n\
+ images=n Size of the persistent image cache.\n\
+\n\
+ shseg=n Enable the use of the MIT-SHM extension between the\n\
+ NX client proxy and the real X server. A value greater\n\
+ than 1 is assumed to be the size of requested shared\n\
+ memory segment. By default, the size of the segment is\n\
+ determined based on the size of the in-memory cache.\n\
+\n\
+ load=b Enable loading a persistent X message cache at the\n\
+ proxy startup.\n\
+\n\
+ save=b Enable saving a persistent X message cache at the\n\
+ end of session.\n\
+\n\
+ cups=n Enable or disable forwarding of CUPS connections,\n\
+ by listening on the optional port 'n'.\n\
+\n\
+ aux=n Enable or disable forwarding of the auxiliary X chan-\n\
+ nel used for controlling the keyboard. The 'keybd=n'\n\
+ form is accepted for backward compatibility.\n\
+\n\
+ smb=n Enable or disable forwarding of SMB connections. The\n\
+ 'samba=n' form is accepted for backward compatibility.\n\
+\n\
+ media=n Enable forwarding of audio connections.\n\
+\n\
+ http=n Enable forwarding of HTTP connections.\n\
+\n\
+ font=n Enable forwarding of reversed connections to a font\n\
+ server running on the NX server.\n\
+\n\
+ file=n Enable forwarding of file transfer connections.\n\
+\n\
+ mask=n Determine the distribution of channel ids between the\n\
+ proxies. By default, channels whose ids are multiple\n\
+ of 8 (starting from 0) are reserved for the NX client\n\
+ side. All the other channels can be allocated by the\n\
+ NX server side.\n\
+\n\
+ timeout=t Specify the keep-alive timeout used by proxies to\n\
+ determine if there is a network problem preventing\n\
+ communication with the remote peer. A value of 0\n\
+ disables the check.\n\
+\n\
+ cleanup=t Specify the number of seconds the proxy has to wait\n\
+ at session shutdown before closing all channels.\n\
+ The feature is used by the NX server to ensure that\n\
+ services are disconnected before shutting down the\n\
+ link.\n\
+\n\
+ pack=s Determine the method used to compress images.\n\
+\n\
+ product=s The product id of the client or server. The value is\n\
+ ignored by the proxy, but the client or server can\n\
+ provide it to facilitate the support.\n\
+\n\
+ core=b Enable production of core dumps when aborting the\n\
+ proxy connection.\n\
+\n\
+ options=s Specify an additional file containing options that\n\
+ has to be merged with option read from the command\n\
+ line or the environment.\n\
+\n\
+ kill=n Add the given process to the list of daemons that\n\
+ must be terminated at session shutdown. Multiple\n\
+ 'kill=n' options can be specified. The proxy will\n\
+ send them a SIGTERM signal just before exiting.\n\
+\n\
+ strict=b Optimize for responsiveness, rather than for the best\n\
+ use of all the available bandwidth.\n\
+\n\
+ encryption=b Should be set to 1 if the proxy is running as part of\n\
+ a program providing encryption of the point to point\n\
+ communication.\n\
+\n\
+rootless=b\n\
+geometry=s\n\
+resize=b\n\
+fullscreen=b\n\
+keyboard=s\n\
+clipboard=s\n\
+streaming=n\n\
+backingstore=n\n\
+composite=n\n\
+xinerama=n\n\
+shmem=b\n\
+shpix=b\n\
+kbtype=s\n\
+client=s\n\
+shadow=n\n\
+shadowuid=n\n\
+shadowmode=s\n\
+defer=n\n\
+tile=s\n\
+menu=n\n\
+sleep=n\n\
+tolerancechecks=s\n\
+ These options are interpreted by the NX agent. They\n\
+ are ignored by the proxy.\n\
+\n\
+ Environment:\n\
+\n\
+ NX_ROOT The root NX directory is the place where the session\n\
+ directory and the cache files are created. This is\n\
+ usually overridden by passing the 'root=' option. By\n\
+ default, the root NX directory is assumed to be the\n\
+ directory '.nx' in the user's home.\n\
+\n\
+ NX_SYSTEM The directory where NX programs and libraries reside.\n\
+ If not set, the value is assumed to be '/usr/NX'.\n\
+ Programs, libraries and data files are respectedly\n\
+ searched in the 'bin', 'lib' and 'share' subdirecto-\n\
+ ries.\n\
+\n\
+ NX_HOME The NX user's home directory. If NX_ROOT is not set\n\
+ or invalid, the user's NX directory is created here.\n\
+\n\
+ NX_TEMP The directory where the X11 Unix Domain Sockets and\n\
+ all temporary files are to be created.\n\
+\n\
+ NX_CLIENT The full path to the nxclient executable. If the va-\n\
+ riable is not set, the nxclient executable will be\n\
+ run assuming that the program is in the system path.\n\
+ This can be useful on platforms like Windows and the\n\
+ Mac where nxclient is located in a different direct-\n\
+ ory compared to the other programs, to make easier\n\
+ for the user to execute the program from the shell.\n\
+\n\
+ NX_SLAVE_CMD The full path to the slave channel handler. When the\n\
+ slave channel is enabled, the agent will listen on a\n\
+ port and forward the connection to the NX_SLAVE_CMD\n\
+ program. This can be used to implement agent/proxy\n\
+ communication for applications such as serial port and\n\
+ USB forwarding.\n\
+\n\
+ Shell environment:\n\
+\n\
+ HOME The variable is checked in the case NX_HOME is not\n\
+ set, null or invalid.\n\
+\n\
+ TEMP The variable is checked whenever the NX_TEMP direct-\n\
+ ory is not set, null or invalid.\n\
+\n\
+ PATH The path where all executables are searched, except\n\
+ nxclient. If NX_CLIENT is not set, also the client\n\
+ executable is searched in the system path.\n\
+\n\
+ LD_LIBRARY_PATH\n\
+ System-wide library search order. This should be set\n\
+ by the program invoking the proxy.\n\
+\n\
+ DISPLAY On the X server side, the DISPLAY variable indicates\n\
+ the location of the X11 server. When nxcomp is used\n\
+ as a transport library, the DISPLAY may represent a\n\
+ NX transport specification and options can passed in\n\
+ the form nx/nx,option=value...\n\
+\n\
+ XAUTHORITY This is the file containing the X11 authorization\n\
+ cookie. If not set, the file is assumed to be in\n\
+ the user's home (either NX_HOME or HOME).\n\
+\n\
+";
+
+const char *GetUsageInfo()
+{
+ return UsageInfo;
+}
+
+static const char CopyrightInfo[] =
+"\
+Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com)\n\
+Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de>\n\
+Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de>\n\
+Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de>\n\
+Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>\n\
+Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com)\n\
+\n\
+NXCOMP, NX protocol compression and NX extensions to this software\n\
+are copyright of the aforementioned persons and companies.\n\
+\n\
+Redistribution and use of the present software is allowed according\n\
+to terms specified in the file LICENSE.nxcomp which comes in the\n\
+source distribution.\n\
+\n\
+All rights reserved.\n\
+\n\
+NOTE: This software has received contributions from various other\n\
+contributors, only the core maintainers and supporters are listed as\n\
+copyright holders. Please contact us, if you feel you should be listed\n\
+as copyright holder, as well.\n\
+";
+
+const char *GetCopyrightInfo()
+{
+ return CopyrightInfo;
+}
+
+static const char OtherCopyrightInfo[] =
+"\
+NX protocol compression is derived from DXPC project.\n\
+\n\
+Copyright (c) 1995,1996 Brian Pane\n\
+Copyright (c) 1996,1997 Zachary Vonler and Brian Pane\n\
+Copyright (c) 1999 Kevin Vigor and Brian Pane\n\
+Copyright (c) 2000,2003 Gian Filippo Pinzari and Brian Pane\n\
+\n\
+All rights reserved.\n\
+";
+
+const char *GetOtherCopyrightInfo()
+{
+ return OtherCopyrightInfo;
+}
+
+int _hostBigEndian = 0;
+int _storeBigEndian = 0;
+
+const unsigned int IntMask[33] =
+{
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff
+};
+
+unsigned int GetUINT(unsigned const char *buffer, int bigEndian)
+{
+ //
+ // It doesn't work on SPARCs if the buffer
+ // is not aligned to the word boundary. We
+ // should check the CPU, not the OS as this
+ // surely applies to other architectures.
+ //
+
+ #ifndef __sun
+
+ if (_hostBigEndian == bigEndian)
+ {
+ return *((unsigned short *) buffer);
+ }
+
+ #else
+
+ if (_hostBigEndian == bigEndian && ((unsigned int) buffer) & 0x1 == 0)
+ {
+ return *((unsigned short *) buffer);
+ }
+
+ #endif
+
+ unsigned int result;
+
+ if (bigEndian)
+ {
+ result = *buffer;
+
+ result <<= 8;
+
+ result += buffer[1];
+ }
+ else
+ {
+ result = buffer[1];
+
+ result <<= 8;
+
+ result += *buffer;
+ }
+
+ return result;
+}
+
+unsigned int GetULONG(unsigned const char *buffer, int bigEndian)
+{
+ //
+ // It doesn't work on SPARCs if the buffer
+ // is not aligned to word the boundary.
+ //
+
+ #ifndef __sun
+
+ if (_hostBigEndian == bigEndian)
+ {
+ return *((unsigned int *) buffer);
+ }
+
+ #else
+
+ if (_hostBigEndian == bigEndian && ((unsigned int) buffer) & 0x3 == 0)
+ {
+ return *((unsigned int *) buffer);
+ }
+
+ #endif
+
+ const unsigned char *next = (bigEndian ? buffer : buffer + 3);
+
+ unsigned int result = 0;
+
+ for (int i = 0; i < 4; i++)
+ {
+ result <<= 8;
+
+ result += *next;
+
+ if (bigEndian)
+ {
+ next++;
+ }
+ else
+ {
+ next--;
+ }
+ }
+
+ return result;
+}
+
+void PutUINT(unsigned int value, unsigned char *buffer, int bigEndian)
+{
+ if (_hostBigEndian == bigEndian)
+ {
+ *((unsigned short *) buffer) = value;
+
+ return;
+ }
+
+ if (bigEndian)
+ {
+ buffer[1] = (unsigned char) (value & 0xff);
+
+ value >>= 8;
+
+ *buffer = (unsigned char) value;
+ }
+ else
+ {
+ *buffer = (unsigned char) (value & 0xff);
+
+ value >>= 8;
+
+ buffer[1] = (unsigned char) value;
+ }
+}
+
+void PutULONG(unsigned int value, unsigned char *buffer, int bigEndian)
+{
+ if (_hostBigEndian == bigEndian)
+ {
+ *((unsigned int *) buffer) = value;
+
+ return;
+ }
+
+ if (bigEndian)
+ {
+ buffer += 3;
+
+ for (int i = 4; i > 0; i--)
+ {
+ *buffer-- = (unsigned char) (value & 0xff);
+
+ value >>= 8;
+ }
+ }
+ else
+ {
+ for (int i = 4; i > 0; i--)
+ {
+ *buffer++ = (unsigned char) (value & 0xff);
+
+ value >>= 8;
+ }
+ }
+}
+
+int CheckData(istream *fs)
+{
+ if (fs == NULL || fs -> fail())
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int CheckData(ostream *fs)
+{
+ if (fs == NULL || fs -> fail())
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int PutData(ostream *fs, const unsigned char *buffer, int size)
+{
+ fs -> write((char *) buffer, size);
+
+ #ifdef DEBUG
+ *logofs << "PutData: Written " << size << " bytes with eof "
+ << fs -> eof() << " fail " << fs -> fail() << " and bad "
+ << fs -> bad() << ".\n" << logofs_flush;
+ #endif
+
+ if (fs -> fail())
+ {
+ return -1;
+ }
+
+ return size;
+}
+
+int GetData(istream *fs, unsigned char *buffer, int size)
+{
+ fs -> read((char *) buffer, size);
+
+ #ifdef DEBUG
+ *logofs << "GetData: Read " << size << " bytes with eof "
+ << fs -> eof() << " fail " << fs -> fail()
+ << " and bad " << fs -> bad() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef __APPLE__
+
+ if (fs -> bad())
+ {
+ return -1;
+ }
+
+ #else
+
+ if (fs -> fail())
+ {
+ return -1;
+ }
+
+ #endif
+
+ return size;
+}
+
+int FlushData(ostream *fs)
+{
+ fs -> flush();
+
+ if (fs -> fail())
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+unsigned int RoundUp2(unsigned int x)
+{
+ unsigned int y = x / 2;
+
+ y *= 2;
+
+ if (y != x)
+ {
+ y += 2;
+ }
+
+ return y;
+}
+
+unsigned int RoundUp4(unsigned int x)
+{
+ unsigned int y = x / 4;
+
+ y *= 4;
+
+ if (y != x)
+ {
+ y += 4;
+ }
+
+ return y;
+}
+
+unsigned int RoundUp8(unsigned int x)
+{
+ unsigned int y = x / 8;
+
+ y *= 8;
+
+ if (y != x)
+ {
+ y += 8;
+ }
+
+ return y;
+}
+
+const char *DumpSignal(int signal)
+{
+ switch (signal)
+ {
+ case SIGCHLD:
+ {
+ return "SIGCHLD";
+ }
+ case SIGUSR1:
+ {
+ return "SIGUSR1";
+ }
+ case SIGUSR2:
+ {
+ return "SIGUSR2";
+ }
+ case SIGHUP:
+ {
+ return "SIGHUP";
+ }
+ case SIGINT:
+ {
+ return "SIGINT";
+ }
+ case SIGTERM:
+ {
+ return "SIGTERM";
+ }
+ case SIGPIPE:
+ {
+ return "SIGPIPE";
+ }
+ case SIGALRM:
+ {
+ return "SIGALRM";
+ }
+ case SIGVTALRM:
+ {
+ return "SIGVTALRM";
+ }
+ case SIGWINCH:
+ {
+ return "SIGWINCH";
+ }
+ case SIGIO:
+ {
+ return "SIGIO";
+ }
+ case SIGTSTP:
+ {
+ return "SIGTSTP";
+ }
+ case SIGTTIN:
+ {
+ return "SIGTTIN";
+ }
+ case SIGTTOU:
+ {
+ return "SIGTTOU";
+ }
+ case SIGSEGV:
+ {
+ return "SIGSEGV";
+ }
+ case SIGABRT:
+ {
+ return "SIGABRT";
+ }
+ default:
+ {
+ return "Unknown";
+ }
+ }
+}
+
+const char *DumpPolicy(int type)
+{
+ switch ((T_flush_policy) type)
+ {
+ case policy_immediate:
+ {
+ return "immediate";
+ }
+ case policy_deferred:
+ {
+ return "deferred";
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Misc: PANIC! Unknown policy type '"
+ << type << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown policy type '"
+ << type << "'.\n";
+
+ HandleCleanup();
+ }
+ }
+}
+
+const char *DumpAction(int type)
+{
+ T_store_action action = (T_store_action) type;
+
+ if (action == IS_HIT)
+ {
+ return "is_hit";
+ }
+ else if (action == IS_ADDED)
+ {
+ return "is_added";
+ }
+ else if (action == is_discarded)
+ {
+ return "is_discarded";
+ }
+ else if (action == is_removed)
+ {
+ return "is_removed";
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Misc: PANIC! Unknown store action '"
+ << type << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown store action '"
+ << type << "'.\n";
+
+ HandleCleanup();
+ }
+}
+
+const char *DumpState(int type)
+{
+ switch ((T_split_state) type)
+ {
+ case split_added:
+ {
+ return "split_added";
+ }
+ case split_missed:
+ {
+ return "split_missed";
+ }
+ case split_loaded:
+ {
+ return "split_loaded";
+ }
+ case split_aborted:
+ {
+ return "split_aborted";
+ }
+ case split_notified:
+ {
+ return "split_notified";
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Misc: PANIC! Unknown split state '"
+ << type << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown split state '"
+ << type << "'.\n";
+
+ HandleCleanup();
+ }
+ }
+}
+
+const char *DumpControl(int code)
+{
+ switch ((T_proxy_code) code)
+ {
+ case code_new_x_connection:
+ {
+ return "code_new_x_connection";
+ }
+ case code_new_cups_connection:
+ {
+ return "code_new_cups_connection";
+ }
+ case code_new_aux_connection:
+ {
+ return "code_new_aux_connection";
+ }
+ case code_new_smb_connection:
+ {
+ return "code_new_smb_connection";
+ }
+ case code_new_media_connection:
+ {
+ return "code_new_media_connection";
+ }
+ case code_switch_connection:
+ {
+ return "code_switch_connection";
+ }
+ case code_drop_connection:
+ {
+ return "code_drop_connection";
+ }
+ case code_finish_connection:
+ {
+ return "code_finish_connection";
+ }
+ case code_begin_congestion:
+ {
+ return "code_begin_congestion";
+ }
+ case code_end_congestion:
+ {
+ return "code_end_congestion";
+ }
+ case code_alert_request:
+ {
+ return "code_alert_request";
+ }
+ case code_alert_reply:
+ {
+ return "code_alert_reply";
+ }
+ case code_reset_request:
+ {
+ return "code_reset_request";
+ }
+ case code_reset_reply:
+ {
+ return "code_reset_reply";
+ }
+ case code_load_request:
+ {
+ return "code_load_request";
+ }
+ case code_load_reply:
+ {
+ return "code_load_reply";
+ }
+ case code_save_request:
+ {
+ return "code_save_request";
+ }
+ case code_save_reply:
+ {
+ return "code_save_reply";
+ }
+ case code_shutdown_request:
+ {
+ return "code_shutdown_request";
+ }
+ case code_shutdown_reply:
+ {
+ return "code_shutdown_reply";
+ }
+ case code_control_token_request:
+ {
+ return "code_control_token_request";
+ }
+ case code_control_token_reply:
+ {
+ return "code_control_token_reply";
+ }
+ case code_configuration_request:
+ {
+ return "code_configuration_request";
+ }
+ case code_configuration_reply:
+ {
+ return "code_configuration_reply";
+ }
+ case code_statistics_request:
+ {
+ return "code_statistics_request";
+ }
+ case code_statistics_reply:
+ {
+ return "code_statistics_reply";
+ }
+ case code_new_http_connection:
+ {
+ return "code_new_http_connection";
+ }
+ case code_sync_request:
+ {
+ return "code_sync_request";
+ }
+ case code_sync_reply:
+ {
+ return "code_sync_reply";
+ }
+ case code_new_font_connection:
+ {
+ return "code_new_font_connection";
+ }
+ case code_new_slave_connection:
+ {
+ return "code_new_slave_connection";
+ }
+ case code_finish_listeners:
+ {
+ return "code_finish_listeners";
+ }
+ case code_split_token_request:
+ {
+ return "code_split_token_request";
+ }
+ case code_split_token_reply:
+ {
+ return "code_split_token_reply";
+ }
+ case code_data_token_request:
+ {
+ return "code_data_token_request";
+ }
+ case code_data_token_reply:
+ {
+ return "code_data_token_reply";
+ }
+ default:
+ {
+ #ifdef WARNING
+ *logofs << "Misc: WARNING! Unknown control code '"
+ << code << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unknown control code '"
+ << code << "'.\n";
+
+ return "unknown";
+ }
+ }
+}
+
+const char *DumpSession(int code)
+{
+ switch ((T_session_mode) code)
+ {
+ case session_agent:
+ {
+ return "session_agent";
+ }
+ case session_shadow:
+ {
+ return "session_shadow";
+ }
+ case session_proxy:
+ {
+ return "session_proxy";
+ }
+ default:
+ {
+ #ifdef WARNING
+ *logofs << "Misc: WARNING! Unknown session type '"
+ << code << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unknown session type '"
+ << code << "'.\n";
+
+ return "unknown";
+ }
+ }
+}
+
+const char *DumpToken(int type)
+{
+ switch ((T_token_type) type)
+ {
+ case token_control:
+ {
+ return "token_control";
+ }
+ case token_split:
+ {
+ return "token_split";
+ }
+ case token_data:
+ {
+ return "token_data";
+ }
+ default:
+ {
+ #ifdef WARNING
+ *logofs << "Misc: WARNING! Unknown token type '"
+ << type << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unknown token type '"
+ << type << "'.\n";
+
+ return "unknown";
+ }
+ }
+}
+
+//
+// Always include this in code as it is generally
+// needed to test channels and split store.
+//
+
+const char *DumpChecksum(const void *checksum)
+{
+ static char string[MD5_LENGTH * 2 + 1];
+
+ if (checksum != NULL)
+ {
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ sprintf(string + (i * 2), "%02X", ((unsigned char *) checksum)[i]);
+ }
+ }
+ else
+ {
+ strcpy(string, "null");
+ }
+
+ return string;
+}
+
+//
+// Define OPCODES here and in the channel
+// if you want to log the opcode literal.
+//
+
+#ifdef OPCODES
+
+const char *DumpOpcode(const int &opcode)
+{
+ switch (opcode)
+ {
+ case X_CreateWindow:
+ {
+ return "X_CreateWindow";
+ }
+ case X_ChangeWindowAttributes:
+ {
+ return "X_ChangeWindowAttributes";
+ }
+ case X_GetWindowAttributes:
+ {
+ return "X_GetWindowAttributes";
+ }
+ case X_DestroyWindow:
+ {
+ return "X_DestroyWindow";
+ }
+ case X_DestroySubwindows:
+ {
+ return "X_DestroySubwindows";
+ }
+ case X_ChangeSaveSet:
+ {
+ return "X_ChangeSaveSet";
+ }
+ case X_ReparentWindow:
+ {
+ return "X_ReparentWindow";
+ }
+ case X_MapWindow:
+ {
+ return "X_MapWindow";
+ }
+ case X_MapSubwindows:
+ {
+ return "X_MapSubwindows";
+ }
+ case X_UnmapWindow:
+ {
+ return "X_UnmapWindow";
+ }
+ case X_UnmapSubwindows:
+ {
+ return "X_UnmapSubwindows";
+ }
+ case X_ConfigureWindow:
+ {
+ return "X_ConfigureWindow";
+ }
+ case X_CirculateWindow:
+ {
+ return "X_CirculateWindow";
+ }
+ case X_GetGeometry:
+ {
+ return "X_GetGeometry";
+ }
+ case X_QueryTree:
+ {
+ return "X_QueryTree";
+ }
+ case X_InternAtom:
+ {
+ return "X_InternAtom";
+ }
+ case X_GetAtomName:
+ {
+ return "X_GetAtomName";
+ }
+ case X_ChangeProperty:
+ {
+ return "X_ChangeProperty";
+ }
+ case X_DeleteProperty:
+ {
+ return "X_DeleteProperty";
+ }
+ case X_GetProperty:
+ {
+ return "X_GetProperty";
+ }
+ case X_ListProperties:
+ {
+ return "X_ListProperties";
+ }
+ case X_SetSelectionOwner:
+ {
+ return "X_SetSelectionOwner";
+ }
+ case X_GetSelectionOwner:
+ {
+ return "X_GetSelectionOwner";
+ }
+ case X_ConvertSelection:
+ {
+ return "X_ConvertSelection";
+ }
+ case X_SendEvent:
+ {
+ return "X_SendEvent";
+ }
+ case X_GrabPointer:
+ {
+ return "X_GrabPointer";
+ }
+ case X_UngrabPointer:
+ {
+ return "X_UngrabPointer";
+ }
+ case X_GrabButton:
+ {
+ return "X_GrabButton";
+ }
+ case X_UngrabButton:
+ {
+ return "X_UngrabButton";
+ }
+ case X_ChangeActivePointerGrab:
+ {
+ return "X_ChangeActivePointerGrab";
+ }
+ case X_GrabKeyboard:
+ {
+ return "X_GrabKeyboard";
+ }
+ case X_UngrabKeyboard:
+ {
+ return "X_UngrabKeyboard";
+ }
+ case X_GrabKey:
+ {
+ return "X_GrabKey";
+ }
+ case X_UngrabKey:
+ {
+ return "X_UngrabKey";
+ }
+ case X_AllowEvents:
+ {
+ return "X_AllowEvents";
+ }
+ case X_GrabServer:
+ {
+ return "X_GrabServer";
+ }
+ case X_UngrabServer:
+ {
+ return "X_UngrabServer";
+ }
+ case X_QueryPointer:
+ {
+ return "X_QueryPointer";
+ }
+ case X_GetMotionEvents:
+ {
+ return "X_GetMotionEvents";
+ }
+ case X_TranslateCoords:
+ {
+ return "X_TranslateCoords";
+ }
+ case X_WarpPointer:
+ {
+ return "X_WarpPointer";
+ }
+ case X_SetInputFocus:
+ {
+ return "X_SetInputFocus";
+ }
+ case X_GetInputFocus:
+ {
+ return "X_GetInputFocus";
+ }
+ case X_QueryKeymap:
+ {
+ return "X_QueryKeymap";
+ }
+ case X_OpenFont:
+ {
+ return "X_OpenFont";
+ }
+ case X_CloseFont:
+ {
+ return "X_CloseFont";
+ }
+ case X_QueryFont:
+ {
+ return "X_QueryFont";
+ }
+ case X_QueryTextExtents:
+ {
+ return "X_QueryTextExtents";
+ }
+ case X_ListFonts:
+ {
+ return "X_ListFonts";
+ }
+ case X_ListFontsWithInfo:
+ {
+ return "X_ListFontsWithInfo";
+ }
+ case X_SetFontPath:
+ {
+ return "X_SetFontPath";
+ }
+ case X_GetFontPath:
+ {
+ return "X_GetFontPath";
+ }
+ case X_CreatePixmap:
+ {
+ return "X_CreatePixmap";
+ }
+ case X_FreePixmap:
+ {
+ return "X_FreePixmap";
+ }
+ case X_CreateGC:
+ {
+ return "X_CreateGC";
+ }
+ case X_ChangeGC:
+ {
+ return "X_ChangeGC";
+ }
+ case X_CopyGC:
+ {
+ return "X_CopyGC";
+ }
+ case X_SetDashes:
+ {
+ return "X_SetDashes";
+ }
+ case X_SetClipRectangles:
+ {
+ return "X_SetClipRectangles";
+ }
+ case X_FreeGC:
+ {
+ return "X_FreeGC";
+ }
+ case X_ClearArea:
+ {
+ return "X_ClearArea";
+ }
+ case X_CopyArea:
+ {
+ return "X_CopyArea";
+ }
+ case X_CopyPlane:
+ {
+ return "X_CopyPlane";
+ }
+ case X_PolyPoint:
+ {
+ return "X_PolyPoint";
+ }
+ case X_PolyLine:
+ {
+ return "X_PolyLine";
+ }
+ case X_PolySegment:
+ {
+ return "X_PolySegment";
+ }
+ case X_PolyRectangle:
+ {
+ return "X_PolyRectangle";
+ }
+ case X_PolyArc:
+ {
+ return "X_PolyArc";
+ }
+ case X_FillPoly:
+ {
+ return "X_FillPoly";
+ }
+ case X_PolyFillRectangle:
+ {
+ return "X_PolyFillRectangle";
+ }
+ case X_PolyFillArc:
+ {
+ return "X_PolyFillArc";
+ }
+ case X_PutImage:
+ {
+ return "X_PutImage";
+ }
+ case X_GetImage:
+ {
+ return "X_GetImage";
+ }
+ case X_PolyText8:
+ {
+ return "X_PolyText8";
+ }
+ case X_PolyText16:
+ {
+ return "X_PolyText16";
+ }
+ case X_ImageText8:
+ {
+ return "X_ImageText8";
+ }
+ case X_ImageText16:
+ {
+ return "X_ImageText16";
+ }
+ case X_CreateColormap:
+ {
+ return "X_CreateColormap";
+ }
+ case X_FreeColormap:
+ {
+ return "X_FreeColormap";
+ }
+ case X_CopyColormapAndFree:
+ {
+ return "X_CopyColormapAndFree";
+ }
+ case X_InstallColormap:
+ {
+ return "X_InstallColormap";
+ }
+ case X_UninstallColormap:
+ {
+ return "X_UninstallColormap";
+ }
+ case X_ListInstalledColormaps:
+ {
+ return "X_ListInstalledColormaps";
+ }
+ case X_AllocColor:
+ {
+ return "X_AllocColor";
+ }
+ case X_AllocNamedColor:
+ {
+ return "X_AllocNamedColor";
+ }
+ case X_AllocColorCells:
+ {
+ return "X_AllocColorCells";
+ }
+ case X_AllocColorPlanes:
+ {
+ return "X_AllocColorPlanes";
+ }
+ case X_FreeColors:
+ {
+ return "X_FreeColors";
+ }
+ case X_StoreColors:
+ {
+ return "X_StoreColors";
+ }
+ case X_StoreNamedColor:
+ {
+ return "X_StoreNamedColor";
+ }
+ case X_QueryColors:
+ {
+ return "X_QueryColors";
+ }
+ case X_LookupColor:
+ {
+ return "X_LookupColor";
+ }
+ case X_CreateCursor:
+ {
+ return "X_CreateCursor";
+ }
+ case X_CreateGlyphCursor:
+ {
+ return "X_CreateGlyphCursor";
+ }
+ case X_FreeCursor:
+ {
+ return "X_FreeCursor";
+ }
+ case X_RecolorCursor:
+ {
+ return "X_RecolorCursor";
+ }
+ case X_QueryBestSize:
+ {
+ return "X_QueryBestSize";
+ }
+ case X_QueryExtension:
+ {
+ return "X_QueryExtension";
+ }
+ case X_ListExtensions:
+ {
+ return "X_ListExtensions";
+ }
+ case X_ChangeKeyboardMapping:
+ {
+ return "X_ChangeKeyboardMapping";
+ }
+ case X_GetKeyboardMapping:
+ {
+ return "X_GetKeyboardMapping";
+ }
+ case X_ChangeKeyboardControl:
+ {
+ return "X_ChangeKeyboardControl";
+ }
+ case X_GetKeyboardControl:
+ {
+ return "X_GetKeyboardControl";
+ }
+ case X_Bell:
+ {
+ return "X_Bell";
+ }
+ case X_ChangePointerControl:
+ {
+ return "X_ChangePointerControl";
+ }
+ case X_GetPointerControl:
+ {
+ return "X_GetPointerControl";
+ }
+ case X_SetScreenSaver:
+ {
+ return "X_SetScreenSaver";
+ }
+ case X_GetScreenSaver:
+ {
+ return "X_GetScreenSaver";
+ }
+ case X_ChangeHosts:
+ {
+ return "X_ChangeHosts";
+ }
+ case X_ListHosts:
+ {
+ return "X_ListHosts";
+ }
+ case X_SetAccessControl:
+ {
+ return "X_SetAccessControl";
+ }
+ case X_SetCloseDownMode:
+ {
+ return "X_SetCloseDownMode";
+ }
+ case X_KillClient:
+ {
+ return "X_KillClient";
+ }
+ case X_RotateProperties:
+ {
+ return "X_RotateProperties";
+ }
+ case X_ForceScreenSaver:
+ {
+ return "X_ForceScreenSaver";
+ }
+ case X_SetPointerMapping:
+ {
+ return "X_SetPointerMapping";
+ }
+ case X_GetPointerMapping:
+ {
+ return "X_GetPointerMapping";
+ }
+ case X_SetModifierMapping:
+ {
+ return "X_SetModifierMapping";
+ }
+ case X_GetModifierMapping:
+ {
+ return "X_GetModifierMapping";
+ }
+ case X_NoOperation:
+ {
+ return "X_NoOperation";
+ }
+ case X_NXInternalGenericData:
+ {
+ return "X_NXInternalGenericData";
+ }
+ //
+ // case X_NXInternalGenericReply:
+ // {
+ // return "X_NXInternalGenericReply";
+ // }
+ //
+ case X_NXInternalGenericRequest:
+ {
+ return "X_NXInternalGenericRequest";
+ }
+ case X_NXInternalShapeExtension:
+ {
+ return "X_NXInternalShapeExtension";
+ }
+ case X_NXGetControlParameters:
+ {
+ return "X_NXGetControlParameters";
+ }
+ case X_NXGetCleanupParameters:
+ {
+ return "X_NXGetCleanupParameters";
+ }
+ case X_NXGetImageParameters:
+ {
+ return "X_NXGetImageParameters";
+ }
+ case X_NXGetUnpackParameters:
+ {
+ return "X_NXGetUnpackParameters";
+ }
+ case X_NXGetShmemParameters:
+ {
+ return "X_NXGetShmemParameters";
+ }
+ case X_NXGetFontParameters:
+ {
+ return "X_NXGetFontParameters";
+ }
+ case X_NXSetExposeParameters:
+ {
+ return "X_NXSetExposeParameters";
+ }
+ case X_NXSetCacheParameters:
+ {
+ return "X_NXSetCacheParameters";
+ }
+ case X_NXStartSplit:
+ {
+ return "X_NXStartSplit";
+ }
+ case X_NXEndSplit:
+ {
+ return "X_NXEndSplit";
+ }
+ case X_NXSplitData:
+ {
+ return "X_NXSplitData";
+ }
+ case X_NXSplitEvent:
+ {
+ return "X_NXSplitEvent";
+ }
+ case X_NXCommitSplit:
+ {
+ return "X_NXCommitSplit";
+ }
+ case X_NXFinishSplit:
+ {
+ return "X_NXFinishSplit";
+ }
+ case X_NXAbortSplit:
+ {
+ return "X_NXAbortSplit";
+ }
+ case X_NXFreeSplit:
+ {
+ return "X_NXFreeSplit";
+ }
+ case X_NXSetUnpackGeometry:
+ {
+ return "X_NXSetUnpackGeometry";
+ }
+ case X_NXSetUnpackColormap:
+ {
+ return "X_NXSetUnpackColormap";
+ }
+ case X_NXSetUnpackAlpha:
+ {
+ return "X_NXSetUnpackAlpha";
+ }
+ case X_NXPutPackedImage:
+ {
+ return "X_NXPutPackedImage";
+ }
+ case X_NXFreeUnpack:
+ {
+ return "X_NXFreeUnpack";
+ }
+ default:
+ {
+ if (opcode > 127)
+ {
+ return "Extension";
+ }
+ else
+ {
+ return "?";
+ }
+ }
+ }
+}
+
+#else /* #ifdef OPCODES */
+
+const char *DumpOpcode(const int &opcode)
+{
+ return "?";
+}
+
+#endif /* #ifdef OPCODES */
+
+void DumpData(const unsigned char *buffer, unsigned int size)
+{
+ if (buffer != NULL)
+ {
+ unsigned int i = 0;
+
+ while (i < size)
+ {
+ *logofs << "[" << i << "]\t";
+
+ for (unsigned int ii = 0; i < size && ii < 8; i++, ii++)
+ {
+ *logofs << (unsigned int) (buffer[i]) << "\t";
+ }
+
+ *logofs << "\n" << logofs_flush;
+ }
+ }
+}
+
+void DumpChecksum(const unsigned char *buffer, unsigned int size)
+{
+ if (buffer != NULL)
+ {
+ md5_byte_t md5_digest[MD5_LENGTH];
+
+ md5_state_t md5_state;
+
+ md5_init(&md5_state);
+
+ md5_append(&md5_state, buffer, size);
+
+ md5_finish(&md5_state, md5_digest);
+
+ char md5_string[MD5_LENGTH * 2 + 1];
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ sprintf(md5_string + (i * 2), "%02X", md5_digest[i]);
+ }
+
+ *logofs << "[" << md5_string << "]" << logofs_flush;
+ }
+}
+
+void DumpBlockChecksums(const unsigned char *buffer,
+ unsigned int size, unsigned int block)
+{
+ for (unsigned int i = 0; i < (size / block); i++)
+ {
+ *logofs << "[" << i * block << "]";
+
+ DumpChecksum(buffer + (i * block), block);
+
+ *logofs << "\n";
+ }
+
+ if (size % block > 0)
+ {
+ *logofs << "[" << size / block * block << "]";
+
+ DumpChecksum(buffer + (size / block * block), size % block);
+
+ *logofs << "\n";
+ }
+}
+
+void DumpHexData(const unsigned char *buffer, unsigned int size)
+{
+ char message [65536];
+ char ascii [17];
+
+ unsigned int index = 0;
+ unsigned int linescan = 0;
+ unsigned int index_ascii = 0;
+
+ sprintf (message,"\n#### Start Dump Buffer of [%.5d] Bytes ####\n\n",size);
+
+ *logofs << message << logofs_flush;
+
+ //
+ // "Index 0 1 2 3 4 5 6 7 8 9 a b c d e f Ascii "
+ // "----- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----------------"
+ // "00000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................"
+ //
+
+ sprintf (message,"Index 0 1 2 3 4 5 6 7 8 9 a b c d e f Ascii \n");
+ *logofs << message << logofs_flush;
+ sprintf (message,"----- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ----------------\n");
+ *logofs << message << logofs_flush;
+
+ index = 0;
+
+ while (index < size)
+ {
+ memset (ascii, ' ', sizeof(ascii));
+
+ ascii[16] = '\0';
+
+ sprintf (message,"%.5d ", index);
+
+ for (index_ascii = 0, linescan = index;
+ ((index < (linescan + 16)) && (index < size));
+ index++, index_ascii++)
+ {
+ if (isprint(buffer [index]))
+ {
+ ascii[index_ascii] = buffer [index];
+ }
+ else
+ {
+ ascii[index_ascii] = '.';
+ }
+
+ sprintf (&message [strlen (message)],"%.2x ", (unsigned char) buffer [index]);
+ }
+
+ for (linescan = index_ascii; linescan < 16; linescan++)
+ {
+ strcat (&message [strlen (message)], " ");
+ }
+
+ sprintf (&message [strlen (message)]," %s\n", ascii);
+
+ *logofs << message << logofs_flush;
+ }
+
+ sprintf (message,"\n#### End Dump Buffer ####\n\n");
+
+ *logofs << message << logofs_flush;
+}
diff --git a/nxcomp/src/Misc.h b/nxcomp/src/Misc.h
new file mode 100644
index 000000000..997630137
--- /dev/null
+++ b/nxcomp/src/Misc.h
@@ -0,0 +1,279 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Misc_H
+#define Misc_H
+
+#include <iostream>
+#include <fstream>
+
+#include <cerrno>
+#include <cstring>
+
+#ifdef __sun
+
+#include <strings.h>
+
+#endif
+
+using namespace std;
+
+//
+// This is MD5 length.
+//
+
+#define MD5_LENGTH 16
+
+//
+// Error handling macros.
+//
+
+#define ESET(e) (errno = (e))
+#define EGET() (errno)
+#define ESTR() strerror(errno)
+
+//
+// TCP port offset applied to NX port specification.
+//
+
+extern const int DEFAULT_NX_PROXY_PORT_OFFSET;
+
+//
+// Default TCP port used by client proxy to listen
+// to X clients and by server proxy to connect to
+// remote.
+//
+
+extern const int DEFAULT_NX_PROXY_PORT;
+
+//
+// Default X display number that client
+// proxy imitates.
+//
+
+extern const int DEFAULT_NX_X_PORT;
+
+//
+// Establish the port offsets for the additional
+// services.
+//
+
+extern const int DEFAULT_NX_CUPS_PORT_OFFSET;
+extern const int DEFAULT_NX_SMB_PORT_OFFSET;
+extern const int DEFAULT_NX_MEDIA_PORT_OFFSET;
+extern const int DEFAULT_NX_AUX_PORT_OFFSET;
+extern const int DEFAULT_NX_HTTP_PORT_OFFSET;
+extern const int DEFAULT_NX_FONT_PORT_OFFSET;
+
+//
+// Slave channels can be originated by both sides
+// so they need to have different port offsets
+// in the case the user runs both proxies on the
+// same host.
+//
+
+extern const int DEFAULT_NX_SLAVE_PORT_CLIENT_OFFSET;
+extern const int DEFAULT_NX_SLAVE_PORT_SERVER_OFFSET;
+
+//
+// NX proxy binds to all network interfaces by default
+// With the -loopback parameter, you can switch
+// over to binding to the loopback device only.
+//
+
+extern const int DEFAULT_LOOPBACK_BIND;
+
+//
+// Return strings containing various info.
+//
+
+const char *GetUsageInfo();
+const char *GetCopyrightInfo();
+const char *GetOtherCopyrightInfo();
+
+//
+// Define this if you want immediate flush of
+// the log output.
+//
+
+#define FLUSH_LOGOFS
+
+//
+// Global objects providing shared functions.
+//
+
+class Auth;
+class Control;
+class Statistics;
+
+extern Auth *auth;
+extern Control *control;
+extern Statistics *statistics;
+
+//
+// Log file.
+//
+
+extern ostream *logofs;
+
+//
+// Cleanup code.
+//
+
+void HandleAbort() __attribute__((noreturn));
+void HandleShutdown() __attribute__((noreturn));
+
+extern "C"
+{
+ void HandleCleanup(int code = 0) __attribute__((noreturn));
+ void HandleCleanupForReconnect();
+}
+
+//
+// Manage signal handlers.
+//
+
+void DisableSignals();
+void EnableSignals();
+
+//
+// Manage timers.
+//
+
+void SetTimer(int value);
+void ResetTimer();
+
+//
+// Show a dialog asking the user if he/she
+// wants to close the current session. Look
+// in the alerts file for the known critical
+// events.
+//
+
+void HandleAlert(int code, int local);
+
+//
+// Run the callback registered by the proxy
+// or the agent.
+//
+
+void KeeperCallback();
+void FlushCallback(int length);
+
+//
+// Return the string literal corresponding
+// the value.
+//
+
+const char *DumpSignal(int signal);
+const char *DumpPolicy(int type);
+const char *DumpControl(int code);
+const char *DumpSession(int code);
+const char *DumpAction(int type);
+const char *DumpState(int type);
+const char *DumpToken(int type);
+
+//
+// Print out content of buffer to log file.
+// You need to define DUMP or OPCODES in
+// the source to have these compiled.
+//
+
+const char *DumpOpcode(const int &opcode);
+const char *DumpChecksum(const void *checksum);
+
+void DumpData(const unsigned char *data, unsigned int length);
+void DumpHexData(const unsigned char *data, unsigned int length);
+void DumpChecksum(const unsigned char *data, unsigned int length);
+void DumpBlockChecksums(const unsigned char *data, unsigned int length,
+ unsigned int block);
+
+//
+// Defines logofs_flush as an empty string to
+// avoid calling the corresponding ostream's
+// flush() function.
+//
+
+#ifdef FLUSH_LOGOFS
+
+#define logofs_flush "" ; logofs -> flush()
+
+#else
+
+#define logofs_flush ""
+
+#endif
+
+//
+// Is the host where local proxy is running
+// big-endian?
+//
+
+extern int _hostBigEndian;
+extern int _storeBigEndian;
+
+inline void setHostBigEndian(int flag)
+{
+ _hostBigEndian = flag;
+}
+
+inline int hostBigEndian()
+{
+ return _hostBigEndian;
+}
+
+inline int storeBigEndian()
+{
+ return _storeBigEndian;
+}
+
+extern const unsigned int IntMask[33];
+
+unsigned int GetUINT(unsigned const char *buffer, int bigEndian);
+unsigned int GetULONG(unsigned const char *buffer, int bigEndian);
+void PutUINT(unsigned int value, unsigned char *buffer, int bigEndian);
+void PutULONG(unsigned int value, unsigned char *buffer, int bigEndian);
+
+inline void CleanData(unsigned char *buffer, int size)
+{
+ unsigned char *end = buffer + size;
+
+ while (buffer < end)
+ {
+ *buffer++ = 0x00;
+ }
+}
+
+int CheckData(istream *fs);
+int CheckData(ostream *fs);
+int PutData(ostream *fs, const unsigned char *buffer, int size);
+int GetData(istream *fs, unsigned char *buffer, int size);
+int FlushData(ostream *fs);
+
+unsigned int RoundUp2(unsigned int x);
+unsigned int RoundUp4(unsigned int x);
+unsigned int RoundUp8(unsigned int x);
+
+#endif /* Misc_H */
diff --git a/nxcomp/src/NXmitshm.h b/nxcomp/src/NXmitshm.h
new file mode 100644
index 000000000..939d488fb
--- /dev/null
+++ b/nxcomp/src/NXmitshm.h
@@ -0,0 +1,56 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef NXmitshm_H
+#define NXmitshm_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Import opcodes from <X11/extensions/XShm.h>
+ * to get rid of weird dependencies from other
+ * headers of X environment.
+ */
+
+#define X_ShmQueryVersion 0
+#define X_ShmAttach 1
+#define X_ShmDetach 2
+#define X_ShmPutImage 3
+#define X_ShmGetImage 4
+#define X_ShmCreatePixmap 5
+
+#define ShmCompletion 0
+#define ShmNumberEvents (ShmCompletion + 1)
+
+#define BadShmSeg 0
+#define ShmNumberErrors (BadShmSeg + 1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NXmitshm_H */
diff --git a/nxcomp/src/NXrender.h b/nxcomp/src/NXrender.h
new file mode 100644
index 000000000..280715833
--- /dev/null
+++ b/nxcomp/src/NXrender.h
@@ -0,0 +1,78 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef NXrender_H
+#define NXrender_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Import this from <X11/extensions/render.h>
+ * to compile under old XFree86 distributions
+ * when render extension was not present yet.
+ */
+
+#define X_RenderQueryVersion 0
+#define X_RenderQueryPictFormats 1
+#define X_RenderQueryPictIndexValues 2
+#define X_RenderQueryDithers 3
+#define X_RenderCreatePicture 4
+#define X_RenderChangePicture 5
+#define X_RenderSetPictureClipRectangles 6
+#define X_RenderFreePicture 7
+#define X_RenderComposite 8
+#define X_RenderScale 9
+#define X_RenderTrapezoids 10
+#define X_RenderTriangles 11
+#define X_RenderTriStrip 12
+#define X_RenderTriFan 13
+#define X_RenderColorTrapezoids 14
+#define X_RenderColorTriangles 15
+#define X_RenderTransform 16
+#define X_RenderCreateGlyphSet 17
+#define X_RenderReferenceGlyphSet 18
+#define X_RenderFreeGlyphSet 19
+#define X_RenderAddGlyphs 20
+#define X_RenderAddGlyphsFromPicture 21
+#define X_RenderFreeGlyphs 22
+#define X_RenderCompositeGlyphs8 23
+#define X_RenderCompositeGlyphs16 24
+#define X_RenderCompositeGlyphs32 25
+#define X_RenderFillRectangles 26
+/* 0.5 */
+#define X_RenderCreateCursor 27
+/* 0.6 */
+#define X_RenderSetPictureTransform 28
+#define X_RenderQueryFilters 29
+#define X_RenderSetPictureFilter 30
+#define X_RenderCreateAnimCursor 31
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NXrender_H */
diff --git a/nxcomp/src/OpcodeCache.h b/nxcomp/src/OpcodeCache.h
new file mode 100644
index 000000000..e07a1b997
--- /dev/null
+++ b/nxcomp/src/OpcodeCache.h
@@ -0,0 +1,53 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef OpcodeCache_H
+#define OpcodeCache_H
+
+#include "CharCache.h"
+
+class OpcodeCache
+{
+ friend class EncodeBuffer;
+ friend class DecodeBuffer;
+
+ public:
+
+ OpcodeCache()
+ {
+ slot_ = 0;
+ }
+
+ ~OpcodeCache()
+ {
+ }
+
+ private:
+
+ CharCache base_[256];
+ unsigned char slot_;
+};
+
+#endif /* OpcodeCache_H */
diff --git a/nxcomp/src/OpcodeStore.cpp b/nxcomp/src/OpcodeStore.cpp
new file mode 100644
index 000000000..21b919c8a
--- /dev/null
+++ b/nxcomp/src/OpcodeStore.cpp
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "OpcodeStore.h"
+
+OpcodeStore::OpcodeStore()
+{
+ //
+ // Assign values of the specific
+ // NX opcodes.
+ //
+
+ getControlParameters = X_NXGetControlParameters;
+ getCleanupParameters = X_NXGetCleanupParameters;
+ getImageParameters = X_NXGetImageParameters;
+ getUnpackParameters = X_NXGetUnpackParameters;
+ getShmemParameters = X_NXGetShmemParameters;
+ getFontParameters = X_NXGetFontParameters;
+
+ startSplit = X_NXStartSplit;
+ endSplit = X_NXEndSplit;
+ commitSplit = X_NXCommitSplit;
+ finishSplit = X_NXFinishSplit;
+ abortSplit = X_NXAbortSplit;
+
+ splitData = X_NXSplitData;
+ splitEvent = X_NXSplitEvent;
+
+ setCacheParameters = X_NXSetCacheParameters;
+ setExposeParameters = X_NXSetExposeParameters;
+
+ setUnpackGeometry = X_NXSetUnpackGeometry;
+ setUnpackColormap = X_NXSetUnpackColormap;
+ setUnpackAlpha = X_NXSetUnpackAlpha;
+
+ putPackedImage = X_NXPutPackedImage;
+
+ freeUnpack = X_NXFreeUnpack;
+ freeSplit = X_NXFreeSplit;
+
+ //
+ // These values must be fetched
+ // from the X server.
+ //
+
+ shapeExtension = 0;
+ renderExtension = 0;
+
+ //
+ // Events sent as ClientMessage.
+ //
+
+ noSplitNotify = NXNoSplitNotify;
+ startSplitNotify = NXStartSplitNotify;
+ commitSplitNotify = NXCommitSplitNotify;
+ endSplitNotify = NXEndSplitNotify;
+ emptySplitNotify = NXEmptySplitNotify;
+}
+
+OpcodeStore::~OpcodeStore()
+{
+}
diff --git a/nxcomp/src/OpcodeStore.h b/nxcomp/src/OpcodeStore.h
new file mode 100644
index 000000000..d041ed4b8
--- /dev/null
+++ b/nxcomp/src/OpcodeStore.h
@@ -0,0 +1,91 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef OpcodeStore_H
+#define OpcodeStore_H
+
+#include "NXproto.h"
+
+class OpcodeStore
+{
+ public:
+
+ OpcodeStore();
+
+ ~OpcodeStore();
+
+ //
+ // Map NX protocol messages. At the moment mapping is hard-
+ // coded. Opcodes should be instead agreed with the proxied
+ // X server (by excluding all opcodes used for extensions)
+ // and exported by the proxy class to channels.
+ //
+ // Some toolkits query the server only once for extensions'
+ // opcodes and share the same settings across all channels.
+ // This could be a problem as channels needed to monitor the
+ // traffic to find out the extensions' opcodes themselves,
+ // so it is important that proxy passes an instance of this
+ // class to new channels.
+ //
+
+ unsigned char getControlParameters;
+ unsigned char getCleanupParameters;
+ unsigned char getImageParameters;
+ unsigned char getUnpackParameters;
+ unsigned char getShmemParameters;
+ unsigned char getFontParameters;
+
+ unsigned char startSplit;
+ unsigned char endSplit;
+ unsigned char commitSplit;
+ unsigned char finishSplit;
+ unsigned char abortSplit;
+
+ unsigned char splitData;
+ unsigned char splitEvent;
+
+ unsigned char setCacheParameters;
+ unsigned char setExposeParameters;
+
+ unsigned char setUnpackGeometry;
+ unsigned char setUnpackColormap;
+ unsigned char setUnpackAlpha;
+
+ unsigned char putPackedImage;
+
+ unsigned char freeUnpack;
+ unsigned char freeSplit;
+
+ unsigned char shapeExtension;
+ unsigned char renderExtension;
+
+ unsigned char noSplitNotify;
+ unsigned char startSplitNotify;
+ unsigned char commitSplitNotify;
+ unsigned char endSplitNotify;
+ unsigned char emptySplitNotify;
+};
+
+#endif /* OpcodeStore_H */
diff --git a/nxcomp/src/Pack.c b/nxcomp/src/Pack.c
new file mode 100644
index 000000000..97fb93b5f
--- /dev/null
+++ b/nxcomp/src/Pack.c
@@ -0,0 +1,180 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+
+#include "NXpack.h"
+
+const ColorMask Mask8TrueColor = { 128, 63, 240, 7 };
+const ColorMask Mask64TrueColor = { 192, 7, 240, 4 };
+const ColorMask Mask256TrueColor = { 255, 0, 255, 0 };
+const ColorMask Mask512TrueColor = { 224, 5, 240, 4 };
+const ColorMask Mask4KTrueColor = { 240, 4, 240, 2 };
+const ColorMask Mask32KTrueColor = { 248, 3, 248, 2 };
+const ColorMask Mask64KTrueColor = { 255, 0, 255, 0 };
+const ColorMask Mask256KTrueColor = { 252, 1, 252, 1 };
+const ColorMask Mask2MTrueColor = { 255, 0, 254, 1 };
+const ColorMask Mask16MTrueColor = { 255, 0, 255, 0 };
+
+const ColorMask *MethodColorMask(unsigned int method)
+{
+ switch (method)
+ {
+ case MASK_8_COLORS:
+ {
+ return &Mask8TrueColor;
+ }
+ case MASK_64_COLORS:
+ {
+ return &Mask64TrueColor;
+ }
+ case MASK_256_COLORS:
+ {
+ return &Mask256TrueColor;
+ }
+ case MASK_512_COLORS:
+ {
+ return &Mask512TrueColor;
+ }
+ case MASK_4K_COLORS:
+ {
+ return &Mask4KTrueColor;
+ }
+ case MASK_32K_COLORS:
+ {
+ return &Mask32KTrueColor;
+ }
+ case MASK_64K_COLORS:
+ {
+ return &Mask64KTrueColor;
+ }
+ case MASK_256K_COLORS:
+ {
+ return &Mask256KTrueColor;
+ }
+ case MASK_2M_COLORS:
+ {
+ return &Mask2MTrueColor;
+ }
+ case MASK_16M_COLORS:
+ {
+ return &Mask16MTrueColor;
+ }
+ default:
+ {
+ return NULL;
+ }
+ }
+}
+
+int MethodBitsPerPixel(unsigned int method)
+{
+ switch (method)
+ {
+ case PACK_MASKED_8_COLORS:
+ case PACK_JPEG_8_COLORS:
+ case PACK_PNG_8_COLORS:
+ {
+ return 8;
+ }
+ case PACK_MASKED_64_COLORS:
+ case PACK_JPEG_64_COLORS:
+ case PACK_PNG_64_COLORS:
+ {
+ return 8;
+ }
+ case PACK_MASKED_256_COLORS:
+ case PACK_JPEG_256_COLORS:
+ case PACK_PNG_256_COLORS:
+ {
+ return 8;
+ }
+ case PACK_MASKED_512_COLORS:
+ case PACK_JPEG_512_COLORS:
+ case PACK_PNG_512_COLORS:
+ {
+ return 16;
+ }
+ case PACK_MASKED_4K_COLORS:
+ case PACK_JPEG_4K_COLORS:
+ case PACK_PNG_4K_COLORS:
+ {
+ return 16;
+ }
+ case PACK_MASKED_32K_COLORS:
+ case PACK_JPEG_32K_COLORS:
+ case PACK_PNG_32K_COLORS:
+ {
+ return 16;
+ }
+ case PACK_MASKED_64K_COLORS:
+ case PACK_JPEG_64K_COLORS:
+ case PACK_PNG_64K_COLORS:
+ {
+ return 16;
+ }
+ case PACK_MASKED_256K_COLORS:
+ case PACK_JPEG_256K_COLORS:
+ case PACK_PNG_256K_COLORS:
+ {
+ return 24;
+ }
+ case PACK_MASKED_2M_COLORS:
+ case PACK_JPEG_2M_COLORS:
+ case PACK_PNG_2M_COLORS:
+ {
+ return 24;
+ }
+ case PACK_MASKED_16M_COLORS:
+ case PACK_JPEG_16M_COLORS:
+ case PACK_PNG_16M_COLORS:
+ {
+ return 24;
+ }
+ case PACK_BITMAP_16M_COLORS:
+ case PACK_RGB_16M_COLORS:
+ case PACK_RLE_16M_COLORS:
+ {
+ return 24;
+ }
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/nxcomp/src/Pgn.cpp b/nxcomp/src/Pgn.cpp
new file mode 100644
index 000000000..649227f52
--- /dev/null
+++ b/nxcomp/src/Pgn.cpp
@@ -0,0 +1,809 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// This file obviously supports PNG
+// decompression. It was renamed to
+// avoid name clashes on Windows.
+//
+
+#include <X11/Xmd.h>
+
+#ifdef ANDROID
+#include <strings.h>
+#endif
+#include <unistd.h>
+#include <stdio.h>
+#include <png.h>
+
+#include "Unpack.h"
+#include "Pgn.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#define RGB24_TO_PIXEL(bpp,r,g,b) \
+ ((((CARD##bpp)(r) & 0xFF) * srcRedMax2 + 127) / 255 \
+ << srcRedShift2 | \
+ (((CARD##bpp)(g) & 0xFF) * srcGreenMax2 + 127) / 255 \
+ << srcGreenShift2 | \
+ (((CARD##bpp)(b) & 0xFF) * srcBlueMax2 + 127) / 255 \
+ << srcBlueShift2)
+
+#define RGB24_TO_PIXEL32(r,g,b) \
+ (((CARD32)(r) & 0xFF) << srcRedShift2 | \
+ ((CARD32)(g) & 0xFF) << srcGreenShift2 | \
+ ((CARD32)(b) & 0xFF) << srcBlueShift2)
+
+//
+// Functions from Unpack.cpp
+//
+
+extern int Unpack32To32(const T_colormask *colormask, const unsigned int *data,
+ unsigned int *out, unsigned int *end);
+
+extern int Unpack24To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+extern int Unpack16To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+//
+// Local functions used for the png decompression.
+//
+
+static int DecompressPng16(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf,
+ int byteOrder);
+
+static int DecompressPng24(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf,
+ int byteOrder);
+
+static int DecompressPng32(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf,
+ int byteOrder);
+
+static void PngReadData(png_structp png_ptr, png_bytep data, png_size_t length);
+
+//
+// Colormap stuff.
+//
+
+CARD16 srcRedMax2, srcGreenMax2, srcBlueMax2;
+CARD8 srcRedShift2, srcGreenShift2, srcBlueShift2;
+
+//
+// Attributes used for the png decompression.
+//
+
+static char *tmpBuf;
+static int tmpBufSize = 0;
+static int streamPos;
+
+int UnpackPng(T_geometry *geometry, unsigned char method, unsigned char *srcData,
+ int srcSize, int dstBpp, int dstWidth, int dstHeight,
+ unsigned char *dstData, int dstSize)
+{
+ int byteOrder = geometry -> image_byte_order;
+
+ //
+ // Check if data is coming from a failed unsplit.
+ //
+
+ if (srcSize < 2 || (srcData[0] == SPLIT_PATTERN &&
+ srcData[1] == SPLIT_PATTERN))
+ {
+ #ifdef WARNING
+ *logofs << "UnpackPng: WARNING! Skipping unpack of dummy data.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "UnpackPng: Decompressing image with "
+ << "srcSize " << srcSize << " and bpp "
+ << dstBpp << ".\n" << logofs_flush;
+ #endif
+
+ srcRedShift2 = ffs(geometry -> red_mask) - 1;
+ srcGreenShift2 = ffs(geometry -> green_mask) - 1;
+ srcBlueShift2 = ffs(geometry -> blue_mask) - 1;
+ srcRedMax2 = geometry -> red_mask >> srcRedShift2;
+ srcGreenMax2 = geometry -> green_mask >> srcGreenShift2;
+ srcBlueMax2 = geometry -> blue_mask >> srcBlueShift2;
+
+ //
+ // Make enough space in the temporary
+ // buffer to have one complete row of
+ // the image with 3 bytes per pixel.
+ //
+
+ tmpBufSize = dstWidth * 3;
+ tmpBuf = new char [tmpBufSize];
+
+ if (tmpBuf == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackPng: PANIC! Cannot allocate "
+ << dstWidth * 3 << " bytes for PNG "
+ << "decompressed data.\n" << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ return -1;
+ }
+
+ int result = 1;
+
+ switch (dstBpp)
+ {
+ case 8:
+ {
+ //
+ // Simply move the data from srcData to dstData
+ // taking into consideration the correct padding.
+ //
+
+ int row;
+
+ unsigned char * dstBuff = dstData;
+ unsigned char * srcBuff = srcData;
+
+ for (row = 0; row < dstHeight; row++)
+ {
+ memcpy(dstBuff, srcBuff, dstWidth );
+
+ dstBuff += RoundUp4(dstWidth);
+ srcBuff += dstWidth;
+ }
+ }
+ case 16:
+ {
+ result = DecompressPng16(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ case 24:
+ {
+ result = DecompressPng24(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ case 32:
+ {
+ result = DecompressPng32(srcData, srcSize, dstWidth,
+ dstHeight, dstData, byteOrder);
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "UnpackPng: PANIC! Error in PNG compression. "
+ << " Unsupported Bpp value " << dstBpp
+ << " for the PNG compression"
+ << ".\n" << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ result = -1;
+ }
+ }
+
+ if (result == -1)
+ {
+ delete [] tmpBuf;
+
+ return result;
+ }
+
+ //
+ // Apply the correction for the brightness
+ //
+
+ int maskMethod;
+
+ switch (method)
+ {
+ case PACK_PNG_8_COLORS:
+ {
+ maskMethod = MASK_8_COLORS;
+
+ break;
+ }
+ case PACK_PNG_64_COLORS:
+ {
+ maskMethod = MASK_64_COLORS;
+
+ break;
+ }
+ case PACK_PNG_256_COLORS:
+ {
+ maskMethod = MASK_256_COLORS;
+
+ break;
+ }
+ case PACK_PNG_512_COLORS:
+ {
+ maskMethod = MASK_512_COLORS;
+
+ break;
+ }
+ case PACK_PNG_4K_COLORS:
+ {
+ maskMethod = MASK_4K_COLORS;
+
+ break;
+ }
+ case PACK_PNG_32K_COLORS:
+ {
+ maskMethod = MASK_32K_COLORS;
+
+ break;
+ }
+ case PACK_PNG_64K_COLORS:
+ {
+ maskMethod = MASK_64K_COLORS;
+
+ break;
+ }
+ case PACK_PNG_256K_COLORS:
+ {
+ maskMethod = MASK_256K_COLORS;
+
+ break;
+ }
+ case PACK_PNG_2M_COLORS:
+ {
+ maskMethod = MASK_2M_COLORS;
+
+ break;
+ }
+ case PACK_PNG_16M_COLORS:
+ {
+ maskMethod = MASK_16M_COLORS;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << " No matching decompression method.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ return -1;
+ }
+ }
+
+ const T_colormask *colorMask = MethodColorMask(maskMethod);
+
+ unsigned char *dstBuff = dstData;
+
+ switch (dstBpp)
+ {
+ case 16:
+ {
+ Unpack16To16(colorMask, dstBuff, dstBuff, dstBuff + dstSize);
+
+ break;
+ }
+ case 24:
+ {
+ break;
+ }
+ case 32:
+ {
+ Unpack32To32(colorMask, (unsigned int *)dstBuff, (unsigned int *)dstBuff,
+ (unsigned int *)(dstBuff + dstSize));
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << " No matching destination bits per plane.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] tmpBuf;
+
+ return -1;
+ }
+ }
+
+ delete [] tmpBuf;
+
+ return 1;
+}
+
+
+//
+// Functions that actually do
+// the PNG decompression.
+//
+
+int DecompressPng16(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ unsigned char *data;
+ unsigned int dx, dy;
+
+ png_structp pngPtr;
+ png_infop infoPtr;
+ png_bytep rowPointers;
+
+
+ streamPos = 0;
+
+ pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+ if (!pngPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << " Failed png_create_read_struct operation"
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ infoPtr = png_create_info_struct(pngPtr);
+
+ if (!infoPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << "Failed png_create_info_struct operation"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, NULL, NULL);
+
+ return -1;
+ }
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << "Error during IO initialization"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_set_read_fn(pngPtr, (void *)compressedData, PngReadData);
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << "Error during read of PNG header"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_read_info(pngPtr, infoPtr);
+
+ if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_set_expand(pngPtr);
+ }
+
+ //
+ // data points to dstBuf which is
+ // already padded correctly for the final
+ // image to put
+ //
+
+ data = dstBuf;
+ rowPointers = (png_byte *) tmpBuf;
+
+ //
+ // We use setjmp() to save our context.
+ // The PNG library will call longjmp()
+ // in case of error.
+ //
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng16: PANIC! "
+ << "Error during read of PNG rows"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ unsigned long pixel;
+
+ for (dy = 0; dy < h; dy++)
+ {
+ png_read_row(pngPtr, rowPointers, NULL);
+
+ for (dx = 0; dx < w; dx++)
+ {
+ pixel = RGB24_TO_PIXEL(16, tmpBuf[dx*3], tmpBuf[dx*3+1], tmpBuf[dx*3+2]);
+
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ data[0] = (unsigned char) (pixel & 0xff);
+ data[1] = (unsigned char) ((pixel >> 8) & 0xff);
+ }
+ else
+ {
+ data[1] = (unsigned char) (pixel & 0xff);
+ data[0] = (unsigned char) ((pixel >> 8) & 0xff);
+ }
+
+ data += 2;
+ }
+
+ //
+ // Move pixelPtr at the beginning of the
+ // next line.
+ //
+
+ data = data + (RoundUp4(w * 2) - w * 2);
+ }
+
+ png_destroy_read_struct(&pngPtr, &infoPtr,NULL);
+
+ #ifdef DEBUG
+ *logofs << "DecompressPng16: Decompression finished."
+ << dy << " lines handled.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int DecompressPng24(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ static CARD8 *pixelPtr = NULL;
+ unsigned int dx, dy;
+
+ png_structp pngPtr;
+ png_infop infoPtr;
+ png_bytep rowPointers;
+
+
+ streamPos = 0;
+
+ pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+ if (!pngPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng24: PANIC! "
+ << "Failed png_create_read_struct operation"
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ infoPtr = png_create_info_struct(pngPtr);
+
+ if (!infoPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng24: PANIC! "
+ << "Failed png_create_info_struct operation"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, NULL, NULL);
+
+ return -1;
+ }
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng24: PANIC! "
+ << "Error during IO initialization"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_set_read_fn(pngPtr, (void *)compressedData, PngReadData);
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng24: PANIC! "
+ << "Error during read of PNG header"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_read_info( pngPtr, infoPtr ) ;
+
+ if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_set_expand(pngPtr);
+ }
+
+ //
+ // PixelPtr points to dstBuf which is
+ // already padded correctly for the final
+ // image to put
+ //
+
+ pixelPtr = (CARD8 *) dstBuf;
+
+ rowPointers = (png_byte *)tmpBuf;
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng24: PANIC! "
+ << "Error during read of PNG rows"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ for (dy = 0; dy < h; dy++)
+ {
+ png_read_row(pngPtr, rowPointers, NULL);
+
+ for (dx = 0; dx < w; dx++)
+ {
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ pixelPtr[0] = tmpBuf[dx * 3];
+ pixelPtr[1] = tmpBuf[dx * 3 + 1];
+ pixelPtr[2] = tmpBuf[dx * 3 + 2];
+ }
+ else
+ {
+ pixelPtr[2] = tmpBuf[dx * 3];
+ pixelPtr[1] = tmpBuf[dx * 3 + 1];
+ pixelPtr[0] = tmpBuf[dx * 3 + 2];
+ }
+
+ pixelPtr += 3;
+ }
+
+ //
+ // Go to the next line.
+ //
+
+ pixelPtr = (CARD8 *) (((char *) pixelPtr) + (RoundUp4(w * 3) - w * 3));
+ }
+
+ png_destroy_read_struct(&pngPtr, &infoPtr,NULL);
+
+ #ifdef DEBUG
+ *logofs << "DecompressPng24: Decompression finished."
+ << dy << " lines handled.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int DecompressPng32(unsigned char *compressedData, int compressedLen,
+ unsigned int w, unsigned int h, unsigned char *dstBuf, int byteOrder)
+{
+ unsigned char *data;
+
+ unsigned int dx, dy;
+
+ png_structp pngPtr;
+ png_infop infoPtr;
+ png_bytep rowPointers;
+
+ streamPos = 0;
+
+ pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+
+ if (!pngPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng32: PANIC! "
+ << "Failed png_create_read_struct operation"
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ infoPtr = png_create_info_struct(pngPtr);
+
+ if (!infoPtr)
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng32: PANIC! "
+ << "Failed png_create_info_struct operation."
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, NULL, NULL);
+
+ return -1;
+ }
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng32: PANIC! "
+ << "Error during IO initialization"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_set_read_fn(pngPtr, (void *)compressedData, PngReadData);
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng32: PANIC! "
+ << "Error during read of PNG header"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ png_read_info(pngPtr, infoPtr) ;
+
+
+ if (png_get_color_type(pngPtr, infoPtr) == PNG_COLOR_TYPE_PALETTE)
+ {
+ png_set_expand(pngPtr);
+ }
+
+ //
+ // data points to dstBuf which is
+ // already padded correctly for the final
+ // image to put
+ //
+
+ data = dstBuf;
+
+ rowPointers = (png_byte *) tmpBuf;
+
+ if (setjmp(png_jmpbuf(pngPtr)))
+ {
+ #ifdef PANIC
+ *logofs << "DecompressPng32: PANIC! "
+ << "Error during read of PNG rows"
+ << ".\n" << logofs_flush;
+ #endif
+
+ png_destroy_read_struct(&pngPtr, &infoPtr, NULL);
+
+ return -1;
+ }
+
+ unsigned long pixel;
+
+ int i;
+
+ for (dy = 0; dy < h; dy++)
+ {
+ png_read_row(pngPtr, rowPointers, NULL);
+
+ for (dx = 0; dx < w; dx++)
+ {
+ pixel = RGB24_TO_PIXEL(32, tmpBuf[dx * 3], tmpBuf[dx * 3 + 1],
+ tmpBuf[dx * 3 + 2]);
+
+ //
+ // Follow the server byte order when arranging data.
+ //
+
+ if (byteOrder == LSBFirst)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ data[i] = (unsigned char)(pixel & 0xff);
+ pixel >>= 8;
+ }
+ }
+ else
+ {
+ for (i = 3; i >= 0; i--)
+ {
+ data[i] = (unsigned char) (pixel & 0xff);
+ pixel >>= 8;
+ }
+ }
+
+ data += 4;
+ }
+ }
+
+ png_destroy_read_struct(&pngPtr, &infoPtr,NULL);
+
+ #ifdef DEBUG
+ *logofs << "DecompressPng32: Decompression finished."
+ << dy << " lines handled.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+static void PngReadData(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ memcpy((char *) data, (char *) png_get_io_ptr(png_ptr) + streamPos, length);
+
+ streamPos += length;
+}
diff --git a/nxcomp/src/Pgn.h b/nxcomp/src/Pgn.h
new file mode 100644
index 000000000..e5ea36715
--- /dev/null
+++ b/nxcomp/src/Pgn.h
@@ -0,0 +1,42 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Pgn_H
+#define Pgn_H
+
+//
+// This file obviously supports PNG
+// decompression. It was renamed to
+// avoid name clashes on Windows.
+//
+
+#include "Misc.h"
+#include "Unpack.h"
+
+int UnpackPng(T_geometry *geometry, unsigned char method, unsigned char *srcData,
+ int srcSize, int dstBpp, int dstWidth, int dstHeight,
+ unsigned char *dstData, int dstSize);
+
+#endif /* Pgn_H */
diff --git a/nxcomp/src/Pipe.cpp b/nxcomp/src/Pipe.cpp
new file mode 100644
index 000000000..4fa149412
--- /dev/null
+++ b/nxcomp/src/Pipe.cpp
@@ -0,0 +1,433 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <grp.h>
+
+#include "Pipe.h"
+#include "Misc.h"
+#include "Fork.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+extern void RegisterChild(int child);
+
+static int Psplit(const char *command, char *parameters[], int limit);
+
+//
+// These are slightly modified versions of popen(3) and pclose(3)
+// that don't rely on a shell to be available on the system, so
+// that they can also work on Windows. As an additional benefit,
+// these functions give up all privileges before running the com-
+// mand. Code is taken from the X distribution and, in turn, is
+// based on libc from FreeBSD 2.2.
+//
+
+static struct pid
+{
+ struct pid *next;
+ FILE *fp;
+ int self;
+
+} *pidlist;
+
+//
+// A very unsofisticated attempt to parse the command line and
+// split each parameter in distinct strings. This is not going
+// to work when dealing with parameters containing spaces, even
+// if they are enclosed in quotes.
+//
+
+int Psplit(const char *command, char *parameters[], int limit)
+{
+ char *line;
+ char *value;
+
+ int number;
+
+ //
+ // Preapare the list of parameters.
+ //
+
+ for (number = 0; number < limit; number++)
+ {
+ parameters[number] = NULL;
+ }
+
+ //
+ // Copy the command to get rid of the
+ // const qualifier.
+ //
+
+ line = new char[strlen(command) + 1];
+
+ if (line == NULL)
+ {
+ goto PsplitError;
+ }
+
+ strcpy(line, command);
+
+ number = 0;
+
+ value = strtok(line, " ");
+
+ while (value != NULL && number < limit)
+ {
+ #ifdef DEBUG
+ *logofs << "Psplit: Got parameter '" << value
+ << "'.\n" << logofs_flush;
+ #endif
+
+ parameters[number] = new char[strlen(value) + 1];
+
+ if (parameters[number] == NULL)
+ {
+ goto PsplitError;
+ }
+
+ strcpy(parameters[number], value);
+
+ number++;
+
+ //
+ // If this is the first parameter, then
+ // copy it in the second position and
+ // use it as the name of the command.
+ //
+
+ if (number == 1)
+ {
+ parameters[number] = new char[strlen(value) + 1];
+
+ if (parameters[number] == NULL)
+ {
+ goto PsplitError;
+ }
+
+ strcpy(parameters[number], value);
+
+ number++;
+ }
+
+ value = strtok(NULL, " ");
+ }
+
+ //
+ // Needs at least to have the command itself and
+ // the first argument, being again the name of
+ // the command.
+ //
+
+ if (number < 2)
+ {
+ goto PsplitError;
+ }
+
+ return number;
+
+PsplitError:
+
+ #ifdef PANIC
+ *logofs << "Psplit: PANIC! Can't split command line '"
+ << command << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't split command line '"
+ << command << "'.\n";
+
+ delete [] line;
+
+ return -1;
+}
+
+FILE *Popen(char * const parameters[], const char *type)
+{
+ FILE *iop;
+
+ struct pid *cur;
+ int pdes[2], pid;
+
+ if (parameters == NULL || type == NULL)
+ {
+ return NULL;
+ }
+
+ if ((*type != 'r' && *type != 'w') || type[1])
+ {
+ return NULL;
+ }
+
+ if ((cur = (struct pid *) malloc(sizeof(struct pid))) == NULL)
+ {
+ return NULL;
+ }
+
+ if (pipe(pdes) < 0)
+ {
+ free(cur);
+
+ return NULL;
+ }
+
+ //
+ // Block all signals until command is exited.
+ // We need to gather information about the
+ // child in Pclose().
+ //
+
+ DisableSignals();
+
+ switch (pid = Fork())
+ {
+ case -1:
+ {
+ //
+ // Error.
+ //
+
+ #ifdef PANIC
+ *logofs << "Popen: PANIC! Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Function fork failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ close(pdes[0]);
+ close(pdes[1]);
+
+ free(cur);
+
+ return NULL;
+ }
+ case 0:
+ {
+ //
+ // Child.
+ //
+
+ struct passwd *pwent = getpwuid(getuid());
+ if (pwent) initgroups(pwent->pw_name,getgid());
+ if (setgid(getgid()) == -1)
+ {
+ _exit(127);
+ }
+ if (setuid(getuid()) == -1)
+ {
+ _exit(127);
+ }
+
+ if (*type == 'r')
+ {
+ if (pdes[1] != 1)
+ {
+ //
+ // Set up stdout.
+ //
+
+ dup2(pdes[1], 1);
+ close(pdes[1]);
+ }
+
+ close(pdes[0]);
+ }
+ else
+ {
+ if (pdes[0] != 0)
+ {
+ //
+ // Set up stdin.
+ //
+
+ dup2(pdes[0], 0);
+ close(pdes[0]);
+ }
+
+ close(pdes[1]);
+ }
+
+ execvp(parameters[0], parameters + 1);
+
+ exit(127);
+ }
+ }
+
+ //
+ // Parent. Save data about the child.
+ //
+
+ RegisterChild(pid);
+
+ if (*type == 'r')
+ {
+ iop = fdopen(pdes[0], type);
+
+ close(pdes[1]);
+ }
+ else
+ {
+ iop = fdopen(pdes[1], type);
+
+ close(pdes[0]);
+ }
+
+ cur -> fp = iop;
+ cur -> self = pid;
+ cur -> next = pidlist;
+
+ pidlist = cur;
+
+ #ifdef TEST
+ *logofs << "Popen: Executing ";
+
+ for (int i = 0; i < 256 && parameters[i] != NULL; i++)
+ {
+ *logofs << "[" << parameters[i] << "]";
+ }
+
+ *logofs << " with descriptor " << fileno(iop)
+ << ".\n" << logofs_flush;
+ #endif
+
+ return iop;
+}
+
+FILE *Popen(const char *command, const char *type)
+{
+ char *parameters[256];
+
+ if (Psplit(command, parameters, 256) > 0)
+ {
+ FILE *file = Popen(parameters, type);
+
+ for (int i = 0; i < 256; i++)
+ {
+ delete [] parameters[i];
+ }
+
+ return file;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Popen: PANIC! Failed to parse command '"
+ << command << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to parse command '"
+ << command << "'.\n";
+
+ return NULL;
+ }
+}
+
+int Pclose(FILE *iop)
+{
+ struct pid *cur, *last;
+
+ int pstat;
+ int pid;
+
+ #ifdef TEST
+ *logofs << "Pclose: Closing command with output "
+ << "on descriptor " << fileno(iop) << ".\n"
+ << logofs_flush;
+ #endif
+
+ fclose((FILE *) iop);
+
+ for (last = NULL, cur = pidlist; cur; last = cur, cur = cur -> next)
+ {
+ if (cur -> fp == iop)
+ {
+ break;
+ }
+ }
+
+ if (cur == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Pclose: PANIC! Failed to find the process "
+ << "for descriptor " << fileno(iop) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to find the process "
+ << "for descriptor " << fileno(iop) << ".\n";
+
+ return -1;
+ }
+
+ do
+ {
+ #ifdef TEST
+ *logofs << "Pclose: Going to wait for process "
+ << "with pid '" << cur -> self << "'.\n"
+ << logofs_flush;
+ #endif
+
+ pid = waitpid(cur -> self, &pstat, 0);
+ }
+ while (pid == -1 && errno == EINTR);
+
+ if (last == NULL)
+ {
+ pidlist = cur -> next;
+ }
+ else
+ {
+ last -> next = cur -> next;
+ }
+
+ free(cur);
+
+ //
+ // Child has finished and we called the
+ // waitpid(). We can enable signals again.
+ //
+
+ EnableSignals();
+
+ return (pid == -1 ? -1 : pstat);
+}
diff --git a/nxcomp/src/Pipe.h b/nxcomp/src/Pipe.h
new file mode 100644
index 000000000..fd1061d30
--- /dev/null
+++ b/nxcomp/src/Pipe.h
@@ -0,0 +1,35 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+//
+// These are slightly modified versions of popen(3) and pclose(3)
+// that don't rely on a shell to be available on the system, so
+// that they can also work on Windows.
+//
+
+extern FILE *Popen(char * const parameters[], const char *type);
+extern FILE *Popen(const char *command, const char *type);
+
+extern int Pclose(FILE *file);
diff --git a/nxcomp/src/PolyArc.cpp b/nxcomp/src/PolyArc.cpp
new file mode 100644
index 000000000..42dbb265d
--- /dev/null
+++ b/nxcomp/src/PolyArc.cpp
@@ -0,0 +1,162 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "PolyArc.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyArcStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyArcMessage *polyArc = (PolyArcMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyArc -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyArc -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyArcStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyArcMessage *polyArc = (PolyArcMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polyArc -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyArc -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyArcStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyArcMessage *polyArc = (PolyArcMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polyArc -> drawable
+ << ", gcontext " << polyArc -> gcontext
+ << ", size " << polyArc -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolyArcStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolyArcStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyArcMessage *polyArc = (PolyArcMessage *) message;
+ PolyArcMessage *cachedPolyArc = (PolyArcMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyArc -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyArc -> drawable, clientCache -> drawableCache);
+
+ cachedPolyArc -> drawable = polyArc -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyArc -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyArc -> gcontext, clientCache -> gcCache);
+
+ cachedPolyArc -> gcontext = polyArc -> gcontext;
+}
+
+void PolyArcStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyArcMessage *polyArc = (PolyArcMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyArc -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyArc -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyArc -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyArc -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/src/PolyArc.h b/nxcomp/src/PolyArc.h
new file mode 100644
index 000000000..d744d6a10
--- /dev/null
+++ b/nxcomp/src/PolyArc.h
@@ -0,0 +1,185 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyArc_H
+#define PolyArc_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYARC_ENABLE_CACHE 1
+#define POLYARC_ENABLE_DATA 0
+#define POLYARC_ENABLE_SPLIT 0
+#define POLYARC_ENABLE_COMPRESS 0
+
+#define POLYARC_DATA_LIMIT 1980
+#define POLYARC_DATA_OFFSET 12
+
+#define POLYARC_CACHE_SLOTS 2000
+#define POLYARC_CACHE_THRESHOLD 2
+#define POLYARC_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyArcMessage : public Message
+{
+ friend class PolyArcStore;
+
+ public:
+
+ PolyArcMessage()
+ {
+ }
+
+ ~PolyArcMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolyArcStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyArcStore() : MessageStore()
+ {
+ enableCache = POLYARC_ENABLE_CACHE;
+ enableData = POLYARC_ENABLE_DATA;
+ enableSplit = POLYARC_ENABLE_SPLIT;
+ enableCompress = POLYARC_ENABLE_COMPRESS;
+
+ dataLimit = POLYARC_DATA_LIMIT;
+ dataOffset = POLYARC_DATA_OFFSET;
+
+ cacheSlots = POLYARC_CACHE_SLOTS;
+ cacheThreshold = POLYARC_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYARC_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyArcStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyArc";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyArc;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyArcMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyArcMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyArcMessage((const PolyArcMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyArcMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyArc_H */
diff --git a/nxcomp/src/PolyFillArc.cpp b/nxcomp/src/PolyFillArc.cpp
new file mode 100644
index 000000000..35125f0d3
--- /dev/null
+++ b/nxcomp/src/PolyFillArc.cpp
@@ -0,0 +1,162 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "PolyFillArc.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyFillArcStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyFillArc -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyFillArc -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyFillArcStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polyFillArc -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyFillArc -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyFillArcStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polyFillArc -> drawable
+ << ", gcontext " << polyFillArc -> gcontext
+ << ", size " << polyFillArc -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolyFillArcStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolyFillArcStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message;
+ PolyFillArcMessage *cachedPolyFillArc = (PolyFillArcMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyFillArc -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyFillArc -> drawable, clientCache -> drawableCache);
+
+ cachedPolyFillArc -> drawable = polyFillArc -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyFillArc -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyFillArc -> gcontext, clientCache -> gcCache);
+
+ cachedPolyFillArc -> gcontext = polyFillArc -> gcontext;
+}
+
+void PolyFillArcStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyFillArcMessage *polyFillArc = (PolyFillArcMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyFillArc -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyFillArc -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyFillArc -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyFillArc -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/src/PolyFillArc.h b/nxcomp/src/PolyFillArc.h
new file mode 100644
index 000000000..a4eff5b48
--- /dev/null
+++ b/nxcomp/src/PolyFillArc.h
@@ -0,0 +1,185 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyFillArc_H
+#define PolyFillArc_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYFILLARC_ENABLE_CACHE 1
+#define POLYFILLARC_ENABLE_DATA 0
+#define POLYFILLARC_ENABLE_SPLIT 0
+#define POLYFILLARC_ENABLE_COMPRESS 0
+
+#define POLYFILLARC_DATA_LIMIT 6144
+#define POLYFILLARC_DATA_OFFSET 12
+
+#define POLYFILLARC_CACHE_SLOTS 2000
+#define POLYFILLARC_CACHE_THRESHOLD 2
+#define POLYFILLARC_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyFillArcMessage : public Message
+{
+ friend class PolyFillArcStore;
+
+ public:
+
+ PolyFillArcMessage()
+ {
+ }
+
+ ~PolyFillArcMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolyFillArcStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyFillArcStore() : MessageStore()
+ {
+ enableCache = POLYFILLARC_ENABLE_CACHE;
+ enableData = POLYFILLARC_ENABLE_DATA;
+ enableSplit = POLYFILLARC_ENABLE_SPLIT;
+ enableCompress = POLYFILLARC_ENABLE_COMPRESS;
+
+ dataLimit = POLYFILLARC_DATA_LIMIT;
+ dataOffset = POLYFILLARC_DATA_OFFSET;
+
+ cacheSlots = POLYFILLARC_CACHE_SLOTS;
+ cacheThreshold = POLYFILLARC_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYFILLARC_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyFillArcStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyFillArc";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyFillArc;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyFillArcMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyFillArcMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyFillArcMessage((const PolyFillArcMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyFillArcMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyFillArc_H */
diff --git a/nxcomp/src/PolyFillRectangle.cpp b/nxcomp/src/PolyFillRectangle.cpp
new file mode 100644
index 000000000..95694e582
--- /dev/null
+++ b/nxcomp/src/PolyFillRectangle.cpp
@@ -0,0 +1,160 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "PolyFillRectangle.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyFillRectangleStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyFillRectangle -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyFillRectangle -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyFillRectangleStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polyFillRectangle -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyFillRectangle -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyFillRectangleStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polyFillRectangle -> drawable
+ << ", gcontext " << polyFillRectangle -> gcontext
+ << ", size " << polyFillRectangle -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolyFillRectangleStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolyFillRectangleStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message;
+ PolyFillRectangleMessage *cachedPolyFillRectangle = (PolyFillRectangleMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value " << polyFillRectangle -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyFillRectangle -> drawable, clientCache -> drawableCache);
+
+ cachedPolyFillRectangle -> drawable = polyFillRectangle -> drawable;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value " << polyFillRectangle -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyFillRectangle -> gcontext, clientCache -> gcCache);
+
+ cachedPolyFillRectangle -> gcontext = polyFillRectangle -> gcontext;
+}
+
+void PolyFillRectangleStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyFillRectangleMessage *polyFillRectangle = (PolyFillRectangleMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyFillRectangle -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyFillRectangle -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyFillRectangle -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyFillRectangle -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/PolyFillRectangle.h b/nxcomp/src/PolyFillRectangle.h
new file mode 100644
index 000000000..7ebb9270d
--- /dev/null
+++ b/nxcomp/src/PolyFillRectangle.h
@@ -0,0 +1,185 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyFillRectangle_H
+#define PolyFillRectangle_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYFILLRECTANGLE_ENABLE_CACHE 1
+#define POLYFILLRECTANGLE_ENABLE_DATA 0
+#define POLYFILLRECTANGLE_ENABLE_SPLIT 0
+#define POLYFILLRECTANGLE_ENABLE_COMPRESS 0
+
+#define POLYFILLRECTANGLE_DATA_LIMIT 2048
+#define POLYFILLRECTANGLE_DATA_OFFSET 12
+
+#define POLYFILLRECTANGLE_CACHE_SLOTS 4000
+#define POLYFILLRECTANGLE_CACHE_THRESHOLD 5
+#define POLYFILLRECTANGLE_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyFillRectangleMessage : public Message
+{
+ friend class PolyFillRectangleStore;
+
+ public:
+
+ PolyFillRectangleMessage()
+ {
+ }
+
+ ~PolyFillRectangleMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolyFillRectangleStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyFillRectangleStore() : MessageStore()
+ {
+ enableCache = POLYFILLRECTANGLE_ENABLE_CACHE;
+ enableData = POLYFILLRECTANGLE_ENABLE_DATA;
+ enableSplit = POLYFILLRECTANGLE_ENABLE_SPLIT;
+ enableCompress = POLYFILLRECTANGLE_ENABLE_COMPRESS;
+
+ dataLimit = POLYFILLRECTANGLE_DATA_LIMIT;
+ dataOffset = POLYFILLRECTANGLE_DATA_OFFSET;
+
+ cacheSlots = POLYFILLRECTANGLE_CACHE_SLOTS;
+ cacheThreshold = POLYFILLRECTANGLE_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYFILLRECTANGLE_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyFillRectangleStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyFillRectangle";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyFillRectangle;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyFillRectangleMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyFillRectangleMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyFillRectangleMessage((const PolyFillRectangleMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyFillRectangleMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyFillRectangle_H */
diff --git a/nxcomp/src/PolyLine.cpp b/nxcomp/src/PolyLine.cpp
new file mode 100644
index 000000000..1b12bbe98
--- /dev/null
+++ b/nxcomp/src/PolyLine.cpp
@@ -0,0 +1,168 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "PolyLine.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyLineStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyLineMessage *polyLine = (PolyLineMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyLine -> mode = *(buffer + 1);
+
+ polyLine -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyLine -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyLineStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyLineMessage *polyLine = (PolyLineMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = polyLine -> mode;
+
+ PutULONG(polyLine -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyLine -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyLineStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyLineMessage *polyLine = (PolyLineMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polyLine -> drawable
+ << ", gcontext " << polyLine -> gcontext
+ << ", size " << polyLine -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolyLineStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ // Since ProtoStep8 (#issue 108)
+ md5_append(md5_state_, buffer + 1, 1);
+}
+
+void PolyLineStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyLineMessage *polyLine = (PolyLineMessage *) message;
+ PolyLineMessage *cachedPolyLine = (PolyLineMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyLine -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyLine -> drawable, clientCache -> drawableCache);
+
+ cachedPolyLine -> drawable = polyLine -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyLine -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyLine -> gcontext, clientCache -> gcCache);
+
+ cachedPolyLine -> gcontext = polyLine -> gcontext;
+}
+
+void PolyLineStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyLineMessage *polyLine = (PolyLineMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyLine -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyLine -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyLine -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyLine -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/src/PolyLine.h b/nxcomp/src/PolyLine.h
new file mode 100644
index 000000000..66fa5df1a
--- /dev/null
+++ b/nxcomp/src/PolyLine.h
@@ -0,0 +1,186 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyLine_H
+#define PolyLine_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYLINE_ENABLE_CACHE 1
+#define POLYLINE_ENABLE_DATA 0
+#define POLYLINE_ENABLE_SPLIT 0
+#define POLYLINE_ENABLE_COMPRESS 0
+
+#define POLYLINE_DATA_LIMIT 144
+#define POLYLINE_DATA_OFFSET 12
+
+#define POLYLINE_CACHE_SLOTS 3000
+#define POLYLINE_CACHE_THRESHOLD 3
+#define POLYLINE_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyLineMessage : public Message
+{
+ friend class PolyLineStore;
+
+ public:
+
+ PolyLineMessage()
+ {
+ }
+
+ ~PolyLineMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char mode;
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolyLineStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyLineStore() : MessageStore()
+ {
+ enableCache = POLYLINE_ENABLE_CACHE;
+ enableData = POLYLINE_ENABLE_DATA;
+ enableSplit = POLYLINE_ENABLE_SPLIT;
+ enableCompress = POLYLINE_ENABLE_COMPRESS;
+
+ dataLimit = POLYLINE_DATA_LIMIT;
+ dataOffset = POLYLINE_DATA_OFFSET;
+
+ cacheSlots = POLYLINE_CACHE_SLOTS;
+ cacheThreshold = POLYLINE_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYLINE_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyLineStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyLine";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyLine;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyLineMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyLineMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyLineMessage((const PolyLineMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyLineMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyLine_H */
diff --git a/nxcomp/src/PolyPoint.cpp b/nxcomp/src/PolyPoint.cpp
new file mode 100644
index 000000000..70151cc0d
--- /dev/null
+++ b/nxcomp/src/PolyPoint.cpp
@@ -0,0 +1,168 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "PolyPoint.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyPointStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyPointMessage *polyPoint = (PolyPointMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyPoint -> mode = *(buffer + 1);
+
+ polyPoint -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyPoint -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyPointStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyPointMessage *polyPoint = (PolyPointMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = polyPoint -> mode;
+
+ PutULONG(polyPoint -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyPoint -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyPointStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyPointMessage *polyPoint = (PolyPointMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polyPoint -> drawable
+ << ", gcontext " << polyPoint -> gcontext
+ << ", size " << polyPoint -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolyPointStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ // Since ProtoStep8 (#issue 108)
+ md5_append(md5_state_, buffer + 1, 1);
+}
+
+void PolyPointStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyPointMessage *polyPoint = (PolyPointMessage *) message;
+ PolyPointMessage *cachedPolyPoint = (PolyPointMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyPoint -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyPoint -> drawable, clientCache -> drawableCache);
+
+ cachedPolyPoint -> drawable = polyPoint -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyPoint -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyPoint -> gcontext, clientCache -> gcCache);
+
+ cachedPolyPoint -> gcontext = polyPoint -> gcontext;
+}
+
+void PolyPointStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyPointMessage *polyPoint = (PolyPointMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyPoint -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyPoint -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyPoint -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyPoint -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/src/PolyPoint.h b/nxcomp/src/PolyPoint.h
new file mode 100644
index 000000000..b8ea183bf
--- /dev/null
+++ b/nxcomp/src/PolyPoint.h
@@ -0,0 +1,186 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyPoint_H
+#define PolyPoint_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYPOINT_ENABLE_CACHE 1
+#define POLYPOINT_ENABLE_DATA 0
+#define POLYPOINT_ENABLE_SPLIT 0
+#define POLYPOINT_ENABLE_COMPRESS 0
+
+#define POLYPOINT_DATA_LIMIT 3200
+#define POLYPOINT_DATA_OFFSET 12
+
+#define POLYPOINT_CACHE_SLOTS 3000
+#define POLYPOINT_CACHE_THRESHOLD 3
+#define POLYPOINT_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyPointMessage : public Message
+{
+ friend class PolyPointStore;
+
+ public:
+
+ PolyPointMessage()
+ {
+ }
+
+ ~PolyPointMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char mode;
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolyPointStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyPointStore() : MessageStore()
+ {
+ enableCache = POLYPOINT_ENABLE_CACHE;
+ enableData = POLYPOINT_ENABLE_DATA;
+ enableSplit = POLYPOINT_ENABLE_SPLIT;
+ enableCompress = POLYPOINT_ENABLE_COMPRESS;
+
+ dataLimit = POLYPOINT_DATA_LIMIT;
+ dataOffset = POLYPOINT_DATA_OFFSET;
+
+ cacheSlots = POLYPOINT_CACHE_SLOTS;
+ cacheThreshold = POLYPOINT_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYPOINT_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyPointStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyPoint";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyPoint;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyPointMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyPointMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyPointMessage((const PolyPointMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyPointMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyPoint_H */
diff --git a/nxcomp/src/PolySegment.cpp b/nxcomp/src/PolySegment.cpp
new file mode 100644
index 000000000..aa2d4efe3
--- /dev/null
+++ b/nxcomp/src/PolySegment.cpp
@@ -0,0 +1,162 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "PolySegment.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolySegmentStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolySegmentMessage *polySegment = (PolySegmentMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polySegment -> drawable = GetULONG(buffer + 4, bigEndian);
+ polySegment -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolySegmentStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolySegmentMessage *polySegment = (PolySegmentMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polySegment -> drawable, buffer + 4, bigEndian);
+ PutULONG(polySegment -> gcontext, buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolySegmentStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolySegmentMessage *polySegment = (PolySegmentMessage *) message;
+
+ *logofs << name() << ": Identity drawable " << polySegment -> drawable
+ << ", gcontext " << polySegment -> gcontext
+ << ", size " << polySegment -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void PolySegmentStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolySegmentStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolySegmentMessage *polySegment = (PolySegmentMessage *) message;
+ PolySegmentMessage *cachedPolySegment = (PolySegmentMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polySegment -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polySegment -> drawable, clientCache -> drawableCache);
+
+ cachedPolySegment -> drawable = polySegment -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polySegment -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polySegment -> gcontext, clientCache -> gcCache);
+
+ cachedPolySegment -> gcontext = polySegment -> gcontext;
+}
+
+void PolySegmentStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolySegmentMessage *polySegment = (PolySegmentMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polySegment -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polySegment -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polySegment -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polySegment -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/src/PolySegment.h b/nxcomp/src/PolySegment.h
new file mode 100644
index 000000000..53fd42c60
--- /dev/null
+++ b/nxcomp/src/PolySegment.h
@@ -0,0 +1,185 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef PolySegment_H
+#define PolySegment_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYSEGMENT_ENABLE_CACHE 1
+#define POLYSEGMENT_ENABLE_DATA 0
+#define POLYSEGMENT_ENABLE_SPLIT 0
+#define POLYSEGMENT_ENABLE_COMPRESS 0
+
+#define POLYSEGMENT_DATA_LIMIT 8192
+#define POLYSEGMENT_DATA_OFFSET 12
+
+#define POLYSEGMENT_CACHE_SLOTS 3000
+#define POLYSEGMENT_CACHE_THRESHOLD 5
+#define POLYSEGMENT_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolySegmentMessage : public Message
+{
+ friend class PolySegmentStore;
+
+ public:
+
+ PolySegmentMessage()
+ {
+ }
+
+ ~PolySegmentMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+};
+
+class PolySegmentStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolySegmentStore() : MessageStore()
+ {
+ enableCache = POLYSEGMENT_ENABLE_CACHE;
+ enableData = POLYSEGMENT_ENABLE_DATA;
+ enableSplit = POLYSEGMENT_ENABLE_SPLIT;
+ enableCompress = POLYSEGMENT_ENABLE_COMPRESS;
+
+ dataLimit = POLYSEGMENT_DATA_LIMIT;
+ dataOffset = POLYSEGMENT_DATA_OFFSET;
+
+ cacheSlots = POLYSEGMENT_CACHE_SLOTS;
+ cacheThreshold = POLYSEGMENT_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYSEGMENT_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolySegmentStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolySegment";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolySegment;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolySegmentMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolySegmentMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolySegmentMessage((const PolySegmentMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolySegmentMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolySegment_H */
diff --git a/nxcomp/src/PolyText16.cpp b/nxcomp/src/PolyText16.cpp
new file mode 100644
index 000000000..32be45482
--- /dev/null
+++ b/nxcomp/src/PolyText16.cpp
@@ -0,0 +1,312 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "PolyText16.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyText16Store::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyText16Message *polyText16 = (PolyText16Message *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyText16 -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyText16 -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ polyText16 -> x = GetUINT(buffer + 12, bigEndian);
+ polyText16 -> y = GetUINT(buffer + 14, bigEndian);
+
+ //
+ // Clean up padding bytes.
+ //
+
+ #ifdef DUMP
+
+ DumpData(buffer, size);
+
+ *logofs << "\n" << logofs_flush;
+
+ #endif
+
+ if ((int) size > dataOffset)
+ {
+ int current;
+ int length;
+ int delta;
+ int item;
+
+ unsigned int nitem;
+
+ unsigned char *pad = NULL;
+ unsigned char *end = NULL;
+
+ delta = 1;
+ nitem = 0;
+
+ #ifdef DUMP
+ *logofs << name() << " Size " << size << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Data is a list of TextItem where element
+ // can be a string or a font shift.
+ //
+
+ current = POLYTEXT16_DATA_OFFSET;
+ length = POLYTEXT16_DATA_OFFSET;
+
+ do
+ {
+ #ifdef DUMP
+ *logofs << name() << " Current " << current << ".\n" << logofs_flush;
+ #endif
+
+ item = GetUINT(buffer + length , bigEndian);
+
+ if (item < 255)
+ {
+ //
+ // Text element. Number represents
+ // the 'Length of CHAR2B string'
+ // field.
+ //
+
+ length += ((item * 2) + delta + 1);
+
+ nitem++;
+ }
+ else if (item == 255)
+ {
+ //
+ // Element is a font shift.
+ //
+
+ length += 5;
+
+ nitem++;
+ }
+
+ #ifdef DUMP
+ *logofs << name() << " Item " << item << ".\n" << logofs_flush;
+ #endif
+
+ current += length;
+ }
+ while(current < (int) size && item != 0);
+
+ #ifdef DUMP
+ *logofs << name() << " Final length " << length << ".\n" << logofs_flush;
+ #endif
+
+ end = ((unsigned char *) buffer) + size;
+
+ pad = ((unsigned char *) buffer) + length;
+
+ for (; pad < end && nitem >= 1; pad++)
+ {
+ #ifdef DUMP
+ *logofs << name() << " Padding " << " .\n" << logofs_flush;
+ #endif
+
+ *pad = 0;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyText16Store::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyText16Message *polyText16 = (PolyText16Message *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polyText16 -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyText16 -> gcontext, buffer + 8, bigEndian);
+
+ PutUINT(polyText16 -> x, buffer + 12, bigEndian);
+ PutUINT(polyText16 -> y, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyText16Store::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyText16Message *polyText16 = (PolyText16Message *) message;
+
+ *logofs << name() << ": Identity drawable " << polyText16 -> drawable
+ << ", gcontext " << polyText16 -> gcontext << ", x " << polyText16 -> x
+ << ", y " << polyText16 -> y << ", size " << polyText16 -> size_
+ << ".\n";
+
+ #endif
+}
+
+void PolyText16Store::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolyText16Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyText16Message *polyText16 = (PolyText16Message *) message;
+ PolyText16Message *cachedPolyText16 = (PolyText16Message *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText16 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyText16 -> drawable, clientCache -> drawableCache);
+
+ cachedPolyText16 -> drawable = polyText16 -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText16 -> gcontext
+ << " as " << "gcontext" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyText16 -> gcontext, clientCache -> gcCache);
+
+ cachedPolyText16 -> gcontext = polyText16 -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText16 -> x
+ << " as " << "x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = polyText16 -> x - cachedPolyText16 -> x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> polyTextCacheX);
+
+ cachedPolyText16 -> x = polyText16 -> x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText16 -> y
+ << " as " << "y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = polyText16 -> y - cachedPolyText16 -> y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> polyTextCacheY);
+
+ cachedPolyText16 -> y = polyText16 -> y;
+}
+
+void PolyText16Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyText16Message *polyText16 = (PolyText16Message *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyText16 -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText16 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyText16 -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText16 -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> polyTextCacheX);
+
+ polyText16 -> x += value;
+ polyText16 -> x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText16 -> x
+ << " as x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> polyTextCacheY);
+
+ polyText16 -> y += value;
+ polyText16 -> y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText16 -> y
+ << " as y field.\n" << logofs_flush;
+ #endif
+}
+
+
diff --git a/nxcomp/src/PolyText16.h b/nxcomp/src/PolyText16.h
new file mode 100644
index 000000000..805e1fa04
--- /dev/null
+++ b/nxcomp/src/PolyText16.h
@@ -0,0 +1,188 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyText16_H
+#define PolyText16_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYTEXT16_ENABLE_CACHE 1
+#define POLYTEXT16_ENABLE_DATA 0
+#define POLYTEXT16_ENABLE_SPLIT 0
+#define POLYTEXT16_ENABLE_COMPRESS 0
+
+#define POLYTEXT16_DATA_LIMIT 420
+#define POLYTEXT16_DATA_OFFSET 16
+
+#define POLYTEXT16_CACHE_SLOTS 3000
+#define POLYTEXT16_CACHE_THRESHOLD 4
+#define POLYTEXT16_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyText16Message : public Message
+{
+ friend class PolyText16Store;
+
+ public:
+
+ PolyText16Message()
+ {
+ }
+
+ ~PolyText16Message()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned short x;
+ unsigned short y;
+};
+
+class PolyText16Store : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyText16Store() : MessageStore()
+ {
+ enableCache = POLYTEXT16_ENABLE_CACHE;
+ enableData = POLYTEXT16_ENABLE_DATA;
+ enableSplit = POLYTEXT16_ENABLE_SPLIT;
+ enableCompress = POLYTEXT16_ENABLE_COMPRESS;
+
+ dataLimit = POLYTEXT16_DATA_LIMIT;
+ dataOffset = POLYTEXT16_DATA_OFFSET;
+
+ cacheSlots = POLYTEXT16_CACHE_SLOTS;
+ cacheThreshold = POLYTEXT16_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYTEXT16_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyText16Store()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyText16";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyText16;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyText16Message);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyText16Message();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyText16Message((const PolyText16Message &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyText16Message *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyText16_H */
diff --git a/nxcomp/src/PolyText8.cpp b/nxcomp/src/PolyText8.cpp
new file mode 100644
index 000000000..61fef5a4f
--- /dev/null
+++ b/nxcomp/src/PolyText8.cpp
@@ -0,0 +1,310 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "PolyText8.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PolyText8Store::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyText8Message *polyText8 = (PolyText8Message *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ polyText8 -> drawable = GetULONG(buffer + 4, bigEndian);
+ polyText8 -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ polyText8 -> x = GetUINT(buffer + 12, bigEndian);
+ polyText8 -> y = GetUINT(buffer + 14, bigEndian);
+
+ //
+ // Clean up padding bytes.
+ //
+
+ #ifdef DUMP
+
+ DumpData(buffer, size);
+
+ *logofs << "\n\n" << logofs_flush;
+
+ #endif
+
+ if ((int) size > dataOffset)
+ {
+ int length;
+ int current;
+ int delta;
+ int item;
+
+ unsigned int nitem;
+
+ unsigned char *pad = NULL;
+ unsigned char *end = NULL;
+
+ delta = 1;
+ nitem = 0;
+
+ #ifdef DUMP
+ *logofs << name() << " Size " << size << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Data is a list of TextItem where element
+ // can be a string or a font shift.
+ //
+
+ current = POLYTEXT8_DATA_OFFSET;
+ length = POLYTEXT8_DATA_OFFSET;
+
+ do
+ {
+ #ifdef DUMP
+ *logofs << name() << " Current " << current << ".\n" << logofs_flush;
+ #endif
+
+ item = GetUINT(buffer + length , bigEndian);
+
+ if (item < 255)
+ {
+ //
+ // Text element. Number represents
+ // the 'Length of string' field.
+ //
+
+ length += (item + delta + 1);
+
+ nitem++;
+ }
+ else if (item == 255)
+ {
+ //
+ // Element is a font shift.
+ //
+
+ length += 5;
+
+ nitem++;
+ }
+
+ #ifdef DUMP
+ *logofs << name() << " Item " << item << ".\n" << logofs_flush;
+ #endif
+
+ current += length;
+ }
+ while(current < (int) size && item != 0);
+
+
+ #ifdef DUMP
+ *logofs << name() << " Final length " << length << ".\n" << logofs_flush;
+ #endif
+
+ end = ((unsigned char *) buffer) + size;
+
+ pad = ((unsigned char *) buffer) + length;
+
+ for (; pad < end && nitem >= 1; pad++)
+ {
+ #ifdef DUMP
+ *logofs << name() << " Padding " << " .\n" << logofs_flush;
+ #endif
+
+ *pad = 0;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PolyText8Store::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PolyText8Message *polyText8 = (PolyText8Message *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(polyText8 -> drawable, buffer + 4, bigEndian);
+ PutULONG(polyText8 -> gcontext, buffer + 8, bigEndian);
+
+ PutUINT(polyText8 -> x, buffer + 12, bigEndian);
+ PutUINT(polyText8 -> y, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PolyText8Store::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PolyText8Message *polyText8 = (PolyText8Message *) message;
+
+ *logofs << name() << ": Identity drawable " << polyText8 -> drawable
+ << ", gcontext " << polyText8 -> gcontext << ", x " << polyText8 -> x
+ << ", y " << polyText8 -> y << ", size " << polyText8 -> size_
+ << ".\n";
+
+ #endif
+}
+
+void PolyText8Store::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void PolyText8Store::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ PolyText8Message *polyText8 = (PolyText8Message *) message;
+ PolyText8Message *cachedPolyText8 = (PolyText8Message *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText8 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyText8 -> drawable, clientCache -> drawableCache);
+
+ cachedPolyText8 -> drawable = polyText8 -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText8 -> gcontext
+ << " as " << "gcontext" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(polyText8 -> gcontext, clientCache -> gcCache);
+
+ cachedPolyText8 -> gcontext = polyText8 -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText8 -> x
+ << " as " << "x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = polyText8 -> x - cachedPolyText8 -> x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> polyTextCacheX);
+
+ cachedPolyText8 -> x = polyText8 -> x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << polyText8 -> y
+ << " as " << "y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = polyText8 -> y - cachedPolyText8 -> y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> polyTextCacheY);
+
+ cachedPolyText8 -> y = polyText8 -> y;
+}
+
+void PolyText8Store::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ PolyText8Message *polyText8 = (PolyText8Message *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ polyText8 -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText8 -> drawable
+ << " as " << "drawable" << " field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ polyText8 -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText8 -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> polyTextCacheX);
+
+ polyText8 -> x += value;
+ polyText8 -> x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText8 -> x
+ << " as x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> polyTextCacheY);
+
+ polyText8 -> y += value;
+ polyText8 -> y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << polyText8 -> y
+ << " as y field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/PolyText8.h b/nxcomp/src/PolyText8.h
new file mode 100644
index 000000000..3d5ff533a
--- /dev/null
+++ b/nxcomp/src/PolyText8.h
@@ -0,0 +1,188 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef PolyText8_H
+#define PolyText8_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define POLYTEXT8_ENABLE_CACHE 1
+#define POLYTEXT8_ENABLE_DATA 0
+#define POLYTEXT8_ENABLE_SPLIT 0
+#define POLYTEXT8_ENABLE_COMPRESS 0
+
+#define POLYTEXT8_DATA_LIMIT 380
+#define POLYTEXT8_DATA_OFFSET 16
+
+#define POLYTEXT8_CACHE_SLOTS 3000
+#define POLYTEXT8_CACHE_THRESHOLD 5
+#define POLYTEXT8_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class PolyText8Message : public Message
+{
+ friend class PolyText8Store;
+
+ public:
+
+ PolyText8Message()
+ {
+ }
+
+ ~PolyText8Message()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned short x;
+ unsigned short y;
+};
+
+class PolyText8Store : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PolyText8Store() : MessageStore()
+ {
+ enableCache = POLYTEXT8_ENABLE_CACHE;
+ enableData = POLYTEXT8_ENABLE_DATA;
+ enableSplit = POLYTEXT8_ENABLE_SPLIT;
+ enableCompress = POLYTEXT8_ENABLE_COMPRESS;
+
+ dataLimit = POLYTEXT8_DATA_LIMIT;
+ dataOffset = POLYTEXT8_DATA_OFFSET;
+
+ cacheSlots = POLYTEXT8_CACHE_SLOTS;
+ cacheThreshold = POLYTEXT8_CACHE_THRESHOLD;
+ cacheLowerThreshold = POLYTEXT8_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~PolyText8Store()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "PolyText8";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PolyText8;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PolyText8Message);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new PolyText8Message();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PolyText8Message((const PolyText8Message &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PolyText8Message *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PolyText8_H */
diff --git a/nxcomp/src/Proxy.cpp b/nxcomp/src/Proxy.cpp
new file mode 100644
index 000000000..5529de021
--- /dev/null
+++ b/nxcomp/src/Proxy.cpp
@@ -0,0 +1,6529 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cstdio>
+#include <unistd.h>
+#include <cstdlib>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#ifdef ANDROID
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#endif
+
+#include "Misc.h"
+
+#if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun)
+#include <netinet/in_systm.h>
+#endif
+
+#ifndef ANDROID
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#endif
+
+#include "NXalert.h"
+#include "NXvars.h"
+
+#include "Proxy.h"
+
+#include "Socket.h"
+#include "Channel.h"
+#include "Statistics.h"
+
+#include "ClientChannel.h"
+#include "ServerChannel.h"
+#include "GenericChannel.h"
+#include "ChannelEndPoint.h"
+
+//
+// We need to adjust some values related
+// to these messages at the time the mes-
+// sage stores are reconfigured.
+//
+
+#include "PutImage.h"
+#include "ChangeGC.h"
+#include "PolyFillRectangle.h"
+#include "PutPackedImage.h"
+
+//
+// This is from the main loop.
+//
+
+extern void CleanupListeners();
+
+extern int HandleChild(int);
+
+//
+// Default size of string buffers.
+//
+
+#define DEFAULT_STRING_LENGTH 512
+
+//
+// Set the verbosity level. You also need
+// to define DUMP in Misc.cpp if DUMP is
+// defined here.
+//
+
+#define WARNING
+#define PANIC
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Log the important tracepoints related
+// to writing packets to the peer proxy.
+//
+
+#undef FLUSH
+
+//
+// Log the operations related to splits.
+//
+
+#undef SPLIT
+
+//
+// Log the operations related to sending
+// and receiving the control tokens.
+//
+
+#undef TOKEN
+
+//
+// Log the operations related to setting
+// the token limits.
+//
+
+#undef LIMIT
+
+//
+// Log a warning if no data is written by
+// the proxy within a timeout.
+//
+
+#undef TIME
+
+//
+// Log the operation related to generating
+// the ping message at idle time.
+//
+
+#undef PING
+
+Proxy::Proxy(int fd)
+
+ : transport_(new ProxyTransport(fd)), fd_(fd), readBuffer_(transport_)
+{
+ for (int channelId = 0;
+ channelId < CONNECTIONS_LIMIT;
+ channelId++)
+ {
+ channels_[channelId] = NULL;
+ transports_[channelId] = NULL;
+ congestions_[channelId] = 0;
+
+ fdMap_[channelId] = nothing;
+ channelMap_[channelId] = nothing;
+ slavePidMap_[channelId] = nothing;
+ }
+
+ inputChannel_ = nothing;
+ outputChannel_ = nothing;
+
+ controlLength_ = 0;
+
+ operation_ = operation_in_negotiation;
+
+ draining_ = 0;
+ priority_ = 0;
+ finish_ = 0;
+ shutdown_ = 0;
+ congestion_ = 0;
+
+ timer_ = 0;
+ alert_ = 0;
+
+ agent_ = nothing;
+
+ //
+ // Set null timeouts. This will require
+ // a new link configuration.
+ //
+
+ timeouts_.split = 0;
+ timeouts_.motion = 0;
+
+ timeouts_.readTs = getTimestamp();
+ timeouts_.writeTs = getTimestamp();
+
+ timeouts_.loopTs = getTimestamp();
+ timeouts_.pingTs = getTimestamp();
+ timeouts_.alertTs = nullTimestamp();
+ timeouts_.loadTs = nullTimestamp();
+
+ timeouts_.splitTs = nullTimestamp();
+ timeouts_.motionTs = nullTimestamp();
+
+ //
+ // Initialize the token counters. This
+ // will require a new link configuration.
+ //
+
+ for (int i = token_control; i <= token_data; i++)
+ {
+ tokens_[i].size = 0;
+ tokens_[i].limit = 0;
+
+ tokens_[i].bytes = 0;
+ tokens_[i].remaining = 0;
+ }
+
+ tokens_[token_control].request = code_control_token_request;
+ tokens_[token_control].reply = code_control_token_reply;
+ tokens_[token_control].type = token_control;
+
+ tokens_[token_split].request = code_split_token_request;
+ tokens_[token_split].reply = code_split_token_reply;
+ tokens_[token_split].type = token_split;
+
+ tokens_[token_data].request = code_data_token_request;
+ tokens_[token_data].reply = code_data_token_reply;
+ tokens_[token_data].type = token_data;
+
+ currentStatistics_ = NULL;
+
+ //
+ // Create compressor and decompressor
+ // for image and data payload.
+ //
+
+ compressor_ = new StaticCompressor(control -> LocalDataCompressionLevel,
+ control -> LocalDataCompressionThreshold);
+
+ //
+ // Create object storing NX specific
+ // opcodes.
+ //
+
+ opcodeStore_ = new OpcodeStore();
+
+ //
+ // Create the message stores.
+ //
+
+ clientStore_ = new ClientStore(compressor_);
+ serverStore_ = new ServerStore(compressor_);
+
+ clientCache_ = new ClientCache();
+ serverCache_ = new ServerCache();
+
+ if (clientCache_ == NULL || serverCache_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Failed to create the channel cache.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to create the channel cache.\n";
+
+ HandleCleanup();
+ }
+
+ //
+ // Prepare for image decompression.
+ //
+
+ UnpackInit();
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Created new object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+Proxy::~Proxy()
+{
+ for (int channelId = 0;
+ channelId < CONNECTIONS_LIMIT;
+ channelId++)
+ {
+ if (channels_[channelId] != NULL)
+ {
+ deallocateTransport(channelId);
+
+ delete channels_[channelId];
+ channels_[channelId] = NULL;
+ }
+ }
+
+ //
+ // Kill all active slave channel children, and
+ // give them 5 seconds to exit nicely.
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Killing active slaves" << endl;
+ #endif
+
+ int slave_count = 999;
+ int loop_count = 0;
+
+ while(slave_count > 0 && loop_count < 50)
+ {
+ slave_count = 0;
+
+ for (int channelId = 0; channelId<CONNECTIONS_LIMIT; channelId++)
+ {
+ int pid = slavePidMap_[channelId];
+
+ if (pid > 1) {
+ slave_count++;
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Active slave with pid " << pid << logofs_flush;
+ #endif
+
+ if ( loop_count == 0 )
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Sending SIGTERM to " << pid << logofs_flush;
+ #endif
+ kill(pid, SIGTERM);
+ }
+ else if ( loop_count == 25 )
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Sending SIGKILL to " << pid << logofs_flush;
+ #endif
+ kill(pid, SIGKILL);
+ }
+
+ if (HandleChild(pid))
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Slave " << pid << " terminated" << logofs_flush;
+ #endif
+ slavePidMap_[channelId] = nothing;
+ }
+ }
+ }
+
+ if ( slave_count > 0 )
+ {
+ cerr << "Proxy: Error: Failed to kill all slave channel processes. " << slave_count << " processes still remaining." << endl;
+ }
+
+ usleep(200000);
+ loop_count++;
+ }
+
+ delete transport_;
+ delete compressor_;
+
+ //
+ // Delete storage shared among channels.
+ //
+
+ delete opcodeStore_;
+
+ delete clientStore_;
+ delete serverStore_;
+
+ delete clientCache_;
+ delete serverCache_;
+
+ //
+ // Get rid of the image decompression
+ // resources.
+ //
+
+ UnpackDestroy();
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Deleted proxy object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+int Proxy::setOperational()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Entering operational mode.\n"
+ << logofs_flush;
+ #endif
+
+ operation_ = operation_in_messages;
+
+ return 1;
+}
+
+int Proxy::setReadDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax)
+{
+ //
+ // Set the initial timeout to the time of
+ // the next ping. If the congestion count
+ // is greater than zero, anyway, use a
+ // shorter timeout to force a congestion
+ // update.
+ //
+
+ if (agent_ != nothing && congestions_[agent_] == 0 &&
+ statistics -> getCongestionInFrame() >= 1 &&
+ tokens_[token_control].remaining >=
+ (tokens_[token_control].limit - 1))
+ {
+ setMinTimestamp(tsMax, control -> IdleTimeout);
+
+ #ifdef TEST
+ *logofs << "Proxy: Initial timeout is " << tsMax.tv_sec
+ << " S and " << (double) tsMax.tv_usec /
+ 1000 << " Ms with congestion "
+ << statistics -> getCongestionInFrame()
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ setMinTimestamp(tsMax, control -> PingTimeout);
+
+ #ifdef TEST
+ *logofs << "Proxy: Initial timeout is " << tsMax.tv_sec
+ << " S and " << (double) tsMax.tv_usec /
+ 1000 << " Ms.\n" << logofs_flush;
+ #endif
+ }
+
+ int fd = -1;
+
+ if (isTimeToRead() == 1)
+ {
+ //
+ // If we don't have split tokens available
+ // don't set the timeout.
+ //
+
+ if (tokens_[token_split].remaining > 0 &&
+ isTimestamp(timeouts_.splitTs) == 1)
+ {
+ int diffTs = getTimeToNextSplit();
+
+ #if defined(TEST) || defined(INFO) || \
+ defined(FLUSH) || defined(SPLIT)
+
+ if (diffTimestamp(timeouts_.splitTs,
+ getTimestamp()) > timeouts_.split)
+ {
+ *logofs << "Proxy: FLUSH! SPLIT! WARNING! Running with "
+ << diffTimestamp(timeouts_.splitTs, getTimestamp())
+ << " Ms elapsed since the last split.\n"
+ << logofs_flush;
+ }
+
+ *logofs << "Proxy: FLUSH! SPLIT! Requesting timeout of "
+ << diffTs << " Ms as there are splits to send.\n"
+ << logofs_flush;
+
+ #endif
+
+ setMinTimestamp(tsMax, diffTs);
+ }
+ #if defined(TEST) || defined(INFO)
+ else if (isTimestamp(timeouts_.splitTs) == 1)
+ {
+ *logofs << "Proxy: WARNING! Not requesting a split "
+ << "timeout with " << tokens_[token_split].remaining
+ << " split tokens remaining.\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Loop through the valid channels and set
+ // the descriptors selected for read and
+ // the timeout.
+ //
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] == NULL)
+ {
+ continue;
+ }
+
+ fd = getFd(channelId);
+
+ if (channels_[channelId] -> getFinish() == 0 &&
+ (channels_[channelId] -> getType() == channel_x11 ||
+ tokens_[token_data].remaining > 0) &&
+ congestions_[channelId] == 0)
+ {
+ FD_SET(fd, fdSet);
+
+ if (fd >= fdMax)
+ {
+ fdMax = fd + 1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Descriptor FD#" << fd
+ << " selected for read with buffer length "
+ << transports_[channelId] -> length()
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Wakeup the proxy if there are motion
+ // events to flush.
+ //
+
+ if (isTimestamp(timeouts_.motionTs) == 1)
+ {
+ int diffTs = getTimeToNextMotion();
+
+ #if defined(TEST) || defined(INFO)
+
+ if (diffTimestamp(timeouts_.motionTs,
+ getTimestamp()) > timeouts_.motion)
+ {
+ *logofs << "Proxy: FLUSH! WARNING! Running with "
+ << diffTimestamp(timeouts_.motionTs, getTimestamp())
+ << " Ms elapsed since the last motion.\n"
+ << logofs_flush;
+ }
+
+ *logofs << "Proxy: FLUSH! Requesting timeout of "
+ << diffTs << " Ms as FD#" << fd << " has motion "
+ << "events to send.\n" << logofs_flush;
+
+ #endif
+
+ setMinTimestamp(tsMax, diffTs);
+ }
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ if (channels_[channelId] -> getType() != channel_x11 &&
+ tokens_[token_data].remaining <= 0)
+ {
+ *logofs << "Proxy: WARNING! Descriptor FD#" << fd
+ << " not selected for read with "
+ << tokens_[token_data].remaining << " data "
+ << "tokens remaining.\n" << logofs_flush;
+ }
+ }
+ #endif
+ }
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "Proxy: WARNING! Disabled reading from channels.\n"
+ << logofs_flush;
+
+ *logofs << "Proxy: WARNING! Congestion is " << congestion_
+ << " pending " << transport_ -> pending() << " blocked "
+ << transport_ -> blocked() << " length " << transport_ ->
+ length() << ".\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Include the proxy descriptor.
+ //
+
+ FD_SET(fd_, fdSet);
+
+ if (fd_ >= fdMax)
+ {
+ fdMax = fd_ + 1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Proxy descriptor FD#" << fd_
+ << " selected for read with buffer length "
+ << transport_ -> length() << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// Add to the mask the file descriptors of all
+// X connections to write to.
+//
+
+int Proxy::setWriteDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax)
+{
+ int fd = -1;
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL)
+ {
+ fd = getFd(channelId);
+
+ if (transports_[channelId] -> length() > 0)
+ {
+ FD_SET(fd, fdSet);
+
+ #ifdef TEST
+ *logofs << "Proxy: Descriptor FD#" << fd << " selected "
+ << "for write with blocked " << transports_[channelId] ->
+ blocked() << " and length " << transports_[channelId] ->
+ length() << ".\n" << logofs_flush;
+ #endif
+
+ if (fd >= fdMax)
+ {
+ fdMax = fd + 1;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Proxy: Descriptor FD#" << fd << " not selected "
+ << "for write with blocked " << transports_[channelId] ->
+ blocked() << " and length " << transports_[channelId] ->
+ length() << ".\n" << logofs_flush;
+ }
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+
+ if (transports_[channelId] -> getType() !=
+ transport_agent && transports_[channelId] ->
+ length() > 0 && transports_[channelId] ->
+ blocked() != 1)
+ {
+ *logofs << "Proxy: PANIC! Descriptor FD#" << fd
+ << " has data to write but blocked flag is "
+ << transports_[channelId] -> blocked()
+ << ".\n" << logofs_flush;
+
+ cerr << "Error" << ": Descriptor FD#" << fd
+ << " has data to write but blocked flag is "
+ << transports_[channelId] -> blocked()
+ << ".\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+ }
+ }
+
+ //
+ // Check if the proxy transport has data
+ // from a previous blocking write.
+ //
+
+ if (transport_ -> blocked() == 1)
+ {
+ FD_SET(fd_, fdSet);
+
+ #ifdef TEST
+ *logofs << "Proxy: Proxy descriptor FD#"
+ << fd_ << " selected for write. Blocked is "
+ << transport_ -> blocked() << " length is "
+ << transport_ -> length() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (fd_ >= fdMax)
+ {
+ fdMax = fd_ + 1;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Proxy: Proxy descriptor FD#"
+ << fd_ << " not selected for write. Blocked is "
+ << transport_ -> blocked() << " length is "
+ << transport_ -> length() << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // We are entering the main select. Save
+ // the timestamp of the last loop so that
+ // we can detect the clock drifts.
+ //
+
+ timeouts_.loopTs = getTimestamp();
+
+ return 1;
+}
+
+int Proxy::getChannels(T_channel_type type)
+{
+ int channels = 0;
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL &&
+ (type == channel_none ||
+ type == channels_[channelId] ->
+ getType()))
+ {
+ channels++;
+ }
+ }
+
+ return channels;
+}
+
+T_channel_type Proxy::getType(int fd)
+{
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return channel_none;
+ }
+
+ return channels_[channelId] -> getType();
+}
+
+const char *Proxy::getTypeName(T_channel_type type)
+{
+ switch (type)
+ {
+ case channel_x11:
+ {
+ return "X";
+ }
+ case channel_cups:
+ {
+ return "CUPS";
+ }
+ case channel_smb:
+ {
+ return "SMB";
+ }
+ case channel_media:
+ {
+ return "media";
+ }
+ case channel_http:
+ {
+ return "HTTP";
+ }
+ case channel_font:
+ {
+ return "font";
+ }
+ case channel_slave:
+ {
+ return "slave";
+ }
+ default:
+ {
+ return "unknown";
+ }
+ }
+}
+
+const char *Proxy::getComputerName()
+{
+ //
+ // Strangely enough, under some Windows OSes SMB
+ // service doesn't bind to localhost. Fall back
+ // to localhost if can't find computer name in
+ // the environment. In future we should try to
+ // bind to localhost and then try the other IPs.
+ //
+
+ const char *hostname = NULL;
+
+ #ifdef __CYGWIN32__
+
+ hostname = getenv("COMPUTERNAME");
+
+ #endif
+
+ if (hostname == NULL)
+ {
+ hostname = "localhost";
+ }
+
+ return hostname;
+}
+
+//
+// Handle data from channels selected for read.
+//
+
+int Proxy::handleRead(int &resultFds, fd_set &readSet)
+{
+ #ifdef DEBUG
+ *logofs << "Proxy: Checking descriptors selected for read.\n"
+ << logofs_flush;
+ #endif
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Looping with current channel "
+ << *j << ".\n" << logofs_flush;
+ #endif
+
+ int fd = getFd(*j);
+
+ if (fd >= 0 && resultFds > 0 && FD_ISSET(fd, &readSet))
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Going to read messages from FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ int result = handleRead(fd);
+
+ if (result < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failure reading messages from FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Clearing the read descriptor "
+ << "for FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ FD_CLR(fd, &readSet);
+
+ resultFds--;
+ }
+ }
+
+ if (resultFds > 0 && FD_ISSET(fd_, &readSet))
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Going to read messages from "
+ << "proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (handleRead() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failure reading from proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Clearing the read descriptor "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ FD_CLR(fd_, &readSet);
+
+ resultFds--;
+ }
+
+ return 1;
+}
+
+//
+// Perform flush on descriptors selected for write.
+//
+
+int Proxy::handleFlush(int &resultFds, fd_set &writeSet)
+{
+ #ifdef DEBUG
+ *logofs << "Proxy: Checking descriptors selected for write.\n"
+ << logofs_flush;
+ #endif
+
+ if (resultFds > 0 && FD_ISSET(fd_, &writeSet))
+ {
+ #ifdef TEST
+ *logofs << "Proxy: FLUSH! Proxy descriptor FD#" << fd_
+ << " reported to be writable.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleFlush() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failure flushing the writable "
+ << "proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Clearing the write descriptor "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ FD_CLR(fd_, &writeSet);
+
+ resultFds--;
+ }
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ resultFds > 0 && j != channelList.end(); j++)
+ {
+ #ifdef DEBUG
+ *logofs << "Proxy: Looping with current channel "
+ << *j << ".\n" << logofs_flush;
+ #endif
+
+ int fd = getFd(*j);
+
+ if (fd >= 0 && FD_ISSET(fd, &writeSet))
+ {
+ #ifdef TEST
+ *logofs << "Proxy: X descriptor FD#" << fd
+ << " reported to be writable.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // It can happen that, in handling reads, we
+ // have destroyed the buffer associated to a
+ // closed socket, so don't complain about
+ // the errors.
+ //
+
+ handleFlush(fd);
+
+ //
+ // Clear the descriptor from the mask so
+ // we don't confuse the agent if it's
+ // not checking only its own descriptors.
+ //
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Clearing the write descriptor "
+ << "for FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ FD_CLR(fd, &writeSet);
+
+ resultFds--;
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleRead()
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Decoding data from proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Decode all the available messages from
+ // the remote proxy until is not possible
+ // to read more.
+ //
+
+ for (;;)
+ {
+ int result = readBuffer_.readMessage();
+
+ #if defined(TEST) || defined(DEBUG) || defined(INFO)
+ *logofs << "Proxy: Read result on proxy FD#" << fd_
+ << " is " << result << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (result < 0)
+ {
+ if (shutdown_ == 0)
+ {
+ if (finish_ == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Failure reading from the "
+ << "peer proxy on FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure reading from the "
+ << "peer proxy.\n";
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Proxy: Closure of the proxy link detected "
+ << "after clean shutdown.\n" << logofs_flush;
+ }
+ #endif
+
+ priority_ = 0;
+ finish_ = 1;
+ congestion_ = 0;
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ #if defined(TEST) || defined(DEBUG) || defined(INFO)
+ *logofs << "Proxy: No data read from proxy FD#"
+ << fd_ << "\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // We read some data from the remote. If we set
+ // the congestion flag because we couldn't read
+ // before the timeout and have tokens available,
+ // then reset the congestion flag.
+ //
+
+ if (congestion_ == 1 &&
+ tokens_[token_control].remaining > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Exiting congestion due to "
+ << "proxy data with " << tokens_[token_control].remaining
+ << " tokens.\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ }
+
+ //
+ // Set the timestamp of the last read
+ // operation from the remote proxy and
+ // enable again showing the 'no data
+ // received' dialog at the next timeout.
+ //
+
+ timeouts_.readTs = getTimestamp();
+
+ if (alert_ != 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Displacing the dialog "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleAlert(DISPLACE_MESSAGE_ALERT, 1);
+ }
+
+ timeouts_.alertTs = nullTimestamp();
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Getting messages from proxy FD#" << fd_
+ << " with " << readBuffer_.getLength() << " bytes "
+ << "in the read buffer.\n" << logofs_flush;
+ #endif
+
+ unsigned int controlLength;
+ unsigned int dataLength;
+
+ const unsigned char *message;
+
+ while ((message = readBuffer_.getMessage(controlLength, dataLength)) != NULL)
+ {
+ statistics -> addFrameIn();
+
+ if (controlLength == 3 && *message == 0 &&
+ *(message + 1) < code_last_tag)
+ {
+ if (handleControlFromProxy(message) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (operation_ == operation_in_messages)
+ {
+ int channelId = inputChannel_;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Identified message of " << dataLength
+ << " bytes for FD#" << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ if (channelId >= 0 && channelId < CONNECTIONS_LIMIT &&
+ channels_[channelId] != NULL)
+ {
+ int finish = channels_[channelId] -> getFinish();
+
+ #ifdef WARNING
+
+ if (finish == 1)
+ {
+ *logofs << "Proxy: WARNING! Handling data for finishing "
+ << "FD#" << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // We need to decode all the data to preserve
+ // the consistency of the cache, so can't re-
+ // turn as soon as the first error is encount-
+ // ered. Check if this is the first time that
+ // the failure is detected.
+ //
+
+ int _result = channels_[channelId] -> handleWrite(message, dataLength);
+
+ if (_result < 0 && finish == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failed to write proxy data to FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Check if we have splits or motion
+ // events to send.
+ //
+
+ setSplitTimeout(channelId);
+ setMotionTimeout(channelId);
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Proxy: WARNING! Received data for "
+ << "invalid channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+ #endif
+ }
+ else if (operation_ == operation_in_statistics)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Received statistics data from remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleStatisticsFromProxy(message, dataLength) < 0)
+ {
+ return -1;
+ }
+
+ operation_ = operation_in_messages;
+ }
+ else if (operation_ == operation_in_negotiation)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Received new negotiation data from remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleNegotiationFromProxy(message, dataLength) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // if (controlLength == 3 && *message == 0 && ...) ...
+ // else if (operation_ == operation_in_statistics) ...
+ // else if (operation_ == operation_in_messages) ...
+ // else if (operation_ == operation_in_negotiation) ...
+ // else ...
+ //
+
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Unrecognized message received on proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unrecognized message received on proxy FD#"
+ << fd_ << ".\n";
+
+ return -1;
+ }
+
+ } // while ((message = readBuffer_.getMessage(controlLength, dataLength)) != NULL) ...
+
+ //
+ // Reset the read buffer.
+ //
+
+ readBuffer_.fullReset();
+
+ //
+ // Give up if no data is readable.
+ //
+
+ if (transport_ -> readable() == 0)
+ {
+ break;
+ }
+
+ } // End of for (;;) ...
+
+ return 1;
+}
+
+int Proxy::handleControlFromProxy(const unsigned char *message)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Received message '" << DumpControl(*(message + 1))
+ << "' at " << strMsTimestamp() << " with data ID#"
+ << (int) *(message + 2) << ".\n" << logofs_flush;
+ #endif
+
+ T_channel_type channelType = channel_none;
+
+ switch (*(message + 1))
+ {
+ case code_switch_connection:
+ {
+ int channelId = *(message + 2);
+
+ //
+ // If channel is invalid further messages will
+ // be ignored. The acknowledged shutdown of
+ // channels should prevent this.
+ //
+
+ inputChannel_ = channelId;
+
+ break;
+ }
+ case code_begin_congestion:
+ {
+ //
+ // Set the congestion state for the
+ // channel reported by the remote.
+ //
+
+ int channelId = *(message + 2);
+
+ if (channels_[channelId] != NULL)
+ {
+ congestions_[channelId] = 1;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Received a begin congestion "
+ << "for channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (channelId == agent_ && congestions_[agent_] != 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Forcing an update of the congestion "
+ << "counter with agent congested.\n"
+ << logofs_flush;
+ #endif
+
+ statistics -> updateCongestion(-tokens_[token_control].remaining,
+ tokens_[token_control].limit);
+ }
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Proxy: WARNING! Received a begin congestion "
+ << "for invalid channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ break;
+ }
+ case code_end_congestion:
+ {
+ //
+ // Attend again to the channel.
+ //
+
+ int channelId = *(message + 2);
+
+ if (channels_[channelId] != NULL)
+ {
+ congestions_[channelId] = 0;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Received an end congestion "
+ << "for channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (channelId == agent_ && congestions_[agent_] != 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Forcing an update of the congestion "
+ << "counter with agent decongested.\n"
+ << logofs_flush;
+ #endif
+
+ statistics -> updateCongestion(tokens_[token_control].remaining,
+ tokens_[token_control].limit);
+ }
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Proxy: WARNING! Received an end congestion "
+ << "for invalid channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ break;
+ }
+ case code_control_token_request:
+ {
+ T_proxy_token &token = tokens_[token_control];
+
+ if (handleTokenFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_split_token_request:
+ {
+ T_proxy_token &token = tokens_[token_split];
+
+ if (handleTokenFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_data_token_request:
+ {
+ T_proxy_token &token = tokens_[token_data];
+
+ if (handleTokenFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_control_token_reply:
+ {
+ T_proxy_token &token = tokens_[token_control];
+
+ if (handleTokenReplyFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_split_token_reply:
+ {
+ T_proxy_token &token = tokens_[token_split];
+
+ if (handleTokenReplyFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_data_token_reply:
+ {
+ T_proxy_token &token = tokens_[token_data];
+
+ if (handleTokenReplyFromProxy(token, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_new_x_connection:
+ {
+ //
+ // Opening the channel is handled later.
+ //
+
+ channelType = channel_x11;
+
+ break;
+ }
+ case code_new_cups_connection:
+ {
+ channelType = channel_cups;
+
+ break;
+ }
+ case code_new_aux_connection:
+ {
+ //
+ // Starting from version 1.5.0 we create real X
+ // connections for the keyboard channel. We need
+ // to refuse old auxiliary X connections because
+ // they would be unable to leverage the new fake
+ // authorization cookie.
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Can't open outdated auxiliary X "
+ << "channel for code " << *(message + 1) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't open outdated auxiliary X "
+ << "channel for code " << *(message + 1) << ".\n";
+
+ if (handleControl(code_drop_connection, *(message + 2)) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_new_smb_connection:
+ {
+ channelType = channel_smb;
+
+ break;
+ }
+ case code_new_media_connection:
+ {
+ channelType = channel_media;
+
+ break;
+ }
+ case code_new_http_connection:
+ {
+ channelType = channel_http;
+
+ break;
+ }
+ case code_new_font_connection:
+ {
+ channelType = channel_font;
+
+ break;
+ }
+ case code_new_slave_connection:
+ {
+ channelType = channel_slave;
+
+ break;
+ }
+ case code_drop_connection:
+ {
+ int channelId = *(message + 2);
+
+ if (channelId >= 0 && channelId < CONNECTIONS_LIMIT &&
+ channels_[channelId] != NULL)
+ {
+ handleDropFromProxy(channelId);
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Proxy: WARNING! Received a drop message "
+ << "for invalid channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ break;
+ }
+ case code_finish_connection:
+ {
+ int channelId = *(message + 2);
+
+ if (channelId >= 0 && channelId < CONNECTIONS_LIMIT &&
+ channels_[channelId] != NULL)
+ {
+ //
+ // Force the finish state on the channel.
+ // We can receive this message while in
+ // the read loop, so we only mark the
+ // channel for deletion.
+ //
+
+ #ifdef TEST
+ *logofs << "Proxy: Received a finish message for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ handleFinishFromProxy(channelId);
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "Proxy: WARNING! Received a finish message "
+ << "for invalid channel id ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ break;
+ }
+ case code_finish_listeners:
+ {
+ //
+ // This is from the main loop.
+ //
+
+ #ifdef TEST
+ *logofs << "Proxy: Closing down all local listeners.\n"
+ << logofs_flush;
+ #endif
+
+ CleanupListeners();
+
+ finish_ = 1;
+
+ break;
+ }
+ case code_reset_request:
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Proxy reset not supported "
+ << "in this version.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Proxy reset not supported "
+ << "in this version.\n";
+
+ HandleCleanup();
+ }
+ case code_shutdown_request:
+ {
+ //
+ // Time to rest in peace.
+ //
+
+ shutdown_ = 1;
+
+ break;
+ }
+ case code_load_request:
+ {
+ if (handleLoadFromProxy() < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_save_request:
+ {
+ //
+ // Don't abort the connection
+ // if can't write to disk.
+ //
+
+ handleSaveFromProxy();
+
+ break;
+ }
+ case code_statistics_request:
+ {
+ int type = *(message + 2);
+
+ if (handleStatisticsFromProxy(type) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_statistics_reply:
+ {
+ operation_ = operation_in_statistics;
+
+ break;
+ }
+ case code_alert_request:
+ {
+ HandleAlert(*(message + 2), 1);
+
+ break;
+ }
+ case code_sync_request:
+ {
+ int channelId = *(message + 2);
+
+ if (handleSyncFromProxy(channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case code_sync_reply:
+ {
+ //
+ // We are not the one that issued
+ // the request.
+ //
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: PANIC! Received an unexpected "
+ << "synchronization reply.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Received an unexpected "
+ << "synchronization reply.\n";
+
+ HandleCleanup();
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Received bad control message number "
+ << (unsigned int) *(message + 1) << " with attribute "
+ << (unsigned int) *(message + 2) << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Received bad control message number "
+ << (unsigned int) *(message + 1) << " with attribute "
+ << (unsigned int) *(message + 2) << ".\n";
+
+ HandleCleanup();
+ }
+
+ } // End of switch (*(message + 1)) ...
+
+ if (channelType == channel_none)
+ {
+ return 1;
+ }
+
+ //
+ // Handle the channel allocation that we
+ // left from the main switch case.
+ //
+
+ int channelId = *(message + 2);
+
+ //
+ // Check if the channel has been dropped.
+ //
+
+ if (channels_[channelId] != NULL &&
+ (channels_[channelId] -> getDrop() == 1 ||
+ channels_[channelId] -> getClosing() == 1))
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Dropping the descriptor FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ handleDrop(channelId);
+ }
+
+ //
+ // Check if the channel is in the valid
+ // range.
+ //
+
+ int result = checkChannelMap(channelId);
+
+ if (result >= 0)
+ {
+ result = handleNewConnectionFromProxy(channelType, channelId);
+ }
+
+ if (result < 0)
+ {
+ //
+ // Realization of new channel failed.
+ // Send channel shutdown message to
+ // the peer proxy.
+ //
+
+ if (handleControl(code_drop_connection, channelId) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ int fd = getFd(channelId);
+
+ if (getReadable(fd) > 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Trying to read immediately "
+ << "from descriptor FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (handleRead(fd) < 0)
+ {
+ return -1;
+ }
+ }
+ #ifdef TEST
+ *logofs << "Proxy: Nothing to read immediately "
+ << "from descriptor FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ return 1;
+}
+
+int Proxy::handleRead(int fd, const char *data, int size)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Handling data for connection on FD#"
+ << fd << ".\n" << logofs_flush;
+ #endif
+
+ if (canRead(fd) == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (getChannel(fd) < 0)
+ {
+ *logofs << "Proxy: PANIC! Can't read from invalid FD#"
+ << fd << ".\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+ else
+ {
+ *logofs << "Proxy: WARNING! Read method called for FD#"
+ << fd << " but operation is not possible.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+
+ int channelId = getChannel(fd);
+
+ //
+ // Let the channel object read all the new data from
+ // its file descriptor, isolate messages, compress
+ // those messages, and append the compressed form to
+ // the encode buffer.
+ //
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Reading messages from FD#" << fd
+ << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = channels_[channelId] -> handleRead(encodeBuffer_, (const unsigned char *) data,
+ (unsigned int) size);
+
+ //
+ // Even in the case of a failure, write the produced
+ // data to the proxy connection. To keep the stores
+ // synchronized, the remote side needs to decode any
+ // message encoded by this side, also if the X socket
+ // was closed in the meanwhile. If this is the case,
+ // the decompressed output will be silently discarded.
+ //
+
+ if (result < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failed to read data from connection FD#"
+ << fd << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Check if there are new splits or
+ // motion events to send.
+ //
+
+ setSplitTimeout(channelId);
+ setMotionTimeout(channelId);
+
+ return 1;
+}
+
+int Proxy::handleEvents()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Going to check the events on channels.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if we can safely write to the
+ // proxy link.
+ //
+
+ int read = isTimeToRead();
+
+ //
+ // Loop on channels and send the pending
+ // events. We must copy the list because
+ // channels can be removed in the middle
+ // of the loop.
+ //
+
+ T_list channelList = activeChannels_.copyList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] == NULL)
+ {
+ continue;
+ }
+
+ //
+ // Check if we need to drop the channel.
+ //
+
+ if (channels_[channelId] -> getDrop() == 1 ||
+ channels_[channelId] -> getClosing() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Dropping the descriptor FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ if (handleDrop(channelId) < 0)
+ {
+ return -1;
+ }
+
+ continue;
+ }
+ else if (channels_[channelId] -> getFinish() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Skipping finishing "
+ << "descriptor FD#" << getFd(channelId)
+ << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+
+ //
+ // If the proxy link or the channel is
+ // in congestion state, don't handle
+ // the further events.
+ //
+
+ if (read == 0 || congestions_[channelId] == 1)
+ {
+ #ifdef TEST
+
+ if (read == 0)
+ {
+ *logofs << "Proxy: Can't handle events for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " with proxy not available.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Proxy: Can't handle events for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " with channel congested.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ continue;
+ }
+
+ //
+ // Handle the timeouts on the channel
+ // operations.
+ //
+
+ int result = 0;
+
+ //
+ // Handle the motion events.
+ //
+
+ if (result >= 0 && channels_[channelId] -> needMotion() == 1)
+ {
+ if (isTimeToMotion() == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+
+ *logofs << "Proxy: FLUSH! Motion timeout expired after "
+ << diffTimestamp(timeouts_.motionTs, getTimestamp())
+ << " Ms.\n" << logofs_flush;
+
+ #endif
+
+ result = channels_[channelId] -> handleMotion(encodeBuffer_);
+
+ #ifdef TEST
+
+ if (result < 0)
+ {
+ *logofs << "Proxy: Failed to handle motion events for FD#"
+ << getFd(channelId) << " channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ timeouts_.motionTs = nullTimestamp();
+
+ setMotionTimeout(channelId);
+ }
+ #if defined(TEST) || defined(INFO)
+ else if (isTimestamp(timeouts_.motionTs) == 1)
+ {
+ *logofs << "Proxy: Running with "
+ << diffTimestamp(timeouts_.motionTs, getTimestamp())
+ << " Ms elapsed since the last motion.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ if (result >= 0 && channels_[channelId] -> needSplit() == 1)
+ {
+ //
+ // Check if it is time to send more splits
+ // and how many bytes are going to be sent.
+ //
+
+ if (isTimeToSplit() == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ *logofs << "Proxy: SPLIT! Split timeout expired after "
+ << diffTimestamp(timeouts_.splitTs, getTimestamp())
+ << " Ms.\n" << logofs_flush;
+ #endif
+
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+
+ *logofs << "Proxy: SPLIT! Encoding splits for FD#"
+ << getFd(channelId) << " at " << strMsTimestamp()
+ << " with " << clientStore_ -> getSplitTotalStorageSize()
+ << " total bytes and " << control -> SplitDataPacketLimit
+ << " bytes " << "to write.\n"
+ << logofs_flush;
+
+ #endif
+
+ result = channels_[channelId] -> handleSplit(encodeBuffer_);
+
+ #ifdef TEST
+
+ if (result < 0)
+ {
+ *logofs << "Proxy: Failed to handle splits for FD#"
+ << getFd(channelId) << " channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ timeouts_.splitTs = nullTimestamp();
+
+ setSplitTimeout(channelId);
+ }
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ else if (channels_[channelId] -> needSplit() == 1 &&
+ isTimestamp(timeouts_.splitTs) == 0)
+ {
+ *logofs << "Proxy: SPLIT! WARNING! Channel for FD#"
+ << getFd(channelId) << " has split to send but "
+ << "there is no timeout.\n" << logofs_flush;
+ }
+ else if (isTimestamp(timeouts_.splitTs) == 1)
+ {
+ *logofs << "Proxy: SPLIT! Running with "
+ << diffTimestamp(timeouts_.splitTs, getTimestamp())
+ << " Ms elapsed since the last split.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ if (result < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Error handling events for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleFrame(T_frame_type type)
+{
+ //
+ // Write any outstanding control message, followed by the
+ // content of the encode buffer, to the proxy transport.
+ //
+ // This code assumes that the encode buffer data is at an
+ // offset several bytes from start of the buffer, so that
+ // the length header and any necessary control bytes can
+ // be inserted in front of the data already in the buffer.
+ // This is the easiest way to encapsulate header and data
+ // together in a single frame.
+ //
+ // The way framing is implemented is very efficient but
+ // inherently limited and does not allow for getting the
+ // best performance, especially when running over a fast
+ // link. Framing should be rewritten to include the length
+ // of the packets in a fixed size header and, possibly,
+ // to incapsulate the control messages and the channel's
+ // data in a pseudo X protocol message, so that the proxy
+ // itself would be treated like any other channel.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ if (congestion_ == 1)
+ {
+ //
+ // This can happen because there may be control
+ // messages to send, like a proxy shutdown mes-
+ // sage or a statistics request. All the other
+ // cases should be considered an error.
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Data is to be sent while "
+ << "congestion is " << congestion_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ #endif
+
+ //
+ // Check if there is any data available on
+ // the socket. Recent Linux kernels are very
+ // picky. They require that we read often or
+ // they assume that the process is non-inter-
+ // active.
+ //
+
+ if (handleAsyncEvents() < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Check if this is a ping, not a data frame.
+ //
+
+ if (type == frame_ping)
+ {
+ if (handleToken(frame_ping) < 0)
+ {
+ return -1;
+ }
+ }
+
+ unsigned int dataLength = encodeBuffer_.getLength();
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Data length is " << dataLength
+ << " control length is " << controlLength_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (dataLength > 0)
+ {
+ //
+ // If this is a generic channel we need
+ // to add the completion bits. Data can
+ // also have been encoded because of a
+ // statistics request, even if no output
+ // channel was currently selected.
+ //
+
+ if (outputChannel_ != -1)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (channels_[outputChannel_] == NULL)
+ {
+ *logofs << "Proxy: PANIC! A new frame was requested "
+ << "but the channel is invalid.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ channels_[outputChannel_] -> handleCompletion(encodeBuffer_);
+
+ dataLength = encodeBuffer_.getLength();
+ }
+ }
+ else if (controlLength_ == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ *logofs << "Proxy: PANIC! A new frame was requested "
+ << "but there is no data to write.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+
+ #endif
+
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Data length is now " << dataLength
+ << " control length is " << controlLength_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if this frame needs to carry a new
+ // token request.
+ //
+
+ if (type == frame_data)
+ {
+ if (handleToken(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Adding a new frame for the remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char temp[5];
+
+ unsigned int lengthLength = 0;
+ unsigned int shift = dataLength;
+
+ while (shift)
+ {
+ temp[lengthLength++] = (unsigned char) (shift & 0x7f);
+
+ shift >>= 7;
+ }
+
+ unsigned char *data = encodeBuffer_.getData();
+
+ unsigned char *outputMessage = data - (controlLength_ + lengthLength);
+
+ unsigned char *nextDest = outputMessage;
+
+ for (int i = 0; i < controlLength_; i++)
+ {
+ *nextDest++ = controlCodes_[i];
+ }
+
+ for (int j = lengthLength - 1; j > 0; j--)
+ {
+ *nextDest++ = (temp[j] | 0x80);
+ }
+
+ if (lengthLength)
+ {
+ *nextDest++ = temp[0];
+ }
+
+ unsigned int outputLength = dataLength + controlLength_ + lengthLength;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Produced plain output for " << dataLength << "+"
+ << controlLength_ << "+" << lengthLength << " out of "
+ << outputLength << " bytes.\n" << logofs_flush;
+ #endif
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH) || defined(TIME)
+
+ T_timestamp nowTs = getTimestamp();
+
+ *logofs << "Proxy: FLUSH! Immediate with blocked " << transport_ ->
+ blocked() << " length " << transport_ -> length()
+ << " new " << outputLength << " flushable " << transport_ ->
+ flushable() << " tokens " << tokens_[token_control].remaining
+ << " after " << diffTimestamp(timeouts_.writeTs, nowTs)
+ << " Ms.\n" << logofs_flush;
+
+ *logofs << "Proxy: FLUSH! Immediate flush to proxy FD#" << fd_
+ << " of " << outputLength << " bytes at " << strMsTimestamp()
+ << " with priority " << priority_ << ".\n" << logofs_flush;
+
+ *logofs << "Proxy: FLUSH! Current bitrate is "
+ << statistics -> getBitrateInShortFrame() << " with "
+ << statistics -> getBitrateInLongFrame() << " in the "
+ << "long frame and top " << statistics ->
+ getTopBitrate() << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addWriteOut();
+
+ int result = transport_ -> write(write_immediate, outputMessage, outputLength);
+
+ #ifdef TIME
+
+ if (diffTimestamp(timeouts_.writeTs, nowTs) > 50)
+ {
+ *logofs << "Proxy: WARNING! TIME! Data written to proxy FD#"
+ << fd_ << " at " << strMsTimestamp() << " after "
+ << diffTimestamp(timeouts_.writeTs, nowTs)
+ << " Ms.\n" << logofs_flush;
+ }
+
+ #endif
+
+ #ifdef DUMP
+ *logofs << "Proxy: Sent " << outputLength << " bytes of data "
+ << "with checksum ";
+
+ DumpChecksum(outputMessage, outputLength);
+
+ *logofs << " on proxy FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DUMP
+ *logofs << "Proxy: Partial checksums are:\n";
+
+ DumpBlockChecksums(outputMessage, outputLength, 256);
+
+ *logofs << logofs_flush;
+ #endif
+
+ //
+ // Clean up the encode buffer and
+ // bring it to the initial size.
+ //
+
+ encodeBuffer_.fullReset();
+
+ //
+ // Close the connection if we got
+ // an error.
+ //
+
+ if (result < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failed write to proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Account for the data frame and the
+ // framing overhead.
+ //
+
+ if (dataLength > 0)
+ {
+ statistics -> addFrameOut();
+ }
+
+ statistics -> addFramingBits((controlLength_ + lengthLength) << 3);
+
+ controlLength_ = 0;
+
+ //
+ // Reset all buffers, counters and the
+ // priority flag.
+ //
+
+ handleResetFlush();
+
+ //
+ // Check if more data became available
+ // after writing.
+ //
+
+ if (handleAsyncEvents() < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Drain the proxy link if we are in
+ // congestion state.
+ //
+ // if (needDrain() == 1 && draining_ == 0)
+ // {
+ // if (handleDrain() < 0)
+ // {
+ // return -1;
+ // }
+ // }
+ //
+
+ return result;
+}
+
+int Proxy::handleFlush()
+{
+ //
+ // We can have data in the encode buffer or
+ // control bytes to send. In the case make
+ // up a new frame.
+ //
+
+ if (encodeBuffer_.getLength() + controlLength_ > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Flushing data in the encode buffer.\n"
+ << logofs_flush;
+ #endif
+
+ priority_ = 1;
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Check if we have something to write.
+ //
+
+ if (transport_ -> length() + transport_ -> flushable() == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Nothing else to flush for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (transport_ -> blocked() == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Proxy descriptor FD#" << fd_
+ << " has data to flush but the transport "
+ << "is not blocked.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Proxy descriptor FD#" << fd_
+ << " has data to flush but the transport "
+ << "is not blocked.\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: FLUSH! Deferred with blocked " << transport_ ->
+ blocked() << " length " << transport_ -> length()
+ << " flushable " << transport_ -> flushable() << " tokens "
+ << tokens_[token_control].remaining << ".\n"
+ << logofs_flush;
+
+ *logofs << "Proxy: FLUSH! Deferred flush to proxy FD#" << fd_
+ << " of " << transport_ -> length() + transport_ ->
+ flushable() << " bytes at " << strMsTimestamp()
+ << " with priority " << priority_ << ".\n"
+ << logofs_flush;
+
+ *logofs << "Proxy: FLUSH! Current bitrate is "
+ << statistics -> getBitrateInShortFrame() << " with "
+ << statistics -> getBitrateInLongFrame() << " in the "
+ << "long frame and top " << statistics ->
+ getTopBitrate() << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addWriteOut();
+
+ int result = transport_ -> flush();
+
+ if (result < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset the counters and update the
+ // timestamp of the last write.
+ //
+
+ handleResetFlush();
+
+ return result;
+}
+
+int Proxy::handleDrain()
+{
+ //
+ // If the proxy is run in the same process
+ // as SSH, we can't block or the program
+ // would not have a chance to read or write
+ // its data.
+ //
+
+ if (control -> LinkEncrypted == 1)
+ {
+ return 0;
+ }
+
+ if (needDrain() == 0 || draining_ == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (draining_ == 1)
+ {
+ *logofs << "Proxy: WARNING! Already draining proxy FD#"
+ << fd_ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Proxy: WARNING! No need to drain proxy FD#"
+ << fd_ << " with congestion " << congestion_
+ << " length " << transport_ -> length()
+ << " and blocked " << transport_ -> blocked()
+ << ".\n" << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+
+ draining_ = 1;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Going to drain the proxy FD#" << fd_
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int timeout = control -> PingTimeout / 2;
+
+ T_timestamp startTs = getNewTimestamp();
+
+ T_timestamp nowTs = startTs;
+
+ int remaining;
+ int result;
+
+ //
+ // Keep draining the proxy socket while
+ // reading the incoming messages until
+ // the timeout is expired.
+ //
+
+ for (;;)
+ {
+ remaining = timeout - diffTimestamp(startTs, nowTs);
+
+ if (remaining <= 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Timeout raised while draining "
+ << "FD#" << fd_ << " at " << strMsTimestamp()
+ << " after " << diffTimestamp(startTs, nowTs)
+ << " Ms.\n" << logofs_flush;
+ #endif
+
+ result = 0;
+
+ goto ProxyDrainEnd;
+ }
+
+ if (transport_ -> length() > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Trying to write to FD#" << fd_
+ << " at " << strMsTimestamp() << " with length "
+ << transport_ -> length() << " and "
+ << remaining << " Ms remaining.\n"
+ << logofs_flush;
+ #endif
+
+ result = transport_ -> drain(0, remaining);
+
+ if (result == -1)
+ {
+ result = -1;
+
+ goto ProxyDrainEnd;
+ }
+ else if (result == 0 && transport_ -> readable() > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Decoding more data from proxy FD#"
+ << fd_ << " at " << strMsTimestamp() << " with "
+ << transport_ -> length() << " bytes to write and "
+ << transport_ -> readable() << " readable.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleRead() < 0)
+ {
+ result = -1;
+
+ goto ProxyDrainEnd;
+ }
+ }
+ #if defined(TEST) || defined(INFO)
+ else if (result == 1)
+ {
+ *logofs << "Proxy: Transport for proxy FD#" << fd_
+ << " drained down to " << transport_ -> length()
+ << " bytes.\n" << logofs_flush;
+ }
+ #endif
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Waiting for more data from proxy "
+ << "FD#" << fd_ << " at " << strMsTimestamp()
+ << " with " << remaining << " Ms remaining.\n"
+ << logofs_flush;
+ #endif
+
+
+ result = transport_ -> wait(remaining);
+
+ if (result == -1)
+ {
+ result = -1;
+
+ goto ProxyDrainEnd;
+ }
+ else if (result > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Decoding more data from proxy FD#"
+ << fd_ << " at " << strMsTimestamp() << " with "
+ << transport_ -> readable() << " bytes readable.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleRead() < 0)
+ {
+ result = -1;
+
+ goto ProxyDrainEnd;
+ }
+ }
+ }
+
+ //
+ // Check if we finally got the tokens
+ // that would allow us to come out of
+ // the congestion state.
+ //
+
+ if (needDrain() == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Got decongestion for proxy FD#"
+ << fd_ << " at " << strMsTimestamp() << " after "
+ << diffTimestamp(startTs, getTimestamp())
+ << " Ms.\n" << logofs_flush;
+ #endif
+
+ result = 1;
+
+ goto ProxyDrainEnd;
+ }
+
+ nowTs = getNewTimestamp();
+ }
+
+ProxyDrainEnd:
+
+ draining_ = 0;
+
+ return result;
+}
+
+int Proxy::handleFlush(int fd)
+{
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: WARNING! Skipping flush on invalid "
+ << "descriptor FD#" << fd << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (channels_[channelId] -> getFinish() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Skipping flush on finishing "
+ << "descriptor FD#" << fd << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Going to flush FD#" << fd
+ << " with blocked " << transports_[channelId] -> blocked()
+ << " length " << transports_[channelId] -> length()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (channels_[channelId] -> handleFlush() < 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Failed to flush data to FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ handleFinish(channelId);
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleStatistics(int type, ostream *stream)
+{
+ if (stream == NULL || control -> EnableStatistics == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Cannot produce statistics "
+ << " for proxy FD#" << fd_ << ". Invalid settings "
+ << "for statistics or stream.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (currentStatistics_ != NULL)
+ {
+ //
+ // Need to update the stream pointer as the
+ // previous one could have been destroyed.
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Replacing stream while producing "
+ << "statistics in stream at " << currentStatistics_
+ << " for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ currentStatistics_ = stream;
+
+ //
+ // Get statistics of remote peer.
+ //
+
+ if (handleControl(code_statistics_request, type) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleStatisticsFromProxy(int type)
+{
+ if (needFlush() == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: WARNING! Data for the previous "
+ << "channel ID#" << outputChannel_
+ << " flushed in statistics.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ if (control -> EnableStatistics == 1)
+ {
+ //
+ // Allocate a buffer for the output.
+ //
+
+ char *buffer = new char[STATISTICS_LENGTH];
+
+ *buffer = '\0';
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Producing "
+ << (type == TOTAL_STATS ? "total" : "partial")
+ << " client statistics for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> getClientProtocolStats(type, buffer);
+
+ statistics -> getClientOverallStats(type, buffer);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Producing "
+ << (type == TOTAL_STATS ? "total" : "partial")
+ << " server statistics for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> getServerProtocolStats(type, buffer);
+ }
+
+ if (type == PARTIAL_STATS)
+ {
+ statistics -> resetPartialStats();
+ }
+
+ unsigned int length = strlen((char *) buffer) + 1;
+
+ encodeBuffer_.encodeValue(type, 8);
+
+ encodeBuffer_.encodeValue(length, 32);
+
+ #ifdef TEST
+ *logofs << "Proxy: Encoding " << length
+ << " bytes of statistics data for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer_.encodeMemory((unsigned char *) buffer, length);
+
+ //
+ // Account statistics data as framing bits.
+ //
+
+ statistics -> addFramingBits(length << 3);
+
+ delete [] buffer;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Got statistics request "
+ << "but local statistics are disabled.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Got statistics request "
+ << "but local statistics are disabled.\n";
+
+ type = NO_STATS;
+
+ encodeBuffer_.encodeValue(type, 8);
+
+ #ifdef TEST
+ *logofs << "Proxy: Sending error code to remote proxy on FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ //
+ // The next write will flush the statistics
+ // data and the control message.
+ //
+
+ if (handleControl(code_statistics_reply, type) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleStatisticsFromProxy(const unsigned char *message, unsigned int length)
+{
+ if (currentStatistics_ == NULL)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Unexpected statistics data received "
+ << "from remote proxy on FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Unexpected statistics data received "
+ << "from remote proxy.\n";
+
+ return 0;
+ }
+
+ //
+ // Allocate the decode buffer and at least
+ // the 'type' field to see if there was an
+ // error.
+ //
+
+ DecodeBuffer decodeBuffer(message, length);
+
+ unsigned int type;
+
+ decodeBuffer.decodeValue(type, 8);
+
+ if (type == NO_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Couldn't get statistics from remote "
+ << "proxy on FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Couldn't get statistics from remote proxy.\n";
+ }
+ else if (type != TOTAL_STATS && type != PARTIAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n";
+
+ return -1;
+ }
+ else
+ {
+ unsigned int size;
+
+ decodeBuffer.decodeValue(size, 32);
+
+ char *buffer = new char[STATISTICS_LENGTH];
+
+ *buffer = '\0';
+
+ if (control -> EnableStatistics == 1)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Finalizing "
+ << (type == TOTAL_STATS ? "total" : "partial")
+ << " client statistics for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> getClientCacheStats(type, buffer);
+
+ #ifdef TEST
+ *logofs << "Proxy: Decoding " << size
+ << " bytes of statistics data for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size);
+
+ statistics -> getClientProtocolStats(type, buffer);
+
+ statistics -> getClientOverallStats(type, buffer);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Finalizing "
+ << (type == TOTAL_STATS ? "total" : "partial")
+ << " server statistics for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> getServerCacheStats(type, buffer);
+
+ statistics -> getServerProtocolStats(type, buffer);
+
+ #ifdef TEST
+ *logofs << "Proxy: Decoding " << size
+ << " bytes of statistics data for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size);
+ }
+
+ if (type == PARTIAL_STATS)
+ {
+ statistics -> resetPartialStats();
+ }
+
+ *currentStatistics_ << buffer;
+
+ //
+ // Mark the end of text to help external parsing.
+ //
+
+ *currentStatistics_ << '\4';
+
+ *currentStatistics_ << flush;
+ }
+ else
+ {
+ //
+ // It can be that statistics were enabled at the time
+ // we issued the request (otherwise we could not have
+ // set the stream), but now they have been disabled
+ // by user. We must decode statistics data if we want
+ // to keep the connection.
+ //
+
+ #ifdef TEST
+ *logofs << "Proxy: Discarding " << size
+ << " bytes of statistics data for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ strncat(buffer, (char *) decodeBuffer.decodeMemory(size), size);
+ }
+
+ delete [] buffer;
+ }
+
+ currentStatistics_ = NULL;
+
+ return 1;
+}
+
+int Proxy::handleNegotiation(const unsigned char *message, unsigned int length)
+{
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Writing data during proxy "
+ << "negotiation is not implemented.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Writing data during proxy "
+ << "negotiation is not implemented.\n";
+
+ return -1;
+}
+
+int Proxy::handleNegotiationFromProxy(const unsigned char *message, unsigned int length)
+{
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Reading data during proxy "
+ << "negotiation is not implemented.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Reading data during proxy "
+ << "negotiation is not implemented.\n";
+
+ return -1;
+}
+
+int Proxy::handleAlert(int alert)
+{
+ if (handleControl(code_alert_request, alert) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleCloseConnection(int clientFd)
+{
+ int channelId = getChannel(clientFd);
+
+ if (channels_[channelId] != NULL &&
+ channels_[channelId] -> getFinish() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Closing down the channel for FD#"
+ << clientFd << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int Proxy::handleCloseAllXConnections()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Closing down any remaining X channel.\n"
+ << logofs_flush;
+ #endif
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL &&
+ channels_[channelId] -> getType() == channel_x11 &&
+ channels_[channelId] -> getFinish() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Closing down the channel for FD#"
+ << getFd(channelId) << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleCloseAllListeners()
+{
+ // Since ProtoStep7 (#issue 108)
+ if (finish_ == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Closing down all remote listeners.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleControl(code_finish_listeners) < 0)
+ {
+ return -1;
+ }
+
+ finish_ = 1;
+ }
+
+ return 1;
+}
+
+void Proxy::handleResetAlert()
+{
+ if (alert_ != 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: The proxy alert '" << alert_
+ << "' was displaced.\n" << logofs_flush;
+ #endif
+
+ alert_ = 0;
+ }
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL)
+ {
+ channels_[channelId] -> handleResetAlert();
+ }
+ }
+}
+
+int Proxy::handleFinish(int channelId)
+{
+ //
+ // Send any outstanding encoded data and
+ // do any finalization needed on the
+ // channel.
+ //
+
+ if (needFlush(channelId) == 1)
+ {
+ if (channels_[channelId] -> getFinish() == 1)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! The finishing channel ID#"
+ << channelId << " has data to flush.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: WARNING! Flushing data for the "
+ << "finishing channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Reset the congestion state and the
+ // timeouts, if needed.
+ //
+
+ congestions_[channelId] = 0;
+
+ setSplitTimeout(channelId);
+ setMotionTimeout(channelId);
+
+ if (channels_[channelId] -> getFinish() == 0)
+ {
+ channels_[channelId] -> handleFinish();
+
+ //
+ // Force a failure in the case somebody
+ // would try to read from the channel.
+ //
+
+ shutdown(getFd(channelId), SHUT_RD);
+
+ //
+ // If the failure was not originated by
+ // the remote, send a channel shutdown
+ // message.
+ //
+
+ if (channels_[channelId] -> getClosing() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Finishing channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " because of failure.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleControl(code_finish_connection, channelId) < 0)
+ {
+ return -1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleFinishFromProxy(int channelId)
+{
+ //
+ // Check if this channel has pending
+ // data to send.
+ //
+
+ if (needFlush(channelId) == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: WARNING! Flushing data for the "
+ << "finishing channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Mark the channel. We will free its
+ // resources at the next loop and will
+ // send the drop message to the remote.
+ //
+
+ if (channels_[channelId] -> getClosing() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Marking channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " as closing.\n"
+ << logofs_flush;
+ #endif
+
+ channels_[channelId] -> handleClosing();
+ }
+
+ if (channels_[channelId] -> getFinish() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Finishing channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " because of proxy.\n"
+ << logofs_flush;
+ #endif
+
+ channels_[channelId] -> handleFinish();
+ }
+
+ if (handleFinish(channelId) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleDropFromProxy(int channelId)
+{
+ //
+ // Only mark the channel.
+ //
+
+ #ifdef TEST
+ *logofs << "Proxy: Marking channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " as being dropped.\n"
+ << logofs_flush;
+ #endif
+
+ if (channels_[channelId] -> getDrop() == 0)
+ {
+ channels_[channelId] -> handleDrop();
+ }
+
+ return 1;
+}
+
+//
+// Close the channel and deallocate all its
+// resources.
+//
+
+int Proxy::handleDrop(int channelId)
+{
+ //
+ // Check if this channel has pending
+ // data to send.
+ //
+
+ if (needFlush(channelId) == 1)
+ {
+ if (channels_[channelId] -> getFinish() == 1)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! The dropping channel ID#"
+ << channelId << " has data to flush.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: WARNING! Flushing data for the "
+ << "dropping channel ID#" << channelId
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Dropping channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ if (channels_[channelId] -> getFinish() == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! The channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " was not marked as "
+ << "finishing.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": The channel for FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << " was not marked as "
+ << "finishing.\n";
+
+ channels_[channelId] -> handleFinish();
+ }
+
+ //
+ // Send the channel shutdown message
+ // to the peer proxy.
+ //
+
+ if (channels_[channelId] -> getClosing() == 1)
+ {
+ if (handleControl(code_drop_connection, channelId) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Get rid of the channel.
+ //
+
+ if (channels_[channelId] -> getType() != channel_x11)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Closed connection to "
+ << getTypeName(channels_[channelId] -> getType())
+ << " server.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Closed connection to "
+ << getTypeName(channels_[channelId] -> getType())
+ << " server.\n";
+ }
+
+ delete channels_[channelId];
+ channels_[channelId] = NULL;
+
+ cleanupChannelMap(channelId);
+
+ //
+ // Get rid of the transport.
+ //
+
+ deallocateTransport(channelId);
+
+ congestions_[channelId] = 0;
+
+ decreaseChannels(channelId);
+
+ //
+ // Check if the channel was the
+ // one currently selected for
+ // output.
+ //
+
+ if (outputChannel_ == channelId)
+ {
+ outputChannel_ = -1;
+ }
+
+ return 1;
+}
+
+//
+// Send an empty message to the remote peer
+// to verify if the link is alive and let
+// the remote proxy detect a congestion.
+//
+
+int Proxy::handlePing()
+{
+ T_timestamp nowTs = getTimestamp();
+
+ #if defined(DEBUG) || defined(PING)
+
+ *logofs << "Proxy: Checking ping at "
+ << strMsTimestamp(nowTs) << logofs_flush;
+
+ *logofs << " with last loop at "
+ << strMsTimestamp(timeouts_.loopTs) << ".\n"
+ << logofs_flush;
+
+ *logofs << "Proxy: Last bytes in at "
+ << strMsTimestamp(timeouts_.readTs) << logofs_flush;
+
+ *logofs << " last bytes out at "
+ << strMsTimestamp(timeouts_.writeTs) << ".\n"
+ << logofs_flush;
+
+ *logofs << "Proxy: Last ping at "
+ << strMsTimestamp(timeouts_.pingTs) << ".\n"
+ << logofs_flush;
+
+ #endif
+
+ //
+ // Be sure we take into account any clock drift. This
+ // can be caused by the user changing the system timer
+ // or by small adjustments introduced by the operating
+ // system making the clock go backward.
+ //
+
+ if (checkDiffTimestamp(timeouts_.loopTs, nowTs) == 0)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Detected drift in system "
+ << "timer. Resetting to current time.\n"
+ << logofs_flush;
+ #endif
+
+ timeouts_.pingTs = nowTs;
+ timeouts_.readTs = nowTs;
+ timeouts_.writeTs = nowTs;
+ }
+
+ //
+ // Check timestamp of last read from remote proxy. It can
+ // happen that we stayed in the main loop long enough to
+ // have idle timeout expired, for example if the proxy was
+ // stopped and restarted or because of an extremely high
+ // load of the system. In this case we don't complain if
+ // there is something new to read from the remote.
+ //
+
+ int diffIn = diffTimestamp(timeouts_.readTs, nowTs);
+
+ if (diffIn >= (control -> PingTimeout * 2) -
+ control -> LatencyTimeout)
+ {
+ //
+ // Force a read to detect whether the remote proxy
+ // aborted the connection.
+ //
+
+ int result = handleRead();
+
+ if (result < 0)
+ {
+ #if defined(TEST) || defined(INFO) || defined(PING)
+ *logofs << "Proxy: WARNING! Detected shutdown waiting "
+ << "for the ping after " << diffIn / 1000
+ << " seconds.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ else if (result > 0)
+ {
+ diffIn = diffTimestamp(timeouts_.readTs, nowTs);
+
+ if (handleFlush() < 0)
+ {
+ return -1;
+ }
+ }
+ }
+
+ if (diffIn >= (control -> PingTimeout * 2) -
+ control -> LatencyTimeout)
+ {
+ #if defined(TEST) || defined(INFO) || defined(PING)
+ *logofs << "Proxy: Detected congestion at "
+ << strMsTimestamp() << " with " << diffIn / 1000
+ << " seconds since the last read.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // There are two types of proxy congestion. The first,
+ // affecting the ability of the proxy to write the
+ // encoded data to the network, is controlled by the
+ // congestion_ flag. The flag is raised when no data
+ // is received from the remote proxy within a timeout.
+ // On the X client side, the flag is also raised when
+ // the proxy runs out of tokens.
+ //
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ //
+ // At X server side we must return to read data
+ // from the channels after a while, because we
+ // need to give a chance to the channel to read
+ // the key sequence CTRL+ALT+SHIFT+ESC.
+ //
+
+ if (congestion_ == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Forcibly entering congestion due to "
+ << "timeout with " << tokens_[token_control].remaining
+ << " tokens.\n" << logofs_flush;
+ #endif
+
+ congestion_ = 1;
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Forcibly exiting congestion due to "
+ << "timeout with " << tokens_[token_control].remaining
+ << " tokens.\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ }
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+
+ if (congestion_ == 0)
+ {
+ *logofs << "Proxy: Entering congestion due to timeout "
+ << "with " << tokens_[token_control].remaining
+ << " tokens.\n" << logofs_flush;
+ }
+
+ #endif
+
+ congestion_ = 1;
+ }
+
+ if (control -> ProxyTimeout > 0 &&
+ diffIn >= (control -> ProxyTimeout -
+ control -> LatencyTimeout))
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No data received from "
+ << "remote proxy on FD#" << fd_ << " within "
+ << (diffIn + control -> LatencyTimeout) / 1000
+ << " seconds.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No data received from remote "
+ << "proxy within " << (diffIn + control ->
+ LatencyTimeout) / 1000 << " seconds.\n";
+
+ HandleAbort();
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: WARNING! No data received from "
+ << "remote proxy on FD#" << fd_ << " since "
+ << diffIn << " Ms.\n" << logofs_flush;
+ #endif
+
+ if (control -> ProxyTimeout > 0 &&
+ isTimestamp(timeouts_.alertTs) == 0 &&
+ diffIn >= (control -> ProxyTimeout -
+ control -> LatencyTimeout) / 4)
+ {
+ //
+ // If we are in the middle of a shutdown
+ // procedure but the remote is not resp-
+ // onding, force the closure of the link.
+ //
+
+ if (finish_ != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No response received from "
+ << "the remote proxy on FD#" << fd_ << " while "
+ << "waiting for the shutdown.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No response received from remote "
+ << "proxy while waiting for the shutdown.\n";
+
+ HandleAbort();
+ }
+ else
+ {
+ cerr << "Warning" << ": No data received from remote "
+ << "proxy within " << (diffIn + control ->
+ LatencyTimeout) / 1000 << " seconds.\n";
+
+ if (alert_ == 0)
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ alert_ = CLOSE_DEAD_PROXY_CONNECTION_CLIENT_ALERT;
+ }
+ else
+ {
+ alert_ = CLOSE_DEAD_PROXY_CONNECTION_SERVER_ALERT;
+ }
+
+ HandleAlert(alert_, 1);
+ }
+
+ timeouts_.alertTs = nowTs;
+ }
+ }
+ }
+ }
+
+ //
+ // Check if we need to update the congestion
+ // counter.
+ //
+
+ int diffOut = diffTimestamp(timeouts_.writeTs, nowTs);
+
+ if (agent_ != nothing && congestions_[agent_] == 0 &&
+ statistics -> getCongestionInFrame() >= 1 &&
+ diffOut >= (control -> IdleTimeout -
+ control -> LatencyTimeout * 5))
+ {
+ #if defined(TEST) || defined(INFO) || defined(PING)
+ *logofs << "Proxy: Forcing an update of the "
+ << "congestion counter after timeout.\n"
+ << logofs_flush;
+ #endif
+
+ statistics -> updateCongestion(tokens_[token_control].remaining,
+ tokens_[token_control].limit);
+ }
+
+ //
+ // Send a new token if we didn't send any data to
+ // the remote for longer than the ping timeout.
+ // The client side sends a token, the server side
+ // responds with a token reply.
+ //
+ // VMWare virtual machines can have the system
+ // timer deadly broken. Try to send a ping regard-
+ // less we are the client or the server proxy to
+ // force a write by the remote.
+ //
+
+ if (control -> ProxyMode == proxy_client ||
+ diffIn >= (control -> PingTimeout * 4) -
+ control -> LatencyTimeout)
+ {
+ //
+ // We need to send a new ping even if we didn't
+ // receive anything from the remote within the
+ // ping timeout. The server side will respond
+ // to our ping, so we use the ping to force the
+ // remote end to send some data.
+ //
+
+ if (diffIn >= (control -> PingTimeout -
+ control -> LatencyTimeout * 5) ||
+ diffOut >= (control -> PingTimeout -
+ control -> LatencyTimeout * 5))
+ {
+ int diffPing = diffTimestamp(timeouts_.pingTs, nowTs);
+
+ if (diffPing < 0 || diffPing >= (control -> PingTimeout -
+ control -> LatencyTimeout * 5))
+ {
+ #if defined(TEST) || defined(INFO) || defined(PING)
+ *logofs << "Proxy: Sending a new ping at " << strMsTimestamp()
+ << " with " << tokens_[token_control].remaining
+ << " tokens and elapsed in " << diffIn << " out "
+ << diffOut << " ping " << diffPing
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_ping) < 0)
+ {
+ return -1;
+ }
+
+ timeouts_.pingTs = nowTs;
+ }
+ #if defined(TEST) || defined(INFO) || defined(PING)
+ else
+ {
+ *logofs << "Proxy: Not sending a new ping with "
+ << "elapsed in " << diffIn << " out "
+ << diffOut << " ping " << diffPing
+ << ".\n" << logofs_flush;
+ }
+ #endif
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleSyncFromProxy(int channelId)
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: WARNING! Received a synchronization "
+ << "request from the remote proxy.\n"
+ << logofs_flush;
+ #endif
+
+ if (handleControl(code_sync_reply, channelId) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleResetStores()
+{
+ //
+ // Recreate the message stores.
+ //
+
+ delete clientStore_;
+ delete serverStore_;
+
+ clientStore_ = new ClientStore(compressor_);
+ serverStore_ = new ServerStore(compressor_);
+
+ timeouts_.loadTs = nullTimestamp();
+
+ //
+ // Replace message stores in channels.
+ //
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL)
+ {
+ if (channels_[channelId] -> setStores(clientStore_, serverStore_) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Failed to replace message stores in "
+ << "channel for FD#" << getFd(channelId) << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to replace message stores in "
+ << "channel for FD#" << getFd(channelId) << ".\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Proxy: Replaced message stores in channel "
+ << "for FD#" << getFd(channelId) << ".\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleResetPersistentCache()
+{
+ char *fullName = new char[strlen(control -> PersistentCachePath) +
+ strlen(control -> PersistentCacheName) + 2];
+
+ strcpy(fullName, control -> PersistentCachePath);
+ strcat(fullName, "/");
+ strcat(fullName, control -> PersistentCacheName);
+
+ #ifdef TEST
+ *logofs << "Proxy: Going to remove persistent cache file '"
+ << fullName << "'\n" << logofs_flush;
+ #endif
+
+ unlink(fullName);
+
+ delete [] fullName;
+
+ delete [] control -> PersistentCacheName;
+
+ control -> PersistentCacheName = NULL;
+
+ return 1;
+}
+
+void Proxy::handleResetFlush()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Going to reset flush counters "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Reset the proxy priority flag.
+ //
+
+ priority_ = 0;
+
+ //
+ // Restore buffers to their initial
+ // size.
+ //
+
+ transport_ -> partialReset();
+
+ //
+ // Update the timestamp of the last
+ // write operation performed on the
+ // socket.
+ //
+
+ timeouts_.writeTs = getTimestamp();
+}
+
+int Proxy::handleFinish()
+{
+ //
+ // Reset the timestamps to give the proxy
+ // another chance to show the 'no response'
+ // dialog if the shutdown message doesn't
+ // come in time.
+ //
+
+ timeouts_.readTs = getTimestamp();
+
+ timeouts_.alertTs = nullTimestamp();
+
+ finish_ = 1;
+
+ return 1;
+}
+
+int Proxy::handleShutdown()
+{
+ //
+ // Send shutdown message to remote proxy.
+ //
+
+ shutdown_ = 1;
+
+ handleControl(code_shutdown_request);
+
+ #ifdef TEST
+ *logofs << "Proxy: Starting shutdown procedure "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Ensure that all the data accumulated
+ // in the transport buffer is flushed
+ // to the network layer.
+ //
+
+ for (int i = 0; i < 100; i++)
+ {
+ if (canFlush() == 1)
+ {
+ handleFlush();
+ }
+ else
+ {
+ break;
+ }
+
+ usleep(100000);
+ }
+
+ //
+ // Now wait for the network layers to
+ // consume all the data.
+ //
+
+ for (int i = 0; i < 100; i++)
+ {
+ if (transport_ -> queued() <= 0)
+ {
+ break;
+ }
+
+ usleep(100000);
+ }
+
+ //
+ // Give time to the remote end to read
+ // the shutdown message and close the
+ // connection.
+ //
+
+ transport_ -> wait(10000);
+
+ #ifdef TEST
+ *logofs << "Proxy: Ending shutdown procedure "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int Proxy::handleChannelConfiguration()
+{
+ if (activeChannels_.getSize() == 0)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Going to initialize the static "
+ << "members in channels for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ Channel::setReferences();
+
+ ClientChannel::setReferences();
+ ServerChannel::setReferences();
+
+ GenericChannel::setReferences();
+ }
+
+ return 1;
+}
+
+int Proxy::handleSocketConfiguration()
+{
+ //
+ // Set linger mode on proxy to correctly
+ // get shutdown notification.
+ //
+
+ SetLingerTimeout(fd_, 30);
+
+ //
+ // Set keep-alive on socket so that if remote link
+ // terminates abnormally (as killed hard or because
+ // of a power-off) process will get a SIGPIPE. In
+ // practice this is useless as proxies already ping
+ // each other every few seconds.
+ //
+
+ if (control -> OptionProxyKeepAlive == 1)
+ {
+ SetKeepAlive(fd_);
+ }
+
+ //
+ // Set 'priority' flag at TCP layer for path
+ // proxy-to-proxy. Look at IPTOS_LOWDELAY in
+ // man 7 ip.
+ //
+
+ if (control -> OptionProxyLowDelay == 1)
+ {
+ SetLowDelay(fd_);
+ }
+
+ //
+ // Update size of TCP send and receive buffers.
+ //
+
+ if (control -> OptionProxySendBuffer != -1)
+ {
+ SetSendBuffer(fd_, control -> OptionProxySendBuffer);
+ }
+
+ if (control -> OptionProxyReceiveBuffer != -1)
+ {
+ SetReceiveBuffer(fd_, control -> OptionProxyReceiveBuffer);
+ }
+
+ //
+ // Update TCP_NODELAY settings. Note that on old Linux
+ // kernels turning off the Nagle algorithm didn't work
+ // when proxy was run through a PPP link. Trying to do
+ // so caused the kernel to stop delivering data to us
+ // if a serious network congestion was encountered.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ if (control -> OptionProxyClientNoDelay != -1)
+ {
+ SetNoDelay(fd_, control -> OptionProxyClientNoDelay);
+ }
+ }
+ else
+ {
+ if (control -> OptionProxyServerNoDelay != -1)
+ {
+ SetNoDelay(fd_, control -> OptionProxyServerNoDelay);
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleLinkConfiguration()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Propagating parameters to "
+ << "channels' read buffers.\n"
+ << logofs_flush;
+ #endif
+
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL)
+ {
+ channels_[channelId] -> handleConfiguration();
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Propagating parameters to "
+ << "proxy buffers.\n"
+ << logofs_flush;
+ #endif
+
+ readBuffer_.setSize(control -> ProxyInitialReadSize,
+ control -> ProxyMaximumBufferSize);
+
+ encodeBuffer_.setSize(control -> TransportProxyBufferSize,
+ control -> TransportProxyBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ transport_ -> setSize(control -> TransportProxyBufferSize,
+ control -> TransportProxyBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ #ifdef TEST
+ *logofs << "Proxy: Configuring the proxy timeouts.\n"
+ << logofs_flush;
+ #endif
+
+ timeouts_.split = control -> SplitTimeout;
+ timeouts_.motion = control -> MotionTimeout;
+
+ #ifdef TEST
+ *logofs << "Proxy: Configuring the proxy tokens.\n"
+ << logofs_flush;
+ #endif
+
+ tokens_[token_control].size = control -> TokenSize;
+ tokens_[token_control].limit = control -> TokenLimit;
+
+ if (tokens_[token_control].limit < 1)
+ {
+ tokens_[token_control].limit = 1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(LIMIT)
+ *logofs << "Proxy: TOKEN! LIMIT! Setting token ["
+ << DumpToken(token_control) << "] size to "
+ << tokens_[token_control].size << " and limit to "
+ << tokens_[token_control].limit << ".\n"
+ << logofs_flush;
+ #endif
+
+ tokens_[token_split].size = control -> TokenSize;
+ tokens_[token_split].limit = control -> TokenLimit / 2;
+
+ if (tokens_[token_split].limit < 1)
+ {
+ tokens_[token_split].limit = 1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(LIMIT)
+ *logofs << "Proxy: TOKEN! LIMIT! Setting token ["
+ << DumpToken(token_split) << "] size to "
+ << tokens_[token_split].size << " and limit to "
+ << tokens_[token_split].limit << ".\n"
+ << logofs_flush;
+ #endif
+
+ tokens_[token_data].size = control -> TokenSize;
+ tokens_[token_data].limit = control -> TokenLimit / 4;
+
+ if (tokens_[token_data].limit < 1)
+ {
+ tokens_[token_data].limit = 1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(LIMIT)
+ *logofs << "Proxy: TOKEN! LIMIT! Setting token ["
+ << DumpToken(token_data) << "] size to "
+ << tokens_[token_data].size << " and limit to "
+ << tokens_[token_data].limit << ".\n"
+ << logofs_flush;
+ #endif
+
+ for (int i = token_control; i <= token_data; i++)
+ {
+ tokens_[i].remaining = tokens_[i].limit;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(LIMIT)
+ *logofs << "Proxy: LIMIT! Using client bitrate "
+ << "limit " << control -> ClientBitrateLimit
+ << " server bitrate limit " << control ->
+ ServerBitrateLimit << " with local limit "
+ << control -> LocalBitrateLimit << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Set the other parameters based on
+ // the token size.
+ //
+
+ int base = control -> TokenSize;
+
+ control -> SplitDataThreshold = base * 4;
+ control -> SplitDataPacketLimit = base / 2;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: LIMIT! Setting split data threshold "
+ << "to " << control -> SplitDataThreshold
+ << " split packet limit to " << control ->
+ SplitDataPacketLimit << " with base "
+ << base << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Set the number of bytes read from the
+ // data channels at each loop. This will
+ // basically determine the maximum band-
+ // width available for the generic chan-
+ // nels.
+ //
+
+ control -> GenericInitialReadSize = base / 2;
+ control -> GenericMaximumBufferSize = base / 2;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: LIMIT! Setting generic channel "
+ << "initial read size to " << control ->
+ GenericInitialReadSize << " maximum read "
+ << "size to " << control -> GenericMaximumBufferSize
+ << " with base " << base << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int Proxy::handleCacheConfiguration()
+{
+ #ifdef TEST
+ *logofs << "Proxy: Configuring cache according to pack parameters.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Further adjust the cache parameters. If
+ // packing of the images is enabled, reduce
+ // the size available for plain images.
+ //
+
+ if (control -> SessionMode == session_agent)
+ {
+ if (control -> PackMethod != NO_PACK)
+ {
+ clientStore_ -> getRequestStore(X_PutImage) ->
+ cacheThreshold = PUTIMAGE_CACHE_THRESHOLD_IF_PACKED;
+
+ clientStore_ -> getRequestStore(X_PutImage) ->
+ cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED;
+ }
+ }
+
+ //
+ // If this is a shadow session increase the
+ // size of the image cache.
+ //
+
+ if (control -> SessionMode == session_shadow)
+ {
+ if (control -> PackMethod != NO_PACK)
+ {
+ clientStore_ -> getRequestStore(X_NXPutPackedImage) ->
+ cacheThreshold = PUTPACKEDIMAGE_CACHE_THRESHOLD_IF_PACKED_SHADOW;
+
+ clientStore_ -> getRequestStore(X_NXPutPackedImage) ->
+ cacheLowerThreshold = PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED_SHADOW;
+ }
+ else
+ {
+ clientStore_ -> getRequestStore(X_PutImage) ->
+ cacheThreshold = PUTIMAGE_CACHE_THRESHOLD_IF_SHADOW;
+
+ clientStore_ -> getRequestStore(X_PutImage) ->
+ cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_SHADOW;
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleSaveStores()
+{
+ //
+ // Save content of stores on disk.
+ //
+
+ char *cacheToAdopt = NULL;
+
+ //
+ // Set to false the indicator for cumulative store
+ // size too small
+ //
+ bool isTooSmall = false;
+
+ if (control -> PersistentCacheEnableSave)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Going to save content of client store.\n"
+ << logofs_flush;
+ #endif
+
+ cacheToAdopt = handleSaveAllStores(control -> PersistentCachePath, isTooSmall);
+ }
+ #ifdef TEST
+ else
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ *logofs << "Proxy: Saving persistent cache to disk disabled.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Proxy: PANIC! Protocol violation in command save.\n"
+ << logofs_flush;
+
+ cerr << "Error" << ": Protocol violation in command save.\n";
+
+ HandleCleanup();
+ }
+ }
+ #endif
+
+ if (cacheToAdopt != NULL)
+ {
+ //
+ // Do we have a cache already?
+ //
+
+ if (control -> PersistentCacheName != NULL)
+ {
+ //
+ // Check if old and new cache are the same.
+ // In this case don't remove the old cache.
+ //
+
+ if (strcasecmp(control -> PersistentCacheName, cacheToAdopt) != 0)
+ {
+ handleResetPersistentCache();
+ }
+
+ delete [] control -> PersistentCacheName;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Setting current persistent cache file to '"
+ << cacheToAdopt << "'\n" << logofs_flush;
+ #endif
+
+ control -> PersistentCacheName = cacheToAdopt;
+
+ return 1;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Proxy: No cache file produced from message stores.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // It can be that we didn't generate a new cache
+ // because store was too small or persistent cache
+ // was disabled. This is not an error.
+ //
+
+ if (control -> PersistentCacheEnableSave && !isTooSmall)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+}
+
+int Proxy::handleLoadStores()
+{
+ //
+ // Restore the content of the client store
+ // from disk if a valid cache was negotiated
+ // at session startup.
+ //
+
+ if (control -> PersistentCacheEnableLoad == 1 &&
+ control -> PersistentCachePath != NULL &&
+ control -> PersistentCacheName != NULL)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Going to load content of client store.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Returns the same string passed as name of
+ // the cache, or NULL if it was not possible
+ // to load the cache from disk.
+ //
+
+ if (handleLoadAllStores(control -> PersistentCachePath,
+ control -> PersistentCacheName) == NULL)
+ {
+ //
+ // The corrupted cache should have been
+ // removed from disk. Get rid of the
+ // reference so we don't try to delete
+ // it once again.
+ //
+
+ if (control -> PersistentCacheName != NULL)
+ {
+ delete [] control -> PersistentCacheName;
+ }
+
+ control -> PersistentCacheName = NULL;
+
+ return -1;
+ }
+
+ //
+ // Set timestamp of last time cache
+ // was loaded from data on disk.
+ //
+
+ timeouts_.loadTs = getTimestamp();
+
+ return 1;
+ }
+ #ifdef TEST
+ else
+ {
+ if (control -> ProxyMode == proxy_client)
+ {
+ *logofs << "Proxy: Loading of cache disabled or no cache file selected.\n"
+ << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Proxy: PANIC! Protocol violation in command load.\n"
+ << logofs_flush;
+
+ cerr << "Error" << ": Protocol violation in command load.\n";
+
+ HandleCleanup();
+ }
+ }
+ #endif
+
+ return 0;
+}
+
+int Proxy::handleControl(T_proxy_code code, int data)
+{
+ //
+ // Send the given control messages
+ // to the remote proxy.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ if (data != -1)
+ {
+ if (code == code_control_token_reply ||
+ code == code_split_token_reply ||
+ code == code_data_token_reply)
+ {
+ *logofs << "Proxy: TOKEN! Sending message '" << DumpControl(code)
+ << "' at " << strMsTimestamp() << " with count "
+ << data << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Proxy: Sending message '" << DumpControl(code)
+ << "' at " << strMsTimestamp() << " with data ID#"
+ << data << ".\n" << logofs_flush;
+ }
+ }
+ else
+ {
+ *logofs << "Proxy: Sending message '" << DumpControl(code)
+ << "' at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // Add the control message and see if the
+ // data has to be flushed immediately.
+ //
+
+ if (addControlCodes(code, data) < 0)
+ {
+ return -1;
+ }
+
+ switch (code)
+ {
+ //
+ // Append the first data read from the opened
+ // channel to the control code.
+ //
+
+ case code_new_x_connection:
+ case code_new_cups_connection:
+ case code_new_aux_connection:
+ case code_new_smb_connection:
+ case code_new_media_connection:
+ case code_new_http_connection:
+ case code_new_font_connection:
+ case code_new_slave_connection:
+
+ //
+ // Do we send the token reply immediately?
+ // The control messages are put at the begin-
+ // ning of the of the encode buffer, so we may
+ // reply to multiple tokens before having the
+ // chance of handling the actual frame data.
+ // On the other hand, the sooner we reply, the
+ // sooner the remote proxy is restarted.
+ //
+
+ case code_control_token_reply:
+ case code_split_token_reply:
+ case code_data_token_reply:
+ {
+ break;
+ }
+
+ //
+ // Also send the congestion control codes
+ // immediately.
+ //
+ // case code_begin_congestion:
+ // case code_end_congestion:
+ //
+
+ default:
+ {
+ priority_ = 1;
+
+ break;
+ }
+ }
+
+ if (priority_ == 1)
+ {
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int Proxy::handleSwitch(int channelId)
+{
+ //
+ // If data is for a different channel than last
+ // selected for output, prepend to the data the
+ // new channel id.
+ //
+
+ #ifdef DEBUG
+ *logofs << "Proxy: Requested a switch with "
+ << "current channel ID#" << outputChannel_
+ << " new channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (channelId != outputChannel_)
+ {
+ if (needFlush() == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: WARNING! Flushing data for the "
+ << "previous channel ID#" << outputChannel_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (handleFrame(frame_data) < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Sending message '"
+ << DumpControl(code_switch_connection) << "' at "
+ << strMsTimestamp() << " with FD#" << getFd(channelId)
+ << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (addControlCodes(code_switch_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ outputChannel_ = channelId;
+ }
+
+ return 1;
+}
+
+int Proxy::addTokenCodes(T_proxy_token &token)
+{
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: TOKEN! Sending token ["
+ << DumpToken(token.type) << "] with "
+ << token.bytes << " bytes accumulated size "
+ << token.size << " and " << token.remaining
+ << " available.\n" << logofs_flush;
+ #endif
+
+ //
+ // Give a 'weight' to the token. The tokens
+ // remaining can become negative if we sent
+ // a packet that was exceptionally big.
+ //
+
+ int count = 0;
+
+ // Since ProtoStep7 (#issue 108)
+ count = token.bytes / token.size;
+
+ //
+ // Force a count of 1, for example
+ // if this is a ping.
+ //
+
+ if (count < 1)
+ {
+ count = 1;
+
+ token.bytes = 0;
+ }
+ else
+ {
+ // Since ProtoStep7 (#issue 108)
+ if (count > 255)
+ {
+ count = 255;
+ }
+
+ //
+ // Let the next token account for the
+ // remaining bytes.
+ //
+
+ token.bytes %= token.size;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: Sending message '"
+ << DumpControl(token.request) << "' at "
+ << strMsTimestamp() << " with count " << count
+ << ".\n" << logofs_flush;
+ #endif
+
+ controlCodes_[controlLength_++] = 0;
+ controlCodes_[controlLength_++] = (unsigned char) token.request;
+ controlCodes_[controlLength_++] = (unsigned char) count;
+
+ statistics -> addFrameOut();
+
+ token.remaining -= count;
+
+ return 1;
+}
+
+int Proxy::handleToken(T_frame_type type)
+{
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: TOKEN! Checking tokens with "
+ << "frame type [";
+
+ *logofs << (type == frame_ping ? "frame_ping" : "frame_data");
+
+ *logofs << "] with stream ratio " << statistics ->
+ getStreamRatio() << ".\n" << logofs_flush;
+ #endif
+
+ if (type == frame_data)
+ {
+ //
+ // Since ProtoStep7 (#issue 108)
+ //
+
+ // Send a distinct token for each data type.
+ // We don't want to slow down the sending of
+ // the X events, X replies and split confir-
+ // mation events on the X server side, so
+ // take care only of the generic data token.
+ //
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ statistics -> updateControlToken(tokens_[token_control].bytes);
+
+ if (tokens_[token_control].bytes > tokens_[token_control].size)
+ {
+ if (addTokenCodes(tokens_[token_control]) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+
+ T_proxy_token &token = tokens_[token_control];
+
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now "
+ << token.bytes << " bytes accumulated and "
+ << token.remaining << " tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ statistics -> updateSplitToken(tokens_[token_split].bytes);
+
+ if (tokens_[token_split].bytes > tokens_[token_split].size)
+ {
+ if (addTokenCodes(tokens_[token_split]) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+
+ T_proxy_token &token = tokens_[token_split];
+
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now "
+ << token.bytes << " bytes accumulated and "
+ << token.remaining << " tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+ statistics -> updateDataToken(tokens_[token_data].bytes);
+
+ if (tokens_[token_data].bytes > tokens_[token_data].size)
+ {
+ if (addTokenCodes(tokens_[token_data]) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+
+ T_proxy_token &token = tokens_[token_data];
+
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now "
+ << token.bytes << " bytes accumulated and "
+ << token.remaining << " tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+ else
+ {
+ if (addTokenCodes(tokens_[token_control]) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset all counters on a ping.
+ //
+
+ tokens_[token_control].bytes = 0;
+ tokens_[token_split].bytes = 0;
+ tokens_[token_data].bytes = 0;
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+
+ T_proxy_token &token = tokens_[token_control];
+
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now "
+ << token.bytes << " bytes accumulated and "
+ << token.remaining << " tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // Check if we have entered in
+ // congestion state.
+ //
+
+ if (congestion_ == 0 &&
+ tokens_[token_control].remaining <= 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Entering congestion with "
+ << tokens_[token_control].remaining
+ << " tokens remaining.\n" << logofs_flush;
+ #endif
+
+ congestion_ = 1;
+ }
+
+ statistics -> updateCongestion(tokens_[token_control].remaining,
+ tokens_[token_control].limit);
+
+ return 1;
+}
+
+int Proxy::handleTokenFromProxy(T_proxy_token &token, int count)
+{
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: TOKEN! Received token ["
+ << DumpToken(token.type) << "] request at "
+ << strMsTimestamp() << " with count "
+ << count << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Since ProtoStep7 (#issue 108) with no limitations
+ // concerning invalid token requests at this point
+ //
+
+ //
+ // Add our token reply.
+ //
+
+ if (handleControl(token.reply, count) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::handleTokenReplyFromProxy(T_proxy_token &token, int count)
+{
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: TOKEN! Received token ["
+ << DumpToken(token.type) << "] reply at "
+ << strMsTimestamp() << " with count " << count
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Since ProtoStep7 (#issue 108) with no limitations
+ // concerning invalid token requests at this point
+ //
+
+ //
+ // Increment the available tokens.
+ //
+
+ token.remaining += count;
+
+ if (token.remaining > token.limit)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Token overflow handling messages.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Token overflow handling messages.\n";
+
+ HandleCleanup();
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(TOKEN)
+ *logofs << "Proxy: TOKEN! Token class ["
+ << DumpToken(token.type) << "] has now " << token.bytes
+ << " bytes accumulated and " << token.remaining
+ << " tokens remaining.\n" << logofs_flush;
+ #endif
+
+ //
+ // Check if we can jump out of the
+ // congestion state.
+ //
+
+ if (congestion_ == 1 &&
+ tokens_[token_control].remaining > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Exiting congestion with "
+ << tokens_[token_control].remaining
+ << " tokens remaining.\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ }
+
+ statistics -> updateCongestion(tokens_[token_control].remaining,
+ tokens_[token_control].limit);
+
+ return 1;
+}
+
+void Proxy::handleFailOnSave(const char *fullName, const char *failContext) const
+{
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Error saving stores to cache file "
+ << "in context [" << failContext << "].\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Error saving stores to cache file "
+ << "in context [" << failContext << "].\n";
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Removing invalid cache '"
+ << fullName << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Removing invalid cache '"
+ << fullName << "'.\n";
+
+ unlink(fullName);
+}
+
+void Proxy::handleFailOnLoad(const char *fullName, const char *failContext) const
+{
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Error loading stores from cache file "
+ << "in context [" << failContext << "].\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Error loading stores from cache file "
+ << "in context [" << failContext << "].\n";
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Removing invalid cache '"
+ << fullName << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Removing invalid cache '"
+ << fullName << "'.\n";
+
+ unlink(fullName);
+}
+
+int Proxy::handleSaveVersion(unsigned char *buffer, int &major,
+ int &minor, int &patch) const
+{
+ // Since ProtoStep8 (#issue 108)
+ major = 3;
+ minor = 0;
+ patch = 0;
+
+ *(buffer + 0) = major;
+ *(buffer + 1) = minor;
+
+ PutUINT(patch, buffer + 2, storeBigEndian());
+
+ return 1;
+}
+
+int Proxy::handleLoadVersion(const unsigned char *buffer, int &major,
+ int &minor, int &patch) const
+{
+ major = *(buffer + 0);
+ minor = *(buffer + 1);
+
+ patch = GetUINT(buffer + 2, storeBigEndian());
+
+ //
+ // Force the proxy to discard the
+ // incompatible caches.
+ //
+
+ // Since ProtoStep8 (#issue 108)
+ if (major < 3)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+char *Proxy::handleSaveAllStores(const char *savePath, bool & isTooSmall) const
+{
+ isTooSmall = false;
+
+ int cumulativeSize = MessageStore::getCumulativeTotalStorageSize();
+
+ if (cumulativeSize < control -> PersistentCacheThreshold)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: Cache not saved as size is "
+ << cumulativeSize << " with threshold set to "
+ << control -> PersistentCacheThreshold
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Cumulative store size is smaller than threshold
+ // so the indicator is set to true
+ //
+
+ isTooSmall = true;
+
+ return NULL;
+ }
+ else if (savePath == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No name provided for save path.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No name provided for save path.\n";
+
+ return NULL;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Going to save content of message stores.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Our parent process is likely going to terminate.
+ // Until we finish saving cache we must ignore its
+ // SIGIPE.
+ //
+
+ DisableSignals();
+
+ ofstream *cachefs = NULL;
+
+ md5_state_t *md5StateStream = NULL;
+ md5_byte_t *md5DigestStream = NULL;
+
+ md5_state_t *md5StateClient = NULL;
+ md5_byte_t *md5DigestClient = NULL;
+
+ char md5String[MD5_LENGTH * 2 + 2];
+
+ char fullName[strlen(savePath) + MD5_LENGTH * 2 + 4];
+
+ //
+ // Prepare the template for the temporary file
+ //
+
+ const char* const uniqueTemplate = "XXXXXX";
+ char tempName[strlen(savePath) + strlen("/") + 4 + strlen(uniqueTemplate) + 1];
+
+ snprintf(tempName, sizeof tempName, "%s/%s%s",
+ savePath,
+ control -> ProxyMode == proxy_client ?
+ "Z-C-" :
+ "Z-S-",
+ uniqueTemplate);
+
+ #ifdef TEST
+ *logofs << "Proxy: Generating temporary file with template '"
+ << tempName << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Change the mask to make the file only
+ // readable by the user, then restore the
+ // old mask.
+ //
+
+ mode_t fileMode = umask(0077);
+
+ //
+ // Generate a unique temporary filename from tempName
+ // and then create and open the file
+ //
+
+ int fdTemp = mkstemp(tempName);
+ if (fdTemp == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't create temporary file in '"
+ << savePath << "'. Cause = " << strerror(errno) << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create temporary file in '"
+ << savePath << "'. Cause = " << strerror(errno) << ".\n";
+
+ umask(fileMode);
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Saving cache to file '"
+ << tempName << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Create and open the output stream for the new temporary
+ // file
+ //
+
+ cachefs = new (std::nothrow) ofstream(tempName, ios::out | ios::binary);
+ if ((cachefs == NULL) || cachefs->fail())
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't create stream for temporary file '"
+ << tempName << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create stream for temporary file '"
+ << tempName << "'.\n";
+
+ close(fdTemp);
+ unlink(tempName);
+
+ umask(fileMode);
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ //
+ // Close the file descriptor returned by mkstemp
+ // and restore the old mask
+ //
+
+ close(fdTemp);
+ umask(fileMode);
+
+ md5StateStream = new md5_state_t();
+ md5DigestStream = new md5_byte_t[MD5_LENGTH];
+
+ md5_init(md5StateStream);
+
+ //
+ // First write the proxy version.
+ //
+
+ unsigned char version[4];
+
+ int major;
+ int minor;
+ int patch;
+
+ handleSaveVersion(version, major, minor, patch);
+
+ #ifdef TEST
+ *logofs << "Proxy: Saving cache using version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (PutData(cachefs, version, 4) < 0)
+ {
+ handleFailOnSave(tempName, "A");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ //
+ // Make space for the calculated MD5 so we
+ // can later rewind the file and write it
+ // at this position.
+ //
+
+ if (PutData(cachefs, md5DigestStream, MD5_LENGTH) < 0)
+ {
+ handleFailOnSave(tempName, "B");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ md5StateClient = new md5_state_t();
+ md5DigestClient = new md5_byte_t[MD5_LENGTH];
+
+ md5_init(md5StateClient);
+
+ #ifdef DUMP
+
+ ofstream *cacheDump = NULL;
+
+ ofstream *tempfs = (ofstream*) logofs;
+
+ char cacheDumpName[DEFAULT_STRING_LENGTH];
+
+ if (control -> ProxyMode == proxy_client)
+ {
+ snprintf(cacheDumpName, DEFAULT_STRING_LENGTH - 1,
+ "%s/client-cache-dump", control -> TempPath);
+ }
+ else
+ {
+ snprintf(cacheDumpName, DEFAULT_STRING_LENGTH - 1,
+ "%s/server-cache-dump", control -> TempPath);
+ }
+
+ *(cacheDumpName + DEFAULT_STRING_LENGTH - 1) = '\0';
+
+ fileMode = umask(0077);
+
+ cacheDump = new ofstream(cacheDumpName, ios::out);
+
+ umask(fileMode);
+
+ logofs = cacheDump;
+
+ #endif
+
+ //
+ // Use the virtual method of the concrete proxy class.
+ //
+
+ int allSaved = handleSaveAllStores(cachefs, md5StateStream, md5StateClient);
+
+ #ifdef DUMP
+
+ logofs = tempfs;
+
+ delete cacheDump;
+
+ #endif
+
+ if (allSaved == -1)
+ {
+ handleFailOnSave(tempName, "C");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete md5StateClient;
+ delete [] md5DigestClient;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ md5_finish(md5StateClient, md5DigestClient);
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ sprintf(md5String + (i * 2), "%02X", md5DigestClient[i]);
+ }
+
+ strcpy(fullName, (control -> ProxyMode == proxy_client) ? "C-" : "S-");
+
+ strcat(fullName, md5String);
+
+ md5_append(md5StateStream, (const md5_byte_t *) fullName, strlen(fullName));
+ md5_finish(md5StateStream, md5DigestStream);
+
+ //
+ // Go to the beginning of file plus
+ // the integer where we wrote our
+ // proxy version.
+ //
+
+ cachefs -> seekp(4);
+
+ if (PutData(cachefs, md5DigestStream, MD5_LENGTH) < 0)
+ {
+ handleFailOnSave(tempName, "D");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete md5StateClient;
+ delete [] md5DigestClient;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ delete cachefs;
+
+ //
+ // Copy the resulting cache name without
+ // the path so that we can return it to
+ // the caller.
+ //
+
+ char *cacheName = new char[MD5_LENGTH * 2 + 4];
+
+ strcpy(cacheName, fullName);
+
+ //
+ // Add the path to the full name and move
+ // the cache into the path.
+ //
+
+ strcpy(fullName, savePath);
+ strcat(fullName, (control -> ProxyMode == proxy_client) ? "/C-" : "/S-");
+ strcat(fullName, md5String);
+
+ #ifdef TEST
+ *logofs << "Proxy: Renaming cache file from '"
+ << tempName << "' to '" << fullName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ rename(tempName, fullName);
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete md5StateClient;
+ delete [] md5DigestClient;
+
+ //
+ // Restore the original handlers.
+ //
+
+ EnableSignals();
+
+ #ifdef TEST
+ *logofs << "Proxy: Successfully saved cache file '"
+ << cacheName << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // This must be enabled only for test
+ // because it requires that client and
+ // server reside on the same machine.
+ //
+
+ if (control -> PersistentCacheCheckOnShutdown == 1 &&
+ control -> ProxyMode == proxy_server)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: MATCH! Checking if the file '"
+ << fullName << "' matches a client cache.\n"
+ << logofs_flush;
+ #endif
+
+ strcpy(fullName, savePath);
+ strcat(fullName, "/C-");
+ strcat(fullName, md5String);
+
+ struct stat fileStat;
+
+ if (stat(fullName, &fileStat) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't find a client cache "
+ << "with name '" << fullName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't find a client cache "
+ << "with name '" << fullName << "'.\n";
+
+ HandleShutdown();
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: MATCH! Client cache '" << fullName
+ << "' matches the local cache.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ return cacheName;
+}
+
+const char *Proxy::handleLoadAllStores(const char *loadPath, const char *loadName) const
+{
+ #ifdef TEST
+ *logofs << "Proxy: Going to load content of message stores.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Until we finish loading cache we
+ // must at least ignore any SIGIPE.
+ //
+
+ DisableSignals();
+
+ if (loadPath == NULL || loadName == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No path or no file name provided for cache to restore.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No path or no file name provided for cache to restore.\n";
+
+ EnableSignals();
+
+ return NULL;
+ }
+ else if (strlen(loadName) != MD5_LENGTH * 2 + 2)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Bad file name provided for cache to restore.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Bad file name provided for cache to restore.\n";
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ istream *cachefs = NULL;
+ char md5String[(MD5_LENGTH * 2) + 2];
+ md5_byte_t md5FromFile[MD5_LENGTH];
+
+ char *cacheName = new char[strlen(loadPath) + strlen(loadName) + 3];
+
+ strcpy(cacheName, loadPath);
+ strcat(cacheName, "/");
+ strcat(cacheName, loadName);
+
+ #ifdef TEST
+ *logofs << "Proxy: Name of cache file is '"
+ << cacheName << "'.\n" << logofs_flush;
+ #endif
+
+ cachefs = new ifstream(cacheName, ios::in | ios::binary);
+
+ unsigned char version[4];
+
+ if (cachefs == NULL || GetData(cachefs, version, 4) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't read cache file '"
+ << cacheName << "'.\n" << logofs_flush;;
+ #endif
+
+ handleFailOnLoad(cacheName, "A");
+
+ delete cachefs;
+
+ delete [] cacheName;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ int major;
+ int minor;
+ int patch;
+
+ if (handleLoadVersion(version, major, minor, patch) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: WARNING! Incompatible version '"
+ << major << "." << minor << "." << patch
+ << "' in cache file '" << cacheName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Incompatible version '"
+ << major << "." << minor << "." << patch
+ << "' in cache file '" << cacheName
+ << "'.\n" << logofs_flush;
+
+ if (control -> ProxyMode == proxy_server)
+ {
+ handleFailOnLoad(cacheName, "B");
+ }
+ else
+ {
+ //
+ // Simply remove the cache file.
+ //
+
+ unlink(cacheName);
+ }
+
+ delete cachefs;
+
+ delete [] cacheName;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Reading from cache file version '"
+ << major << "." << minor << "." << patch
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (GetData(cachefs, md5FromFile, MD5_LENGTH) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No checksum in cache file '"
+ << loadName << "'.\n" << logofs_flush;
+ #endif
+
+ handleFailOnLoad(cacheName, "C");
+
+ delete cachefs;
+
+ delete [] cacheName;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ md5_state_t *md5StateStream = NULL;
+ md5_byte_t *md5DigestStream = NULL;
+
+ md5StateStream = new md5_state_t();
+ md5DigestStream = new md5_byte_t[MD5_LENGTH];
+
+ md5_init(md5StateStream);
+
+ //
+ // Use the virtual method of the proxy class.
+ //
+
+ if (handleLoadAllStores(cachefs, md5StateStream) < 0)
+ {
+ handleFailOnLoad(cacheName, "D");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete [] cacheName;
+
+ EnableSignals();
+
+ return NULL;
+ }
+
+ md5_append(md5StateStream, (const md5_byte_t *) loadName, strlen(loadName));
+ md5_finish(md5StateStream, md5DigestStream);
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ if (md5DigestStream[i] != md5FromFile[i])
+ {
+ #ifdef PANIC
+
+ *logofs << "Proxy: PANIC! Bad checksum for cache file '"
+ << cacheName << "'.\n" << logofs_flush;
+
+ for (unsigned int j = 0; j < MD5_LENGTH; j++)
+ {
+ sprintf(md5String + (j * 2), "%02X", md5FromFile[j]);
+ }
+
+ *logofs << "Proxy: PANIC! Saved checksum is '"
+ << md5String << "'.\n" << logofs_flush;
+
+ for (unsigned int j = 0; j < MD5_LENGTH; j++)
+ {
+ sprintf(md5String + (j * 2),"%02X", md5DigestStream[i]);
+ }
+
+ *logofs << "Proxy: PANIC! Calculated checksum is '"
+ << md5String << "'.\n" << logofs_flush;
+
+ #endif
+
+ handleFailOnLoad(cacheName, "E");
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete [] cacheName;
+
+ EnableSignals();
+
+ return NULL;
+ }
+ }
+
+ delete cachefs;
+
+ delete md5StateStream;
+ delete [] md5DigestStream;
+
+ delete [] cacheName;
+
+ //
+ // Restore the original handlers.
+ //
+
+ EnableSignals();
+
+ #ifdef TEST
+ *logofs << "Proxy: Successfully loaded cache file '"
+ << loadName << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Return the string provided by caller.
+ //
+
+ return loadName;
+}
+
+int Proxy::allocateChannelMap(int fd)
+{
+ //
+ // We assume that the fd is lower than
+ // the maximum allowed number. This is
+ // checked at the time the connection
+ // is accepted.
+ //
+
+ if (fd < 0 || fd >= CONNECTIONS_LIMIT)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Internal error allocating "
+ << "new channel with FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Internal error allocating "
+ << "new channel with FD#" << fd_ << ".\n";
+
+ HandleCleanup();
+ }
+
+ for (int channelId = 0;
+ channelId < CONNECTIONS_LIMIT;
+ channelId++)
+ {
+ if (checkLocalChannelMap(channelId) == 1 &&
+ fdMap_[channelId] == -1)
+ {
+ fdMap_[channelId] = fd;
+ channelMap_[fd] = channelId;
+
+ #ifdef TEST
+ *logofs << "Proxy: Allocated new channel ID#"
+ << channelId << " with FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ return channelId;
+ }
+ }
+
+ //
+ // No available channel is remaining.
+ //
+
+ #ifdef TEST
+ *logofs << "Proxy: WARNING! Can't allocate a new channel "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+}
+
+int Proxy::checkChannelMap(int channelId)
+{
+ //
+ // To be acceptable, the channel id must
+ // be an id that is not possible to use
+ // to allocate channels at this side.
+ //
+
+ if (checkLocalChannelMap(channelId) == 1)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't open a new channel "
+ << "with invalid ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't open a new channel "
+ << "with invalid ID#" << channelId << ".\n";
+
+ return -1;
+ }
+ else if (channels_[channelId] != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't open a new channel "
+ << "over an existing ID#" << channelId
+ << " with FD#" << getFd(channelId)
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't open a new channel "
+ << "over an existing ID#" << channelId
+ << " with FD#" << getFd(channelId)
+ << ".\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::assignChannelMap(int channelId, int fd)
+{
+ //
+ // We assume that the fd is lower than
+ // the maximum allowed number. This is
+ // checked at the time the connection
+ // is accepted.
+ //
+
+ if (channelId < 0 || channelId >= CONNECTIONS_LIMIT ||
+ fd < 0 || fd >= CONNECTIONS_LIMIT)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Internal error assigning "
+ << "new channel with FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Internal error assigning "
+ << "new channel with FD#" << fd_ << ".\n";
+
+ HandleCleanup();
+ }
+
+ fdMap_[channelId] = fd;
+ channelMap_[fd] = channelId;
+
+ return 1;
+}
+
+void Proxy::cleanupChannelMap(int channelId)
+{
+ int fd = fdMap_[channelId];
+
+ if (fd != -1)
+ {
+ fdMap_[channelId] = -1;
+ channelMap_[fd] = -1;
+ }
+}
+
+int Proxy::addControlCodes(T_proxy_code code, int data)
+{
+ //
+ // Flush the encode buffer plus all the outstanding
+ // control codes if there is not enough space for
+ // the new control message. We need to ensure that
+ // there are further bytes available, in the case
+ // we will need to add more token control messages.
+ //
+
+ if (controlLength_ + 3 > CONTROL_CODES_THRESHOLD)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Flushing control messages "
+ << "while sending code '" << DumpControl(code)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ if (handleFlush() < 0)
+ {
+ return -1;
+ }
+ }
+
+ controlCodes_[controlLength_++] = 0;
+ controlCodes_[controlLength_++] = (unsigned char) code;
+ controlCodes_[controlLength_++] = (unsigned char) (data == -1 ? 0 : data);
+
+ //
+ // Account for the control frame.
+ //
+
+ statistics -> addFrameOut();
+
+ return 1;
+}
+
+void Proxy::setSplitTimeout(int channelId)
+{
+ int needed = channels_[channelId] -> needSplit();
+
+ if (needed != isTimestamp(timeouts_.splitTs))
+ {
+ if (needed == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ *logofs << "Proxy: SPLIT! Allocating split timestamp at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ timeouts_.splitTs = getTimestamp();
+ }
+ else
+ {
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int _channelId = *j;
+
+ if (channels_[_channelId] != NULL &&
+ channels_[_channelId] -> needSplit() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: SPLIT! Channel for FD#"
+ << getFd(_channelId) << " still needs splits.\n"
+ << logofs_flush;
+ #endif
+
+ return;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ *logofs << "Proxy: SPLIT! Resetting split timestamp at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ timeouts_.splitTs = nullTimestamp();
+ }
+ }
+}
+
+void Proxy::setMotionTimeout(int channelId)
+{
+ int needed = channels_[channelId] -> needMotion();
+
+ if (needed != isTimestamp(timeouts_.motionTs))
+ {
+ if (channels_[channelId] -> needMotion() == 1)
+ {
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ *logofs << "Proxy: Allocating motion timestamp at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ timeouts_.motionTs = getTimestamp();
+ }
+ else
+ {
+ T_list &channelList = activeChannels_.getList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int _channelId = *j;
+
+ if (channels_[_channelId] != NULL &&
+ channels_[_channelId] -> needMotion() == 1)
+ {
+ #ifdef TEST
+ *logofs << "Proxy: SPLIT! Channel for FD#"
+ << getFd(_channelId) << " still needs motions.\n"
+ << logofs_flush;
+ #endif
+
+ return;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(SPLIT)
+ *logofs << "Proxy: Resetting motion timestamp at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ timeouts_.motionTs = nullTimestamp();
+ }
+ }
+}
+
+void Proxy::increaseChannels(int channelId)
+{
+ #ifdef TEST
+ *logofs << "Proxy: Adding channel " << channelId
+ << " to the list of active channels.\n"
+ << logofs_flush;
+ #endif
+
+ activeChannels_.add(channelId);
+
+ #ifdef TEST
+ *logofs << "Proxy: There are " << activeChannels_.getSize()
+ << " allocated channels for proxy FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+void Proxy::decreaseChannels(int channelId)
+{
+ #ifdef TEST
+ *logofs << "Proxy: Removing channel " << channelId
+ << " from the list of active channels.\n"
+ << logofs_flush;
+ #endif
+
+ activeChannels_.remove(channelId);
+
+ #ifdef TEST
+ *logofs << "Proxy: There are " << activeChannels_.getSize()
+ << " allocated channels for proxy FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+int Proxy::allocateTransport(int channelFd, int channelId)
+{
+ if (transports_[channelId] == NULL)
+ {
+ transports_[channelId] = new Transport(channelFd);
+
+ if (transports_[channelId] == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Can't allocate transport for "
+ << "channel id " << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate transport for "
+ << "channel id " << channelId << ".\n";
+
+ return -1;
+ }
+ }
+ else if (transports_[channelId] ->
+ getType() != transport_agent)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Transport for channel id "
+ << channelId << " should be null.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Transport for channel id "
+ << channelId << " should be null.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Proxy::deallocateTransport(int channelId)
+{
+ //
+ // Transport for the agent connection
+ // is passed from the outside when
+ // creating the channel.
+ //
+
+ if (transports_[channelId] ->
+ getType() != transport_agent)
+ {
+ delete transports_[channelId];
+ }
+
+ transports_[channelId] = NULL;
+
+ return 1;
+}
+
+int Proxy::handleNewGenericConnection(int clientFd, T_channel_type type, const char *label)
+{
+ int channelId = allocateChannelMap(clientFd);
+
+ if (channelId == -1)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Maximum number of available "
+ << "channels exceeded.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Maximum number of available "
+ << "channels exceeded.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Channel for " << label << " descriptor "
+ << "FD#" << clientFd << " mapped to ID#"
+ << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Turn queuing off for path server-to-proxy.
+ //
+
+ SetNoDelay(clientFd, 1);
+
+ if (allocateTransport(clientFd, channelId) < 0)
+ {
+ return -1;
+ }
+
+ switch (type)
+ {
+ case channel_cups:
+ {
+ channels_[channelId] = new CupsChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_smb:
+ {
+ channels_[channelId] = new SmbChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_media:
+ {
+ channels_[channelId] = new MediaChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_http:
+ {
+ channels_[channelId] = new HttpChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_font:
+ {
+ channels_[channelId] = new FontChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ default:
+ {
+ channels_[channelId] = new SlaveChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ }
+
+ if (channels_[channelId] == NULL)
+ {
+ deallocateTransport(channelId);
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Accepted new connection to "
+ << label << " server.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Accepted new connection to "
+ << label << " server.\n";
+
+ increaseChannels(channelId);
+
+ switch (type)
+ {
+ case channel_cups:
+ {
+ if (handleControl(code_new_cups_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case channel_smb:
+ {
+ if (handleControl(code_new_smb_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case channel_media:
+ {
+ if (handleControl(code_new_media_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case channel_http:
+ {
+ if (handleControl(code_new_http_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ case channel_font:
+ {
+ if (handleControl(code_new_font_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ default:
+ {
+ if (handleControl(code_new_slave_connection, channelId) < 0)
+ {
+ return -1;
+ }
+
+ break;
+ }
+ }
+
+ channels_[channelId] -> handleConfiguration();
+
+ return 1;
+}
+
+int Proxy::handleNewSlaveConnection(int clientFd)
+{
+ // Since ProtoStep7 (#issue 108)
+ return handleNewGenericConnection(clientFd, channel_slave, "slave");
+}
+
+
+
+int Proxy::handleNewGenericConnectionFromProxy(int channelId, T_channel_type type,
+ ChannelEndPoint &endPoint, const char *label)
+{
+ char *unixPath, *host;
+ long port;
+
+ if (endPoint.getUnixPath(&unixPath)) {
+ return handleNewGenericConnectionFromProxyUnix(channelId, type, unixPath, label);
+ }
+
+ if (endPoint.getTCPHostAndPort(&host, &port)) {
+ return handleNewGenericConnectionFromProxyTCP(channelId, type, host, port, label);
+ }
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Refusing attempted connection "
+ << "to " << label << " server.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Refusing attempted connection "
+ << "to " << label << " server.\n";
+
+ return -1;
+}
+
+int Proxy::handleNewGenericConnectionFromProxyTCP(int channelId, T_channel_type type,
+ const char *hostname, long port, const char *label)
+
+{
+ if (port <= 0)
+ {
+ //
+ // This happens when user has disabled
+ // forwarding of the specific service.
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Refusing attempted connection "
+ << "to " << label << " server.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Refusing attempted connection "
+ << "to " << label << " server.\n";
+
+ return -1;
+ }
+
+ const char *serverHost = hostname;
+ int serverAddrFamily = AF_INET;
+ sockaddr *serverAddr = NULL;
+ unsigned int serverAddrLength = 0;
+
+ #ifdef TEST
+ *logofs << "Proxy: Connecting to " << label
+ << " server '" << serverHost << "' on TCP port '"
+ << port << "'.\n" << logofs_flush;
+ #endif
+
+ int serverIPAddr = GetHostAddress(serverHost);
+
+ if (serverIPAddr == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Unknown " << label
+ << " server host '" << serverHost << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unknown " << label
+ << " server host '" << serverHost
+ << "'.\n";
+
+ return -1;
+ }
+
+ sockaddr_in *serverAddrTCP = new sockaddr_in;
+
+ serverAddrTCP -> sin_family = AF_INET;
+ serverAddrTCP -> sin_port = htons(port);
+ serverAddrTCP -> sin_addr.s_addr = serverIPAddr;
+
+ serverAddr = (sockaddr *) serverAddrTCP;
+ serverAddrLength = sizeof(sockaddr_in);
+
+ //
+ // Connect to the requested server.
+ //
+
+ int serverFd = socket(serverAddrFamily, SOCK_STREAM, PF_UNSPEC);
+
+ if (serverFd < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ delete serverAddrTCP;
+
+ return -1;
+ }
+ else if (connect(serverFd, serverAddr, serverAddrLength) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Connection to " << label
+ << " server '" << serverHost << ":" << port
+ << "' failed with error '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Connection to " << label
+ << " server '" << serverHost << ":" << port
+ << "' failed with error '" << ESTR() << "'.\n";
+
+ close(serverFd);
+
+ delete serverAddrTCP;
+
+ return -1;
+ }
+
+ delete serverAddrTCP;
+
+ if (handlePostConnectionFromProxy(channelId, serverFd, type, label) < 0)
+ {
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Forwarded new connection to "
+ << label << " server on port '" << port
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Forwarded new connection to "
+ << label << " server on port '" << port
+ << "'.\n";
+
+ return 1;
+}
+
+int Proxy::handleNewGenericConnectionFromProxyUnix(int channelId, T_channel_type type,
+ const char *path, const char *label)
+{
+ if (path == NULL || *path == '\0' )
+ {
+ //
+ // This happens when user has disabled
+ // forwarding of the specific service.
+ //
+
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Refusing attempted connection "
+ << "to " << label << " server.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Refusing attempted connection "
+ << "to " << label << " server.\n";
+
+ return -1;
+ }
+
+ sockaddr_un serverAddrUnix;
+
+ unsigned int serverAddrLength = sizeof(sockaddr_un);
+
+ int serverAddrFamily = AF_UNIX;
+
+ serverAddrUnix.sun_family = AF_UNIX;
+
+ const int serverAddrNameLength = 108;
+
+ strncpy(serverAddrUnix.sun_path, path, serverAddrNameLength);
+
+ *(serverAddrUnix.sun_path + serverAddrNameLength - 1) = '\0';
+
+ #ifdef TEST
+ *logofs << "Proxy: Connecting to " << label << " server "
+ << "on Unix port '" << path << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Connect to the requested server.
+ //
+
+ int serverFd = socket(serverAddrFamily, SOCK_STREAM, PF_UNSPEC);
+
+ if (serverFd < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+ else if (connect(serverFd, (sockaddr *) &serverAddrUnix, serverAddrLength) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "Proxy: WARNING! Connection to " << label
+ << " server on Unix port '" << path << "' failed "
+ << "with error " << EGET() << ", '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Connection to " << label
+ << " server on Unix port '" << path << "' failed "
+ << "with error " << EGET() << ", '" << ESTR() << "'.\n";
+
+ close(serverFd);
+
+ return -1;
+ }
+
+ if (handlePostConnectionFromProxy(channelId, serverFd, type, label) < 0)
+ {
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Proxy: Forwarded new connection to "
+ << label << " server on Unix port '" << path
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Forwarded new connection to "
+ << label << " server on Unix port '" << path
+ << "'.\n";
+
+ return 1;
+}
+
+int Proxy::handleNewSlaveConnectionFromProxy(int channelId)
+{
+
+ cerr << "Info" << ": New slave connection on "
+ << "channel ID#" << channelId << "\n";
+
+ char *nx_slave_cmd = getenv("NX_SLAVE_CMD");
+ if (nx_slave_cmd == NULL) {
+ return -1;
+ }
+
+ int spair[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, spair) == -1) {
+ perror("socketpair");
+ return -1;
+ }
+
+ int serverFd = spair[0];
+ int clientFd = spair[1];
+
+ if (handlePostConnectionFromProxy(channelId, serverFd, channel_slave, "slave") < 0)
+ {
+ close(serverFd);
+ close(clientFd);
+ return -1;
+ }
+
+
+ int pid = fork();
+ if (pid == 0)
+ {
+
+ if (dup2(clientFd, 0) == -1)
+ {
+ perror("dup2");
+ exit(1);
+ }
+
+ if (dup2(clientFd, 1) == -1)
+ {
+ perror("dup2");
+ exit(1);
+ }
+
+ close(serverFd);
+ close(clientFd);
+
+ /* Close FDs used by NX, QVD #1208 */
+ for (int fd = 3; fd < 256; fd++) {
+ close(fd);
+ }
+
+ char *const argv[2] = {nx_slave_cmd, NULL};
+
+ if (execv(nx_slave_cmd, argv) == -1)
+ {
+ perror("execv");
+ }
+ exit(1);
+
+ }
+ else if (pid == -1)
+ {
+ // TODO Test this!
+ perror("fork");
+ close(serverFd);
+ close(clientFd);
+ return -1;
+ }
+
+ close(clientFd);
+ slavePidMap_[channelId] = pid;
+
+ cerr << "Info" << ": slave channel ID#" << channelId << " handler has PID " << pid << endl;
+
+ return 1;
+}
+
+void Proxy::checkSlaves()
+{
+ for (int channelId = 0; channelId<CONNECTIONS_LIMIT; channelId++)
+ {
+ int pid = slavePidMap_[channelId];
+
+ if (pid > 1 && HandleChild(pid))
+ {
+ slavePidMap_[channelId] = nothing;
+ cerr << "Info:" << " Handled death of slave with pid " << pid << endl;
+ }
+ }
+}
+
+int Proxy::handlePostConnectionFromProxy(int channelId, int serverFd,
+ T_channel_type type, const char *label)
+{
+ //
+ // Turn queuing off for path proxy-to-server.
+ //
+
+ SetNoDelay(serverFd, 1);
+
+ assignChannelMap(channelId, serverFd);
+
+ #ifdef TEST
+ *logofs << "Proxy: Descriptor FD#" << serverFd
+ << " mapped to channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (allocateTransport(serverFd, channelId) < 0)
+ {
+ return -1;
+ }
+
+ switch (type)
+ {
+ case channel_cups:
+ {
+ channels_[channelId] = new CupsChannel(transports_[channelId], compressor_);
+ break;
+ }
+ case channel_smb:
+ {
+ channels_[channelId] = new SmbChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_media:
+ {
+ channels_[channelId] = new MediaChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_http:
+ {
+ channels_[channelId] = new HttpChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ case channel_font:
+ {
+ channels_[channelId] = new FontChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ default:
+ {
+ channels_[channelId] = new SlaveChannel(transports_[channelId], compressor_);
+
+ break;
+ }
+ }
+
+ if (channels_[channelId] == NULL)
+ {
+ deallocateTransport(channelId);
+
+ return -1;
+ }
+
+ increaseChannels(channelId);
+
+ channels_[channelId] -> handleConfiguration();
+
+ return 1;
+}
diff --git a/nxcomp/src/Proxy.h b/nxcomp/src/Proxy.h
new file mode 100644
index 000000000..ea60c827a
--- /dev/null
+++ b/nxcomp/src/Proxy.h
@@ -0,0 +1,1276 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Proxy_H
+#define Proxy_H
+
+#include <sys/types.h>
+
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+#include "Misc.h"
+#include "Timestamp.h"
+
+#include "List.h"
+#include "Channel.h"
+#include "Transport.h"
+#include "EncodeBuffer.h"
+#include "ProxyReadBuffer.h"
+#include "ChannelEndPoint.h"
+
+//
+// Forward declaration as we
+// need a pointer.
+//
+
+class Agent;
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Log the important tracepoints related
+// to writing packets to the peer proxy.
+//
+
+#undef FLUSH
+
+//
+// Codes used for control messages in
+// proxy-to-proxy protocol.
+//
+// The following codes are currently
+// unused.
+//
+// code_alert_reply,
+// code_reset_request,
+// code_reset_reply,
+// code_load_reply,
+// code_save_reply,
+// code_shutdown_reply,
+// code_configuration_request,
+// code_configuration_reply.
+//
+// These are for compatibility with
+// old versions.
+//
+// code_sync_request,
+// code_sync_reply,
+//
+// The code_new_aux_connection should not
+// be used anymore. Auxiliary X connections
+// are treated as normal X channels since
+// version 1.5.0.
+//
+
+typedef enum
+{
+ code_new_x_connection,
+ code_new_cups_connection,
+ code_new_aux_connection,
+ code_new_smb_connection,
+ code_new_media_connection,
+ code_switch_connection,
+ code_drop_connection,
+ code_finish_connection,
+ code_begin_congestion,
+ code_end_congestion,
+ code_alert_request,
+ code_alert_reply,
+ code_reset_request,
+ code_reset_reply,
+ code_load_request,
+ code_load_reply,
+ code_save_request,
+ code_save_reply,
+ code_shutdown_request,
+ code_shutdown_reply,
+ code_control_token_request,
+ code_control_token_reply,
+ code_configuration_request,
+ code_configuration_reply,
+ code_statistics_request,
+ code_statistics_reply,
+ code_new_http_connection,
+ code_sync_request,
+ code_sync_reply,
+ code_new_font_connection,
+ code_new_slave_connection,
+ code_finish_listeners,
+ code_split_token_request,
+ code_split_token_reply,
+ code_data_token_request,
+ code_data_token_reply,
+ code_last_tag
+
+} T_proxy_code;
+
+typedef enum
+{
+ operation_in_negotiation,
+ operation_in_messages,
+ operation_in_configuration,
+ operation_in_statistics,
+ operation_last_tag
+
+} T_proxy_operation;
+
+typedef enum
+{
+ frame_ping,
+ frame_data,
+
+} T_frame_type;
+
+typedef enum
+{
+ token_control,
+ token_split,
+ token_data
+
+} T_token_type;
+
+typedef enum
+{
+ load_if_any,
+ load_if_first
+
+} T_load_type;
+
+class Proxy
+{
+ public:
+
+ //
+ // Maximum number of supported channels.
+ //
+
+ static const int CONNECTIONS_LIMIT = 256;
+
+ //
+ // Numboer of token types.
+ //
+
+ static const int TOKEN_TYPES = 3;
+
+ //
+ // Lenght of buffer we use to add our
+ // control messages plus the length of
+ // the data frame.
+ //
+
+ static const int CONTROL_CODES_LENGTH = ENCODE_BUFFER_PREFIX_SIZE - 5;
+
+ static const int CONTROL_CODES_THRESHOLD = CONTROL_CODES_LENGTH - 9;
+
+ Proxy(int fd);
+
+ virtual ~Proxy();
+
+ //
+ // Inform the proxy that the negotiation phase is
+ // completed and that it can start handling binary
+ // messages.
+ //
+
+ int setOperational();
+
+ int getOperational()
+ {
+ return (operation_ != operation_in_negotiation);
+ }
+
+ int setReadDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax);
+
+ int setWriteDescriptors(fd_set *fdSet, int &fdMax, T_timestamp &tsMax);
+
+ //
+ // Perform the operation on the proxy
+ // link or its own channels.
+ //
+
+ int handleRead(int &resultFds, fd_set &fdSet);
+
+ int handleFlush(int &resultFds, fd_set &fdSet);
+
+ int handleRead();
+
+ int handleRead(int fd, const char *data = NULL, int size = 0);
+
+ int handleEvents();
+
+ int handleFlush();
+
+ int handleFlush(int fd);
+
+ int handlePing();
+
+ int handleFinish();
+
+ int handleShutdown();
+
+ int handleStatistics(int type, ostream *statofs);
+
+ int handleAlert(int alert);
+
+ int handleRotate()
+ {
+ activeChannels_.rotate();
+
+ return 1;
+ }
+
+ int handleChannelConfiguration();
+
+ int handleSocketConfiguration();
+
+ int handleLinkConfiguration();
+
+ int handleCacheConfiguration();
+
+ //
+ // These must be called just after initialization to
+ // tell to the proxy where the network connections
+ // have to be forwarded.
+ //
+
+ virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily,
+ sockaddr * xServerAddr, unsigned int xServerAddrLength) = 0;
+
+ virtual void handlePortConfiguration(ChannelEndPoint &cupsServerPort,
+ ChannelEndPoint &smbServerPort,
+ ChannelEndPoint &mediaServerPort,
+ ChannelEndPoint &httpServerPort,
+ const char *fontServerPort) = 0;
+
+ //
+ // Create new tunneled channels.
+ //
+
+ virtual int handleNewConnection(T_channel_type type, int clientFd) = 0;
+
+ virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId) = 0;
+
+ virtual int handleNewAgentConnection(Agent *agent) = 0;
+
+ virtual int handleNewXConnection(int clientFd) = 0;
+
+ virtual int handleNewXConnectionFromProxy(int channelId) = 0;
+
+ int handleNewGenericConnection(int clientFd, T_channel_type type, const char *label);
+
+ int handleNewGenericConnectionFromProxy(int channelId, T_channel_type type,
+ ChannelEndPoint &endpoint, const char *label);
+
+ int handleNewGenericConnectionFromProxyUnix(int channelId, T_channel_type type,
+ const char *path, const char *label);
+
+ int handleNewGenericConnectionFromProxyTCP(int channelId, T_channel_type type,
+ const char *hostname, long port, const char *label);
+
+ int handleNewSlaveConnection(int clientFd);
+
+ int handleNewSlaveConnectionFromProxy(int channelId);
+
+ void checkSlaves();
+
+ //
+ // Force closure of channels.
+ //
+
+ int handleCloseConnection(int clientFd);
+
+ int handleCloseAllXConnections();
+
+ int handleCloseAllListeners();
+
+ //
+ // Called when the loop has replaced
+ // or closed a previous alert.
+ //
+
+ void handleResetAlert();
+
+ //
+ // Handle the persistent cache.
+ //
+
+ virtual int handleLoad(T_load_type type) = 0;
+
+ virtual int handleSave() = 0;
+
+ protected:
+
+ //
+ // Timeout related data:
+ //
+ // flush
+ // split
+ // motion
+ //
+ // Timeouts in milliseconds after which the
+ // proxy will have to perform the operation.
+ //
+ // readTs, writeTs
+ //
+ // Timestamp of last packet received or sent
+ // to remote proxy. Used to detect lost con-
+ // nection.
+ //
+ // loopTs
+ //
+ // Timestamp of last loop completed by the
+ // proxy
+ //
+ // pingTs
+ //
+ // Timestamp of last ping request sent to the
+ // remote peer.
+ //
+ // alertTs
+ //
+ // Timestamp of last 'no data received' alert
+ // dialog shown to the user.
+ //
+ // loadTs
+ //
+ // Were message stores populated from data on
+ // disk.
+ //
+ // splitTs
+ // motionTs
+ //
+ // Timestamps of the last operation of this
+ // kind handled by the proxy.
+ //
+
+ typedef struct
+ {
+ int split;
+ int motion;
+
+ T_timestamp readTs;
+ T_timestamp writeTs;
+
+ T_timestamp loopTs;
+ T_timestamp pingTs;
+ T_timestamp alertTs;
+ T_timestamp loadTs;
+
+ T_timestamp splitTs;
+ T_timestamp motionTs;
+
+ } T_proxy_timeouts;
+
+ //
+ // Bytes accumulated so far while waiting
+ // to send the next token, number of tokens
+ // remaining for each token type and other
+ // token related information.
+ //
+
+ typedef struct
+ {
+ int size;
+ int limit;
+
+ int bytes;
+ int remaining;
+
+ T_proxy_code request;
+ T_proxy_code reply;
+
+ T_token_type type;
+
+ } T_proxy_token;
+
+ int handlePostConnectionFromProxy(int channelId, int serverFd,
+ T_channel_type type, const char *label);
+
+ int handleDrain();
+
+ int handleFrame(T_frame_type type);
+
+ int handleFinish(int channelId);
+
+ int handleDrop(int channelId);
+
+ int handleFinishFromProxy(int channelId);
+
+ int handleDropFromProxy(int channelId);
+
+ int handleStatisticsFromProxy(int type);
+
+ int handleStatisticsFromProxy(const unsigned char *message, unsigned int length);
+
+ int handleNegotiation(const unsigned char *message, unsigned int length);
+
+ int handleNegotiationFromProxy(const unsigned char *message, unsigned int length);
+
+ int handleToken(T_frame_type type);
+
+ int handleTokenFromProxy(T_proxy_token &token, int count);
+
+ int handleTokenReplyFromProxy(T_proxy_token &token, int count);
+
+ int handleSyncFromProxy(int channelId);
+
+ int handleSwitch(int channelId);
+
+ int handleControl(T_proxy_code code, int data = -1);
+
+ int handleControlFromProxy(const unsigned char *message);
+
+ //
+ // Interleave reads of the X server
+ // events while writing data to the
+ // remote proxy.
+ //
+
+ virtual int handleAsyncEvents() = 0;
+
+ //
+ // Timer related functions.
+ //
+
+ protected:
+
+ void setTimer(int value)
+ {
+ SetTimer(value);
+ }
+
+ void resetTimer()
+ {
+ ResetTimer();
+
+ timer_ = 0;
+ }
+
+ public:
+
+ void handleTimer()
+ {
+ timer_ = 1;
+ }
+
+ int getTimer()
+ {
+ return timer_;
+ }
+
+ //
+ // Can the channel consume data and the
+ // proxy produce more output?
+ //
+
+ int canRead(int fd) const
+ {
+ return (isTimeToRead() == 1 &&
+ isTimeToRead(getChannel(fd)) == 1);
+ }
+
+ //
+ // Can the proxy read more data from its
+ // peer?
+ //
+
+ int canRead() const
+ {
+ return (transport_ -> readable() != 0);
+ }
+
+ int canFlush() const
+ {
+ return (encodeBuffer_.getLength() +
+ controlLength_ + transport_ -> length() +
+ transport_ -> flushable() > 0);
+ }
+
+ int needFlush(int channelId) const
+ {
+ return (outputChannel_ == channelId &&
+ encodeBuffer_.getLength() > 0);
+ }
+
+ int needFlush() const
+ {
+ return (encodeBuffer_.getLength() > 0);
+ }
+
+ int shouldFlush() const
+ {
+ if ((int) ((encodeBuffer_.getLength() +
+ controlLength_) / statistics ->
+ getStreamRatio()) >= control -> TokenSize)
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: FLUSH! Requesting a flush with "
+ << (encodeBuffer_.getLength() + controlLength_)
+ << " bytes and stream ratio " << (double) statistics ->
+ getStreamRatio() << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "Proxy: Not requesting a flush with "
+ << (encodeBuffer_.getLength() + controlLength_)
+ << " bytes and stream ratio " << (double) statistics ->
+ getStreamRatio() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ int needDrain() const
+ {
+ return (congestion_ == 1 || transport_ -> length() >
+ control -> TransportProxyBufferThreshold);
+ }
+
+ int getFd() const
+ {
+ return fd_;
+ }
+
+ int getFlushable(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return (encodeBuffer_.getLength() + controlLength_ +
+ transport_ -> flushable());
+ }
+
+ return 0;
+ }
+
+ int getSplitSize()
+ {
+ return (clientStore_ != NULL ? clientStore_ ->
+ getSplitTotalSize() : 0);
+ }
+
+ int getSplitStorageSize()
+ {
+ return (clientStore_ != NULL ? clientStore_ ->
+ getSplitTotalStorageSize() : 0);
+ }
+
+ //
+ // Returns the number of active channels
+ // that currently managed by this proxy.
+ //
+
+ int getChannels(T_channel_type type = channel_none);
+
+ //
+ // If descriptor corresponds to a valid
+ // channel, returns the type of traffic
+ // carried by it.
+ //
+
+ T_channel_type getType(int fd);
+
+ //
+ // Given a channel type, returns the
+ // literal name.
+ //
+
+ const char *getTypeName(T_channel_type type);
+
+ //
+ // Get a convenient name for 'localhost'.
+ //
+
+ const char *getComputerName();
+
+ //
+ // Set if we have initiated the shutdown
+ // procedure and if the shutdown request
+ // has been received from the remote.
+ //
+
+ int getFinish() const
+ {
+ return finish_;
+ }
+
+ int getShutdown() const
+ {
+ return shutdown_;
+ }
+
+ //
+ // Interfaces to the transport buffers.
+ //
+
+ int getLength(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return transport_ -> length();
+ }
+
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return 0;
+ }
+
+ return transports_[channelId] -> length();
+ }
+
+ int getPending(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return transport_ -> pending();
+ }
+
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return 0;
+ }
+
+ return transports_[channelId] -> pending();
+ }
+
+ //
+ // Check if the proxy or the given channel
+ // has data in the buffer because of a
+ // blocking write.
+ //
+
+ int getBlocked(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return transport_ -> blocked();
+ }
+
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return 0;
+ }
+
+ return transports_[channelId] -> blocked();
+ }
+
+ //
+ // Check if the proxy or the given channel has
+ // data to read.
+ //
+
+ int getReadable(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return transport_ -> readable();
+ }
+
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return 0;
+ }
+
+ return transports_[channelId] -> readable();
+ }
+
+ //
+ // Return a vale between 0 and 9 in the case
+ // of the proxy descriptor.
+ //
+
+ int getCongestion(int fd) const
+ {
+ if (fd == fd_)
+ {
+ return (agent_ != nothing && congestions_[agent_] == 1 ? 9 :
+ (int) statistics -> getCongestionInFrame());
+ }
+
+ int channelId = getChannel(fd);
+
+ if (channelId < 0 || channels_[channelId] == NULL)
+ {
+ return 0;
+ }
+
+ return channels_[channelId] -> getCongestion();
+ }
+
+ //
+ // Let the statistics class get info
+ // from the message stores.
+ //
+
+ const ClientStore * const getClientStore() const
+ {
+ return clientStore_;
+ }
+
+ const ServerStore * const getServerStore() const
+ {
+ return serverStore_;
+ }
+
+ //
+ // These can be called asynchronously by
+ // channels during their read or write
+ // loop.
+ //
+
+ int handleAsyncRead(int fd)
+ {
+ return handleRead(fd);
+ }
+
+ int handleAsyncCongestion(int fd)
+ {
+ int channelId = getChannel(fd);
+
+ return handleControl(code_begin_congestion, channelId);
+ }
+
+ int handleAsyncDecongestion(int fd)
+ {
+ int channelId = getChannel(fd);
+
+ return handleControl(code_end_congestion, channelId);
+ }
+
+ int handleAsyncSplit(int fd, Split *split)
+ {
+ return channels_[getChannel(fd)] ->
+ handleSplitEvent(encodeBuffer_, split);
+ }
+
+ int handleAsyncPriority()
+ {
+ if (control -> FlushPriority == 1)
+ {
+ return handleFlush();
+ }
+
+ return 0;
+ }
+
+ int canAsyncFlush() const
+ {
+ return shouldFlush();
+ }
+
+ int handleAsyncFlush()
+ {
+ return handleFlush();
+ }
+
+ int handleAsyncSwitch(int fd)
+ {
+ return handleSwitch(getChannel(fd));
+ }
+
+ int handleAsyncKeeperCallback()
+ {
+ KeeperCallback();
+
+ return 1;
+ }
+
+ //
+ // Internal interfaces used to verify the
+ // availability of channels and the proxy
+ // link.
+ //
+
+ protected:
+
+ int isTimeToRead() const
+ {
+ if (congestion_ == 0 && transport_ ->
+ blocked() == 0)
+ {
+ return 1;
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Can't read from channels "
+ << "with congestion " << congestion_
+ << " and blocked " << transport_ ->
+ blocked() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+
+ int isTimeToRead(int channelId) const
+ {
+ if (channelId >= 0 && channelId < CONNECTIONS_LIMIT &&
+ channels_[channelId] != NULL &&
+ congestions_[channelId] == 0)
+ {
+ if (channels_[channelId] -> getType() == channel_x11 ||
+ tokens_[token_data].remaining > 0 ||
+ channels_[channelId] ->
+ getFinish() == 1)
+ {
+ return 1;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: Can't read from generic "
+ << "descriptor FD#" << getFd(channelId)
+ << " channel ID#" << channelId << " with "
+ << tokens_[token_data].remaining
+ << " data tokens remaining.\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (channelId < 0 || channelId >= CONNECTIONS_LIMIT ||
+ channels_[channelId] == NULL)
+ {
+ *logofs << "Proxy: WARNING! No valid channel for ID#"
+ << channelId << ".\n" << logofs_flush;
+ }
+ else if (channels_[channelId] -> getFinish())
+ {
+ *logofs << "Proxy: Can't read from finishing "
+ << "descriptor FD#" << getFd(channelId)
+ << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ }
+ else if (congestions_[channelId] == 1)
+ {
+ *logofs << "Proxy: Can't read from congested "
+ << "descriptor FD#" << getFd(channelId)
+ << " channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Handle the flush and split timeouts.
+ // All these functions should round up
+ // to the value of the latency timeout
+ // to save a further loop.
+ //
+
+ protected:
+
+ int isTimeToSplit() const
+ {
+ if (isTimestamp(timeouts_.splitTs) &&
+ getTimeToNextSplit() <= control ->
+ LatencyTimeout)
+ {
+ if (tokens_[token_split].remaining > 0)
+ {
+ return 1;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "Proxy: WARNING! Can't encode splits "
+ << "with " << tokens_[token_split].remaining
+ << " split tokens remaining.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ return 0;
+ }
+
+ int isTimeToMotion() const
+ {
+ return (isTimestamp(timeouts_.motionTs) &&
+ getTimeToNextMotion() <= control ->
+ LatencyTimeout);
+ }
+
+ int getTimeToNextSplit() const
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+
+ if (isTimestamp(timeouts_.splitTs) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No split timeout was set "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No split timeout was set "
+ << "for proxy FD#" << fd_ << ".\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ int diffTs = timeouts_.split -
+ diffTimestamp(timeouts_.splitTs,
+ getTimestamp());
+
+ return (diffTs > 0 ? diffTs : 0);
+ }
+
+ int getTimeToNextMotion() const
+ {
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+
+ if (isTimestamp(timeouts_.motionTs) == 0)
+ {
+ #ifdef PANIC
+ *logofs << "Proxy: PANIC! No motion timeout was set "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No motion timeout was set "
+ << "for proxy FD#" << fd_ << ".\n";
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ int diffTs = timeouts_.motion -
+ diffTimestamp(timeouts_.motionTs,
+ getTimestamp());
+
+ return (diffTs > 0 ? diffTs : 0);
+ }
+
+ protected:
+
+ //
+ // Implement persistence of cache on disk.
+ //
+
+ virtual int handleLoadFromProxy() = 0;
+ virtual int handleSaveFromProxy() = 0;
+
+ int handleLoadStores();
+ int handleSaveStores();
+
+ char *handleSaveAllStores(const char *savePath, bool & isTooSmall) const;
+
+ virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient) const = 0;
+
+ int handleSaveVersion(unsigned char *buffer, int &major, int &minor, int &patch) const;
+
+ void handleFailOnSave(const char *fullName, const char *failContext) const;
+
+ const char *handleLoadAllStores(const char *loadPath, const char *loadName) const;
+
+ virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const = 0;
+
+ int handleLoadVersion(const unsigned char *buffer, int &major, int &minor, int &patch) const;
+
+ void handleFailOnLoad(const char *fullName, const char *failContext) const;
+
+ //
+ // Prepare for a new persistent cache.
+ //
+
+ int handleResetPersistentCache();
+
+ //
+ // Reset the stores in the case of a
+ // failure loading the cache.
+ //
+
+ int handleResetStores();
+
+ //
+ // Reset the transport buffer and the
+ // other counters.
+ //
+
+ void handleResetFlush();
+
+ //
+ // Utility functions used to map file
+ // descriptors to channel ids.
+ //
+
+ protected:
+
+ int allocateChannelMap(int fd);
+ int checkChannelMap(int channelId);
+ int assignChannelMap(int channelId, int fd);
+
+ void cleanupChannelMap(int channelId);
+
+ virtual int checkLocalChannelMap(int channelId) = 0;
+
+ int addControlCodes(T_proxy_code code, int data);
+ int addTokenCodes(T_proxy_token &token);
+
+ int getFd(int channelId) const
+ {
+ if (channelId >= 0 && channelId < CONNECTIONS_LIMIT)
+ {
+ return fdMap_[channelId];
+ }
+
+ return -1;
+ }
+
+ int getChannel(int fd) const
+ {
+ if (fd >= 0 && fd < CONNECTIONS_LIMIT)
+ {
+ return channelMap_[fd];
+ }
+
+ return -1;
+ }
+
+ protected:
+
+ void setSplitTimeout(int channelId);
+ void setMotionTimeout(int channelId);
+
+ void increaseChannels(int channelId);
+ void decreaseChannels(int channelId);
+
+ int allocateTransport(int channelFd, int channelId);
+ int deallocateTransport(int channelId);
+
+ //
+ // The proxy has its own transport.
+ //
+
+ ProxyTransport *transport_;
+
+ //
+ // The static compressor is shared among
+ // channels and all the message stores.
+ //
+
+ StaticCompressor *compressor_;
+
+ //
+ // Map NX specific opcodes.
+ //
+
+ OpcodeStore *opcodeStore_;
+
+ //
+ // Stores are shared between channels.
+ //
+
+ ClientStore *clientStore_;
+ ServerStore *serverStore_;
+
+ //
+ // Client and server caches are shared
+ // between channels.
+ //
+
+ ClientCache *clientCache_;
+ ServerCache *serverCache_;
+
+ //
+ // The proxy's file descriptor.
+ //
+
+ int fd_;
+
+ //
+ // Channels currently selected for I/O
+ // operations.
+ //
+
+ int inputChannel_;
+ int outputChannel_;
+
+ //
+ // List of active channels.
+ //
+
+ List activeChannels_;
+
+ //
+ // Used to read data sent from peer proxy.
+ //
+
+ ProxyReadBuffer readBuffer_;
+
+ //
+ // Used to send data to peer proxy.
+ //
+
+ EncodeBuffer encodeBuffer_;
+
+ //
+ // Control messages' array.
+ //
+
+ int controlLength_;
+
+ unsigned char controlCodes_[CONTROL_CODES_LENGTH];
+
+ //
+ // Table of channel classes taking
+ // care of open X connections.
+ //
+
+ Channel *channels_[CONNECTIONS_LIMIT];
+
+ //
+ // Table of open sockets.
+ //
+
+ Transport *transports_[CONNECTIONS_LIMIT];
+
+ //
+ // Timeout related data.
+ //
+
+ T_proxy_timeouts timeouts_;
+
+ //
+ // Proxy can be decoding messages,
+ // handling a link reconfiguration,
+ // or decoding statistics.
+ //
+
+ int operation_;
+
+ //
+ // True if we are currently draining
+ // the proxy link.
+ //
+
+ int draining_;
+
+ //
+ // Force flush because of prioritized
+ // control messages to send.
+ //
+
+ int priority_;
+
+ //
+ // Set if we have initiated the close
+ // down procedure.
+ //
+
+ int finish_;
+
+ //
+ // Remote peer requested the shutdown.
+ //
+
+ int shutdown_;
+
+ //
+ // We are in the middle of a network
+ // congestion in the path to remote
+ // proxy.
+ //
+
+ int congestion_;
+
+ //
+ // Channels at the remote end that
+ // are not consuming their data.
+ //
+
+ int congestions_[CONNECTIONS_LIMIT];
+
+ //
+ // Is the timer expired?
+ //
+
+ int timer_;
+
+ //
+ // Did the proxy request an alert?
+ //
+
+ int alert_;
+
+ //
+ // The channel id of the agent.
+ //
+
+ int agent_;
+
+ //
+ // Token related data.
+ //
+
+ T_proxy_token tokens_[TOKEN_TYPES];
+
+ //
+ // Pointer to stream descriptor where
+ // proxy is producing statistics.
+ //
+
+ ostream *currentStatistics_;
+
+ private:
+
+ //
+ // Map channel ids to file descriptors.
+ //
+
+ int channelMap_[CONNECTIONS_LIMIT];
+ int fdMap_[CONNECTIONS_LIMIT];
+ int slavePidMap_[CONNECTIONS_LIMIT];
+};
+
+#endif /* Proxy_H */
diff --git a/nxcomp/src/ProxyReadBuffer.cpp b/nxcomp/src/ProxyReadBuffer.cpp
new file mode 100644
index 000000000..77f12771b
--- /dev/null
+++ b/nxcomp/src/ProxyReadBuffer.cpp
@@ -0,0 +1,211 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ProxyReadBuffer.h"
+
+#include "Transport.h"
+
+//
+// Set the verbosity level. You also
+// need to define DUMP in Misc.cpp
+// if DUMP is defined here.
+//
+
+#define WARNING
+#define PANIC
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+unsigned int ProxyReadBuffer::suggestedLength(unsigned int pendingLength)
+{
+ //
+ // Always read all the data that
+ // is available.
+ //
+
+ int readable = transport_ -> readable();
+
+ unsigned int readLength = (readable == -1 ? 0 : (unsigned int) readable);
+
+ if (readLength < pendingLength)
+ {
+ readLength = pendingLength;
+ }
+
+ //
+ // Even if the readable data is not
+ // enough to make a complete message,
+ // resize the buffer to accommodate
+ // it all.
+ //
+
+ if (pendingLength < remaining_)
+ {
+ readLength = remaining_;
+ }
+
+ return readLength;
+}
+
+int ProxyReadBuffer::locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength)
+{
+ unsigned int lengthLength = 0;
+ const unsigned char *nextSrc = start;
+ unsigned char next;
+
+ dataLength = 0;
+
+ #ifdef TEST
+ *logofs << "ProxyReadBuffer: Locating message for FD#"
+ << transport_ -> fd() << " with " << end - start
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ //
+ // Use something like the following if
+ // you are looking for errors.
+ //
+
+ #ifdef DUMP
+ if (control -> ProxyMode == proxy_server && start < end &&
+ transport_ -> fd() == 6 || transport_ -> fd() == 11)
+ {
+ *logofs << "ProxyReadBuffer: Partial checksums are:\n";
+
+ DumpBlockChecksums(start, end - start, 256);
+
+ *logofs << logofs_flush;
+ }
+ #endif
+
+ do
+ {
+ if (nextSrc >= end)
+ {
+ remaining_ = 1;
+
+ #ifdef TEST
+ *logofs << "ProxyReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ next = *nextSrc++;
+
+ dataLength <<= 7;
+ dataLength |= (unsigned int) (next & 0x7f);
+
+ lengthLength++;
+ }
+ while (next & 0x80);
+
+ unsigned int totalLength;
+
+ if (dataLength == 0)
+ {
+ trailerLength = 0;
+ controlLength = 3;
+ totalLength = controlLength;
+ }
+ else
+ {
+ trailerLength = lengthLength;
+ controlLength = 0;
+ totalLength = dataLength + trailerLength;
+ }
+
+ if (start + totalLength > end)
+ {
+ //
+ // When having to decompress a ZLIB stream,
+ // a single byte can be enough to complete
+ // the frame.
+ //
+
+ if (control -> RemoteStreamCompression == 0)
+ {
+ remaining_ = totalLength - (end - start);
+ }
+ else
+ {
+ remaining_ = 1;
+ }
+
+ #ifdef TEST
+ *logofs << "ProxyReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef DUMP
+ *logofs << "ProxyReadBuffer: Received " << totalLength << " bytes of data "
+ << "with checksum ";
+
+ DumpChecksum(start, totalLength);
+
+ *logofs << " on proxy FD#" << transport_ -> fd() << ".\n" << logofs_flush;
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "ProxyReadBuffer: Produced plain input for " << dataLength
+ << "+" << trailerLength << "+" << controlLength << " bytes out of "
+ << totalLength << " bytes.\n" << logofs_flush;
+ #endif
+
+ #ifdef DUMP
+ *logofs << "ProxyReadBuffer: Partial checksums are:\n";
+
+ DumpBlockChecksums(start, totalLength, 256);
+
+ *logofs << logofs_flush;
+ #endif
+
+ remaining_ = 0;
+
+ #ifdef TEST
+ *logofs << "ProxyReadBuffer: Located message with "
+ << "remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+}
diff --git a/nxcomp/src/ProxyReadBuffer.h b/nxcomp/src/ProxyReadBuffer.h
new file mode 100644
index 000000000..68e9e95fa
--- /dev/null
+++ b/nxcomp/src/ProxyReadBuffer.h
@@ -0,0 +1,57 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ProxyReadBuffer_H
+#define ProxyReadBuffer_H
+
+#include "ReadBuffer.h"
+#include "Control.h"
+
+class ProxyReadBuffer : public ReadBuffer
+{
+ public:
+
+ ProxyReadBuffer(Transport *transport)
+
+ : ReadBuffer(transport)
+ {
+ }
+
+ virtual ~ProxyReadBuffer()
+ {
+ }
+
+ protected:
+
+ virtual unsigned int suggestedLength(unsigned int pendingLength);
+
+ virtual int locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength);
+};
+
+#endif /* ProxyReadBuffer_H */
diff --git a/nxcomp/src/PutImage.cpp b/nxcomp/src/PutImage.cpp
new file mode 100644
index 000000000..d14f922c3
--- /dev/null
+++ b/nxcomp/src/PutImage.cpp
@@ -0,0 +1,411 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "PutImage.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+PutImageStore::PutImageStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = PUTIMAGE_ENABLE_CACHE;
+ enableData = PUTIMAGE_ENABLE_DATA;
+
+ // Since ProtoStep7 (#issue 108)
+ enableCompress = PUTIMAGE_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = PUTIMAGE_DATA_LIMIT;
+ dataOffset = PUTIMAGE_DATA_OFFSET;
+
+ cacheSlots = PUTIMAGE_CACHE_SLOTS;
+ cacheThreshold = PUTIMAGE_CACHE_THRESHOLD;
+ cacheLowerThreshold = PUTIMAGE_CACHE_LOWER_THRESHOLD;
+
+ // Since ProtoStep8 (#issue 108)
+ enableSplit = PUTIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+PutImageStore::~PutImageStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PutImageStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(GetUINT(buffer + 2, bigEndian), 16, 8);
+
+ encodeBuffer.encodeValue((unsigned int) buffer[1], 2);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> drawableCache);
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> gcCache);
+
+ unsigned int width = GetUINT(buffer + 12, bigEndian);
+ encodeBuffer.encodeCachedValue(width, 16,
+ clientCache -> putImageWidthCache, 8);
+
+ unsigned int height = GetUINT(buffer + 14, bigEndian);
+ encodeBuffer.encodeCachedValue(height, 16,
+ clientCache -> putImageHeightCache, 8);
+
+ unsigned int x = GetUINT(buffer + 16, bigEndian);
+ int xDiff = x - clientCache -> putImageLastX;
+ clientCache -> putImageLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache -> putImageXCache, 8);
+
+ unsigned int y = GetUINT(buffer + 18, bigEndian);
+ int yDiff = y - clientCache -> putImageLastY;
+ clientCache -> putImageLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache -> putImageYCache, 8);
+
+ encodeBuffer.encodeCachedValue(buffer[20], 8,
+ clientCache -> putImageLeftPadCache);
+
+ encodeBuffer.encodeCachedValue(buffer[21], 8,
+ clientCache -> depthCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutImageStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+ unsigned char cValue;
+
+ decodeBuffer.decodeValue(value, 16, 8);
+
+ size = (value << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeValue(value, 2);
+ buffer[1] = (unsigned char) value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+ PutULONG(value, buffer + 8, bigEndian);
+
+ unsigned int width;
+ decodeBuffer.decodeCachedValue(width, 16,
+ clientCache -> putImageWidthCache, 8);
+ PutUINT(width, buffer + 12, bigEndian);
+
+ unsigned int height;
+ decodeBuffer.decodeCachedValue(height, 16,
+ clientCache -> putImageHeightCache, 8);
+ PutUINT(height, buffer + 14, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageXCache, 8);
+ clientCache -> putImageLastX += value;
+ clientCache -> putImageLastX &= 0xffff;
+ PutUINT(clientCache -> putImageLastX, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageYCache, 8);
+ clientCache -> putImageLastY += value;
+ clientCache -> putImageLastY &= 0xffff;
+ PutUINT(clientCache -> putImageLastY, buffer + 18, bigEndian);
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> putImageLeftPadCache);
+ buffer[20] = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ buffer[21] = cValue;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutImageStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PutImageMessage *putImage = (PutImageMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ putImage -> format = *(buffer + 1);
+ putImage -> depth = *(buffer + 21);
+ putImage -> left_pad = *(buffer + 20);
+
+ putImage -> width = GetUINT(buffer + 12, bigEndian);
+ putImage -> height = GetUINT(buffer + 14, bigEndian);
+ putImage -> pos_x = GetUINT(buffer + 16, bigEndian);
+ putImage -> pos_y = GetUINT(buffer + 18, bigEndian);
+
+ putImage -> drawable = GetULONG(buffer + 4, bigEndian);
+ putImage -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutImageStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PutImageMessage *putImage = (PutImageMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = putImage -> format;
+
+ PutULONG(putImage -> drawable, buffer + 4, bigEndian);
+ PutULONG(putImage -> gcontext, buffer + 8, bigEndian);
+
+ PutUINT(putImage -> width, buffer + 12, bigEndian);
+ PutUINT(putImage -> height, buffer + 14, bigEndian);
+ PutUINT(putImage -> pos_x, buffer + 16, bigEndian);
+ PutUINT(putImage -> pos_y, buffer + 18, bigEndian);
+
+ *(buffer + 20) = (unsigned char) putImage -> left_pad;
+ *(buffer + 21) = (unsigned char) putImage -> depth;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PutImageStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PutImageMessage *putImage = (PutImageMessage *) message;
+
+ *logofs << name() << ": Identity format " << (unsigned) putImage -> format
+ << ", depth " << (unsigned) putImage -> depth << ", left_pad "
+ << (unsigned) putImage -> left_pad << ", width " << putImage -> width
+ << ", height " << putImage -> height << ", pos_x " << putImage -> pos_x
+ << ", pos_y " << putImage -> pos_y << ", drawable " << putImage -> drawable
+ << ", gcontext " << putImage -> gcontext << ", size " << putImage -> size_
+ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void PutImageStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Fields format, width, height, left_pad, depth.
+ //
+
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 12, 4);
+ md5_append(md5_state_, buffer + 20, 2);
+}
+
+void PutImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ PutImageMessage *putImage = (PutImageMessage *) message;
+ PutImageMessage *cachedPutImage = (PutImageMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(putImage -> drawable, clientCache -> drawableCache);
+
+ cachedPutImage -> drawable = putImage -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putImage -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(putImage -> gcontext, clientCache -> gcCache);
+
+ cachedPutImage -> gcontext = putImage -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putImage -> pos_x
+ << " as " << "pos_x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = putImage -> pos_x - cachedPutImage -> pos_x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> putImageXCache, 8);
+
+ cachedPutImage -> pos_x = putImage -> pos_x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putImage -> pos_y
+ << " as " << "pos_y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = putImage -> pos_y - cachedPutImage -> pos_y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> putImageYCache, 8);
+
+ cachedPutImage -> pos_y = putImage -> pos_y;
+}
+
+void PutImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ //
+ // Decode the variant part.
+ //
+
+ PutImageMessage *putImage = (PutImageMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ putImage -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ putImage -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putImage -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageXCache, 8);
+
+ putImage -> pos_x += value;
+ putImage -> pos_x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putImage -> pos_x
+ << " as pos_x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageYCache, 8);
+
+ putImage -> pos_y += value;
+ putImage -> pos_y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putImage -> pos_y
+ << " as pos_y field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/PutImage.h b/nxcomp/src/PutImage.h
new file mode 100644
index 000000000..93dde9dfb
--- /dev/null
+++ b/nxcomp/src/PutImage.h
@@ -0,0 +1,172 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef PutImage_H
+#define PutImage_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define PUTIMAGE_ENABLE_CACHE 1
+#define PUTIMAGE_ENABLE_DATA 1
+
+#define PUTIMAGE_DATA_LIMIT 262144 - 24
+#define PUTIMAGE_DATA_OFFSET 24
+
+#define PUTIMAGE_CACHE_SLOTS 6000
+#define PUTIMAGE_CACHE_THRESHOLD 70
+#define PUTIMAGE_CACHE_LOWER_THRESHOLD 50
+
+#define PUTIMAGE_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+#define PUTIMAGE_CACHE_THRESHOLD_IF_PACKED 10
+#define PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED 5
+
+#define PUTIMAGE_CACHE_THRESHOLD_IF_SHADOW 97
+#define PUTIMAGE_CACHE_LOWER_THRESHOLD_IF_SHADOW 90
+
+#define PUTIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8 0
+
+//
+// The message class.
+//
+
+class PutImageMessage : public Message
+{
+ friend class PutImageStore;
+
+ public:
+
+ PutImageMessage()
+ {
+ }
+
+ ~PutImageMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char format;
+ unsigned char depth;
+ unsigned char left_pad;
+ unsigned short width;
+ unsigned short height;
+ unsigned int drawable;
+ unsigned int gcontext;
+ unsigned short pos_x;
+ unsigned short pos_y;
+};
+
+class PutImageStore : public MessageStore
+{
+ public:
+
+ PutImageStore(StaticCompressor *compressor);
+
+ virtual ~PutImageStore();
+
+ virtual const char *name() const
+ {
+ return "PutImage";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_PutImage;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PutImageMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new PutImageMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PutImageMessage((const PutImageMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PutImageMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PutImage_H */
diff --git a/nxcomp/src/PutPackedImage.cpp b/nxcomp/src/PutPackedImage.cpp
new file mode 100644
index 000000000..0bae2c0d4
--- /dev/null
+++ b/nxcomp/src/PutPackedImage.cpp
@@ -0,0 +1,604 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "PutPackedImage.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+PutPackedImageStore::PutPackedImageStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = PUTPACKEDIMAGE_ENABLE_CACHE;
+ enableData = PUTPACKEDIMAGE_ENABLE_DATA;
+ enableCompress = PUTPACKEDIMAGE_ENABLE_COMPRESS;
+
+ dataLimit = PUTPACKEDIMAGE_DATA_LIMIT;
+ dataOffset = PUTPACKEDIMAGE_DATA_OFFSET;
+
+ cacheSlots = PUTPACKEDIMAGE_CACHE_SLOTS;
+ cacheThreshold = PUTPACKEDIMAGE_CACHE_THRESHOLD;
+ cacheLowerThreshold = PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD;
+
+ // Since ProtoStep8 (#issue 108)
+ enableSplit = PUTPACKEDIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+PutPackedImageStore::~PutPackedImageStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int PutPackedImageStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ // Client.
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> resourceCache);
+
+ // Size.
+ encodeBuffer.encodeValue(GetUINT(buffer + 2, bigEndian), 16, 10);
+
+ // Drawable.
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> drawableCache);
+ // GC.
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> gcCache);
+ // Method.
+ encodeBuffer.encodeCachedValue(*(buffer + 12), 8,
+ clientCache -> methodCache);
+ // Format.
+ encodeBuffer.encodeValue(*(buffer + 13), 2);
+
+ // SrcDepth.
+ encodeBuffer.encodeCachedValue(*(buffer + 14), 8,
+ clientCache -> depthCache);
+ // DstDepth.
+ encodeBuffer.encodeCachedValue(*(buffer + 15), 8,
+ clientCache -> depthCache);
+ // SrcLength.
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 24,
+ clientCache -> putPackedImageSrcLengthCache);
+ // DstLength.
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 24,
+ clientCache -> putPackedImageDstLengthCache);
+ // SrcX.
+ unsigned int x = GetUINT(buffer + 24, bigEndian);
+ int xDiff = x - clientCache -> putImageLastX;
+ clientCache -> putImageLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache -> putImageXCache, 8);
+ // SrcY.
+ unsigned int y = GetUINT(buffer + 26, bigEndian);
+ int yDiff = y - clientCache -> putImageLastY;
+ clientCache -> putImageLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache -> putImageYCache, 8);
+ // SrcWidth.
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 28, bigEndian), 16,
+ clientCache -> putImageWidthCache, 8);
+ // SrcHeight.
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 30, bigEndian), 16,
+ clientCache -> putImageHeightCache, 8);
+ // DstX.
+ x = GetUINT(buffer + 32, bigEndian);
+ xDiff = x - clientCache -> putImageLastX;
+ clientCache -> putImageLastX = x;
+ encodeBuffer.encodeCachedValue(xDiff, 16,
+ clientCache -> putImageXCache, 8);
+ // DstY.
+ y = GetUINT(buffer + 34, bigEndian);
+ yDiff = y - clientCache -> putImageLastY;
+ clientCache -> putImageLastY = y;
+ encodeBuffer.encodeCachedValue(yDiff, 16,
+ clientCache -> putImageYCache, 8);
+ // DstWidth.
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 36, bigEndian), 16,
+ clientCache -> putImageWidthCache, 8);
+ // DstHeight.
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 38, bigEndian), 16,
+ clientCache -> putImageHeightCache, 8);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutPackedImageStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+ unsigned char cValue;
+
+ // Client.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> resourceCache);
+
+ // Size.
+ decodeBuffer.decodeValue(size, 16, 10);
+
+ size <<= 2;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ *(buffer + 1) = cValue;
+
+ // Drawable.
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+ PutULONG(value, buffer + 4, bigEndian);
+
+ // GC.
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+ PutULONG(value, buffer + 8, bigEndian);
+
+ // Method.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> methodCache);
+ *(buffer + 12) = cValue;
+
+ // Format.
+ decodeBuffer.decodeValue(value, 2);
+ *(buffer + 13) = value;
+
+ // SrcDepth.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 14) = cValue;
+
+ // DstDepth.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 15) = cValue;
+
+ // SrcLength.
+ decodeBuffer.decodeCachedValue(value, 24,
+ clientCache -> putPackedImageSrcLengthCache);
+ PutULONG(value, buffer + 16, bigEndian);
+
+ // DstLength.
+ decodeBuffer.decodeCachedValue(value, 24,
+ clientCache -> putPackedImageDstLengthCache);
+ PutULONG(value, buffer + 20, bigEndian);
+
+ // SrcX.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageXCache, 8);
+ clientCache -> putImageLastX += value;
+ clientCache -> putImageLastX &= 0xffff;
+ PutUINT(clientCache -> putImageLastX, buffer + 24, bigEndian);
+
+ // SrcY.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageYCache, 8);
+ clientCache -> putImageLastY += value;
+ clientCache -> putImageLastY &= 0xffff;
+ PutUINT(clientCache -> putImageLastY, buffer + 26, bigEndian);
+
+ // SrcWidth.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageWidthCache, 8);
+ PutUINT(value, buffer + 28, bigEndian);
+
+ // SrcHeight.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageHeightCache, 8);
+ PutUINT(value, buffer + 30, bigEndian);
+
+ // DstX.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageXCache, 8);
+ clientCache -> putImageLastX += value;
+ clientCache -> putImageLastX &= 0xffff;
+ PutUINT(clientCache -> putImageLastX, buffer + 32, bigEndian);
+
+ // DstY.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageYCache, 8);
+ clientCache -> putImageLastY += value;
+ clientCache -> putImageLastY &= 0xffff;
+ PutUINT(clientCache -> putImageLastY, buffer + 34, bigEndian);
+
+ // DstWidth.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageWidthCache, 8);
+ PutUINT(value, buffer + 36, bigEndian);
+
+ // DstHeight.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageHeightCache, 8);
+ PutUINT(value, buffer + 38, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutPackedImageStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ putPackedImage -> client = *(buffer + 1);
+
+ putPackedImage -> drawable = GetULONG(buffer + 4, bigEndian);
+ putPackedImage -> gcontext = GetULONG(buffer + 8, bigEndian);
+
+ putPackedImage -> method = *(buffer + 12);
+
+ putPackedImage -> format = *(buffer + 13);
+ putPackedImage -> src_depth = *(buffer + 14);
+ putPackedImage -> dst_depth = *(buffer + 15);
+
+ putPackedImage -> src_length = GetULONG(buffer + 16, bigEndian);
+ putPackedImage -> dst_length = GetULONG(buffer + 20, bigEndian);
+
+ putPackedImage -> src_x = GetUINT(buffer + 24, bigEndian);
+ putPackedImage -> src_y = GetUINT(buffer + 26, bigEndian);
+ putPackedImage -> src_width = GetUINT(buffer + 28, bigEndian);
+ putPackedImage -> src_height = GetUINT(buffer + 30, bigEndian);
+
+ putPackedImage -> dst_x = GetUINT(buffer + 32, bigEndian);
+ putPackedImage -> dst_y = GetUINT(buffer + 34, bigEndian);
+ putPackedImage -> dst_width = GetUINT(buffer + 36, bigEndian);
+ putPackedImage -> dst_height = GetUINT(buffer + 38, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int PutPackedImageStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = putPackedImage -> client;
+
+ PutULONG(putPackedImage -> drawable, buffer + 4, bigEndian);
+ PutULONG(putPackedImage -> gcontext, buffer + 8, bigEndian);
+
+ *(buffer + 12) = putPackedImage -> method;
+
+ *(buffer + 13) = putPackedImage -> format;
+ *(buffer + 14) = putPackedImage -> src_depth;
+ *(buffer + 15) = putPackedImage -> dst_depth;
+
+ PutULONG(putPackedImage -> src_length, buffer + 16, bigEndian);
+ PutULONG(putPackedImage -> dst_length, buffer + 20, bigEndian);
+
+ PutUINT(putPackedImage -> src_x, buffer + 24, bigEndian);
+ PutUINT(putPackedImage -> src_y, buffer + 26, bigEndian);
+ PutUINT(putPackedImage -> src_width, buffer + 28, bigEndian);
+ PutUINT(putPackedImage -> src_height, buffer + 30, bigEndian);
+
+ PutUINT(putPackedImage -> dst_x, buffer + 32, bigEndian);
+ PutUINT(putPackedImage -> dst_y, buffer + 34, bigEndian);
+ PutUINT(putPackedImage -> dst_width, buffer + 36, bigEndian);
+ PutUINT(putPackedImage -> dst_height, buffer + 38, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void PutPackedImageStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message;
+
+ *logofs << name() << ": Identity format "
+
+ << "drawable " << putPackedImage -> drawable << ", "
+ << "gcontext " << putPackedImage -> gcontext << ", "
+
+ << "format " << (unsigned int) putPackedImage -> format << ", "
+ << "method " << (unsigned int) putPackedImage -> method << ", "
+
+ << "src_depth " << (unsigned int) putPackedImage -> src_depth << ", "
+ << "dst_depth " << (unsigned int) putPackedImage -> dst_depth << ", "
+
+ << "src_length " << putPackedImage -> src_length << ", "
+ << "dst_length " << putPackedImage -> dst_length << ", "
+
+ << "src_x " << putPackedImage -> src_x << ", "
+ << "src_y " << putPackedImage -> src_y << ", "
+ << "src_width " << putPackedImage -> src_width << ", "
+ << "src_height " << putPackedImage -> src_height << ", "
+
+ << "dst_x " << putPackedImage -> dst_x << ", "
+ << "dst_y " << putPackedImage -> dst_y << ", "
+ << "dst_width " << putPackedImage -> dst_width << ", "
+ << "dst_height " << putPackedImage -> dst_height << ", "
+
+ << "size " << putPackedImage -> size_ << ".\n"
+ << logofs_flush;
+
+ #endif
+}
+
+void PutPackedImageStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Fields method, format, src_depth, dst_depth,
+ // src_length, dst_length, src_x, src_y, src_width,
+ // src_height.
+ //
+ //
+ // TODO: We should better investigate the effect of
+ // having fields src_x and src_y in identity instead
+ // of keeping them in differences.
+ //
+
+ md5_append(md5_state_, buffer + 12, 20);
+}
+
+void PutPackedImageStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message;
+ PutPackedImageMessage *cachedPutPackedImage = (PutPackedImageMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value "
+ << (unsigned int) putPackedImage -> client
+ << " as client field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(putPackedImage -> client, 8,
+ clientCache -> resourceCache);
+
+ cachedPutPackedImage -> client = putPackedImage -> client;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(putPackedImage -> drawable, clientCache -> drawableCache);
+
+ cachedPutPackedImage -> drawable = putPackedImage -> drawable;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(putPackedImage -> gcontext, clientCache -> gcCache);
+
+ cachedPutPackedImage -> gcontext = putPackedImage -> gcontext;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> dst_x
+ << " as " << "dst_x" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_x = putPackedImage -> dst_x - cachedPutPackedImage -> dst_x;
+
+ encodeBuffer.encodeCachedValue(diff_x, 16,
+ clientCache -> putImageXCache, 8);
+
+ cachedPutPackedImage -> dst_x = putPackedImage -> dst_x;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> dst_y
+ << " as " << "dst_y" << " field.\n" << logofs_flush;
+ #endif
+
+ unsigned short int diff_y = putPackedImage -> dst_y - cachedPutPackedImage -> dst_y;
+
+ encodeBuffer.encodeCachedValue(diff_y, 16,
+ clientCache -> putImageYCache, 8);
+
+ cachedPutPackedImage -> dst_y = putPackedImage -> dst_y;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> dst_width
+ << " as " << "dst_width" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(putPackedImage -> dst_width, 16,
+ clientCache -> putImageWidthCache, 8);
+
+ cachedPutPackedImage -> dst_width = putPackedImage -> dst_width;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << putPackedImage -> dst_height
+ << " as " << "dst_height" << " field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(putPackedImage -> dst_height, 16,
+ clientCache -> putImageHeightCache, 8);
+
+ cachedPutPackedImage -> dst_height = putPackedImage -> dst_height;
+}
+
+void PutPackedImageStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ //
+ // Decode the variant part.
+ //
+
+ PutPackedImageMessage *putPackedImage = (PutPackedImageMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeCachedValue(putPackedImage -> client, 8,
+ clientCache -> resourceCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value "
+ << (unsigned int) putPackedImage -> client
+ << " as client field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> drawableCache);
+
+ putPackedImage -> drawable = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> drawable
+ << " as drawable field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ putPackedImage -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageXCache, 8);
+
+ putPackedImage -> dst_x += value;
+ putPackedImage -> dst_x &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> dst_x
+ << " as dst_x field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageYCache, 8);
+
+ putPackedImage -> dst_y += value;
+ putPackedImage -> dst_y &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> dst_y
+ << " as dst_y field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageWidthCache, 8);
+
+ putPackedImage -> dst_width = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> dst_width
+ << " as dst_width field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> putImageHeightCache, 8);
+
+ putPackedImage -> dst_height = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << putPackedImage -> dst_height
+ << " as dst_height field.\n" << logofs_flush;
+ #endif
+}
+
diff --git a/nxcomp/src/PutPackedImage.h b/nxcomp/src/PutPackedImage.h
new file mode 100644
index 000000000..d28ad39eb
--- /dev/null
+++ b/nxcomp/src/PutPackedImage.h
@@ -0,0 +1,218 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef PutPackedImage_H
+#define PutPackedImage_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define PUTPACKEDIMAGE_ENABLE_CACHE 1
+#define PUTPACKEDIMAGE_ENABLE_DATA 1
+#define PUTPACKEDIMAGE_ENABLE_COMPRESS 0
+
+//
+// We can't exceed lenght of 262144 bytes.
+//
+
+#define PUTPACKEDIMAGE_DATA_LIMIT 262144 - 40
+#define PUTPACKEDIMAGE_DATA_OFFSET 40
+
+#define PUTPACKEDIMAGE_CACHE_SLOTS 6000
+#define PUTPACKEDIMAGE_CACHE_THRESHOLD 70
+#define PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD 50
+
+#define PUTPACKEDIMAGE_CACHE_THRESHOLD_IF_PACKED_SHADOW 97
+#define PUTPACKEDIMAGE_CACHE_LOWER_THRESHOLD_IF_PACKED_SHADOW 90
+
+#define PUTPACKEDIMAGE_ENABLE_SPLIT_IF_PROTO_STEP_8 0
+
+//
+// The message class.
+//
+
+class PutPackedImageMessage : public Message
+{
+ friend class PutPackedImageStore;
+
+ public:
+
+ PutPackedImageMessage()
+ {
+ }
+
+ ~PutPackedImageMessage()
+ {
+ }
+
+ //
+ // Here are the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char client;
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned char format;
+ unsigned char method;
+
+ unsigned char src_depth;
+ unsigned char dst_depth;
+
+ unsigned int src_length;
+ unsigned int dst_length;
+
+ short int src_x;
+ short int src_y;
+ unsigned short src_width;
+ unsigned short src_height;
+
+ short int dst_x;
+ short int dst_y;
+ unsigned short dst_width;
+ unsigned short dst_height;
+};
+
+class PutPackedImageStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ PutPackedImageStore(StaticCompressor *compressor);
+
+ virtual ~PutPackedImageStore();
+
+ virtual const char *name() const
+ {
+ return "PutPackedImage";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXPutPackedImage;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(PutPackedImageMessage);
+ }
+
+ //
+ // Very special of this class.
+ //
+
+ int getPackedSize(const int position) const
+ {
+ PutPackedImageMessage *message = (PutPackedImageMessage *) (*messages_)[position];
+
+ if (message == NULL)
+ {
+ return 0;
+ }
+
+ return dataOffset + message -> src_length;
+ }
+
+ int getUnpackedSize(const int position) const
+ {
+ PutPackedImageMessage *message = (PutPackedImageMessage *) (*messages_)[position];
+
+ if (message == NULL)
+ {
+ return 0;
+ }
+
+ return dataOffset + message -> dst_length;
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new PutPackedImageMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new PutPackedImageMessage((const PutPackedImageMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (PutPackedImageMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* PutPackedImage_H */
diff --git a/nxcomp/src/QueryFontReply.cpp b/nxcomp/src/QueryFontReply.cpp
new file mode 100644
index 000000000..fde873140
--- /dev/null
+++ b/nxcomp/src/QueryFontReply.cpp
@@ -0,0 +1,154 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "QueryFontReply.h"
+
+#include "ServerCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+QueryFontReplyStore::QueryFontReplyStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = QUERYFONTREPLY_ENABLE_CACHE;
+ enableData = QUERYFONTREPLY_ENABLE_DATA;
+ enableSplit = QUERYFONTREPLY_ENABLE_SPLIT;
+
+ // Since ProtoStep7 (#issue 108)
+ enableCompress = QUERYFONTREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = QUERYFONTREPLY_DATA_LIMIT;
+ dataOffset = QUERYFONTREPLY_DATA_OFFSET;
+
+ cacheSlots = QUERYFONTREPLY_CACHE_SLOTS;
+ cacheThreshold = QUERYFONTREPLY_CACHE_THRESHOLD;
+ cacheLowerThreshold = QUERYFONTREPLY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+QueryFontReplyStore::~QueryFontReplyStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int QueryFontReplyStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Clear the padding bytes.
+ //
+
+ unsigned char *pad = (unsigned char *) buffer;
+
+ if (size >= 24)
+ {
+ PutULONG(0, pad + 20, bigEndian);
+ }
+
+ if (size >= 40)
+ {
+ PutULONG(0, pad + 36, bigEndian);
+ }
+
+ //
+ // TODO: This doesn't work. Probably these
+ // padding bytes are not padding anymore.
+ // This is to be investigated.
+ //
+ // pad += 60;
+ //
+ // while (pad + 16 <= (buffer + size))
+ // {
+ // PutULONG(0, pad + 12, bigEndian);
+ //
+ // pad += 16;
+ // }
+ //
+
+ #ifdef DEBUG
+ *logofs << name() << ": Cleaned padding bytes of "
+ << "message at " << message << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int QueryFontReplyStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ return 1;
+}
+
+void QueryFontReplyStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ QueryFontReplyMessage *queryFontReply = (QueryFontReplyMessage *) message;
+
+ *logofs << name() << ": Identity size " << queryFontReply -> size_ << ".\n";
+
+ #endif
+}
+
+void QueryFontReplyStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
diff --git a/nxcomp/src/QueryFontReply.h b/nxcomp/src/QueryFontReply.h
new file mode 100644
index 000000000..e12fe4e4b
--- /dev/null
+++ b/nxcomp/src/QueryFontReply.h
@@ -0,0 +1,136 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef QueryFontReply_H
+#define QueryFontReply_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+#define QUERYFONTREPLY_ENABLE_CACHE 1
+#define QUERYFONTREPLY_ENABLE_DATA 1
+#define QUERYFONTREPLY_ENABLE_SPLIT 0
+
+#define QUERYFONTREPLY_DATA_LIMIT 1048576 - 32
+#define QUERYFONTREPLY_DATA_OFFSET 8
+
+#define QUERYFONTREPLY_CACHE_SLOTS 200
+#define QUERYFONTREPLY_CACHE_THRESHOLD 20
+#define QUERYFONTREPLY_CACHE_LOWER_THRESHOLD 5
+
+#define QUERYFONTREPLY_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class QueryFontReplyMessage : public Message
+{
+ friend class QueryFontReplyStore;
+
+ public:
+
+ QueryFontReplyMessage()
+ {
+ }
+
+ ~QueryFontReplyMessage()
+ {
+ }
+};
+
+class QueryFontReplyStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ QueryFontReplyStore(StaticCompressor *compressor);
+
+ virtual ~QueryFontReplyStore();
+
+ virtual const char *name() const
+ {
+ return "QueryFontReply";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_QueryFont;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(QueryFontReplyMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new QueryFontReplyMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new QueryFontReplyMessage((const QueryFontReplyMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (QueryFontReplyMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* QueryFontReply_H */
diff --git a/nxcomp/src/ReadBuffer.cpp b/nxcomp/src/ReadBuffer.cpp
new file mode 100644
index 000000000..154225e75
--- /dev/null
+++ b/nxcomp/src/ReadBuffer.cpp
@@ -0,0 +1,639 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ReadBuffer.h"
+
+#include "Transport.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+ReadBuffer::ReadBuffer(Transport *transport)
+
+ : transport_(transport)
+{
+ //
+ // The read buffer will grow until
+ // reaching the maximum buffer size
+ // and then will remain stable at
+ // that size.
+ //
+
+ initialReadSize_ = READ_BUFFER_DEFAULT_SIZE;
+ maximumBufferSize_ = READ_BUFFER_DEFAULT_SIZE;
+
+ size_ = 0;
+ buffer_ = NULL;
+
+ owner_ = 1;
+ length_ = 0;
+ start_ = 0;
+
+ remaining_ = 0;
+}
+
+ReadBuffer::~ReadBuffer()
+{
+ if (owner_ == 1)
+ {
+ delete [] buffer_;
+ }
+}
+
+void ReadBuffer::readMessage(const unsigned char *message, unsigned int length)
+{
+ //
+ // To be here we must be the real owner
+ // of the buffer and there must not be
+ // pending bytes in the transport.
+ //
+
+ #ifdef TEST
+
+ if (owner_ == 0)
+ {
+ *logofs << "ReadBuffer: PANIC! Class for FD#"
+ << transport_ -> fd() << " doesn't "
+ << "appear to be the owner of the buffer "
+ << "while borrowing from the caller.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Be sure that any outstanding data from
+ // the transport is appended to our own
+ // byffer.
+ //
+
+ if (transport_ -> pending() != 0)
+ {
+ #ifdef WARNING
+ *logofs << "ReadBuffer: WARNING! Class for FD#"
+ << transport_ -> fd() << " has pending "
+ << "data in the transport while "
+ << "borrowing from the caller.\n"
+ << logofs_flush;
+ #endif
+
+ readMessage();
+
+ if (owner_ == 0)
+ {
+ convertBuffer();
+ }
+ }
+
+ //
+ // Can't borrow the buffer if there is data
+ // from a partial message. In this case add
+ // the new data to the end of our buffer.
+ //
+
+ if (length_ == 0)
+ {
+ #ifdef TEST
+ *logofs << "ReadBuffer: Borrowing " << length
+ << " bytes from the caller for FD#"
+ << transport_ -> fd() << " with "
+ << length_ << " bytes in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] buffer_;
+
+ buffer_ = (unsigned char *) message;
+ size_ = length;
+
+ length_ = length;
+
+ owner_ = 0;
+ start_ = 0;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "ReadBuffer: Appending " << length
+ << " bytes from the caller for FD#"
+ << transport_ -> fd() << " with "
+ << length_ << " bytes in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ appendBuffer(message, length);
+ }
+}
+
+int ReadBuffer::readMessage()
+{
+ int pendingLength = transport_ -> pending();
+
+ if (pendingLength > 0)
+ {
+ //
+ // Can't move the data in the borrowed buffer,
+ // so use the tansport buffer only if we don't
+ // have any partial message. This can happen
+ // with the proxy where we need to deflate the
+ // stream.
+ //
+
+ if (length_ == 0)
+ {
+ unsigned char *newBuffer;
+
+ length_ = transport_ -> getPending(newBuffer);
+
+ if (newBuffer == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "ReadBuffer: PANIC! Failed to borrow "
+ << length_ << " bytes of memory for buffer "
+ << "in context [A].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to borrow memory for "
+ << "read buffer in context [A].\n";
+
+ HandleCleanup();
+ }
+
+ delete [] buffer_;
+
+ buffer_ = newBuffer;
+ size_ = length_;
+
+ owner_ = 0;
+ start_ = 0;
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: Borrowed " << length_
+ << " pending bytes for FD#" << transport_ ->
+ fd() << ".\n" << logofs_flush;
+ #endif
+
+ return length_;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "ReadBuffer: WARNING! Cannot borrow "
+ << pendingLength << " bytes for FD#"
+ << transport_ -> fd() << " with "
+ << length_ << " bytes in the buffer.\n"
+ << logofs_flush;
+ }
+ #endif
+ }
+
+ unsigned int readLength = suggestedLength(pendingLength);
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Requested " << readLength
+ << " bytes for FD#" << transport_ -> fd()
+ << " with readable " << transport_ -> readable()
+ << " remaining " << remaining_ << " pending "
+ << transport_ -> pending() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (readLength < initialReadSize_)
+ {
+ readLength = initialReadSize_;
+ }
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Buffer size is " << size_
+ << " length " << length_ << " and start "
+ << start_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // We can't use the transport buffer
+ // to read our own data in it.
+ //
+
+ #ifdef TEST
+
+ if (owner_ == 0)
+ {
+ *logofs << "ReadBuffer: PANIC! Class for FD#"
+ << transport_ -> fd() << " doesn't "
+ << "appear to be the owner of the buffer "
+ << "while reading.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Be sure that we have enough space
+ // to store all the requested data.
+ //
+
+ if (buffer_ == NULL || length_ + readLength > size_)
+ {
+ unsigned int newSize = length_ + readLength;
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: Resizing buffer for FD#"
+ << transport_ -> fd() << " in read from "
+ << size_ << " to " << newSize << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned char *newBuffer = allocateBuffer(newSize);
+
+ memcpy(newBuffer, buffer_ + start_, length_);
+
+ delete [] buffer_;
+
+ buffer_ = newBuffer;
+ size_ = newSize;
+
+ transport_ -> pendingReset();
+
+ owner_ = 1;
+ }
+ else if (start_ != 0 && length_ != 0)
+ {
+ //
+ // If any bytes are left due to a partial
+ // message, shift them to the beginning
+ // of the buffer.
+ //
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: Moving " << length_
+ << " bytes of data " << "at beginning of "
+ << "the buffer for FD#" << transport_ -> fd()
+ << ".\n" << logofs_flush;
+ #endif
+
+ memmove(buffer_, buffer_ + start_, length_);
+ }
+
+ start_ = 0;
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Buffer size is now " << size_
+ << " length is " << length_ << " and start is "
+ << start_ << ".\n" << logofs_flush;
+ #endif
+
+ unsigned char *readData = buffer_ + length_;
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Going to read " << readLength
+ << " bytes from FD#" << transport_ -> fd() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int bytesRead = transport_ -> read(readData, readLength);
+
+ if (bytesRead > 0)
+ {
+ #ifdef TEST
+ *logofs << "ReadBuffer: Read " << bytesRead
+ << " bytes from FD#" << transport_ -> fd()
+ << ".\n" << logofs_flush;
+ #endif
+
+ length_ += bytesRead;
+ }
+ else if (bytesRead < 0)
+ {
+ //
+ // Check if there is more data pending than the
+ // size of the provided buffer. After reading
+ // the requested amount, in fact, the transport
+ // may have decompressed the data and produced
+ // more input. This trick allows us to always
+ // borrow the buffer from the transport, even
+ // when the partial read would have prevented
+ // that.
+ //
+
+ if (transport_ -> pending() > 0)
+ {
+ #ifdef TEST
+ *logofs << "ReadBuffer: WARNING! Trying to read some "
+ << "more with " << transport_ -> pending()
+ << " bytes pending for FD#" << transport_ ->
+ fd() << ".\n" << logofs_flush;
+ #endif
+
+ return readMessage();
+ }
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: Error detected reading "
+ << "from FD#" << transport_ -> fd()
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "ReadBuffer: No data read from FD#"
+ << transport_ -> fd() << " with remaining "
+ << remaining_ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return bytesRead;
+}
+
+const unsigned char *ReadBuffer::getMessage(unsigned int &controlLength,
+ unsigned int &dataLength)
+{
+ #ifdef TEST
+
+ if (transport_ -> pending() > 0)
+ {
+ *logofs << "ReadBuffer: PANIC! The transport "
+ << "appears to have data pending.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ if (length_ == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: No message can be located "
+ << "for FD#" << transport_ -> fd() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (owner_ == 0)
+ {
+ buffer_ = NULL;
+ size_ = 0;
+
+ transport_ -> pendingReset();
+
+ owner_ = 1;
+ start_ = 0;
+ }
+
+ return NULL;
+ }
+
+ unsigned int trailerLength;
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Going to locate message with "
+ << "start at " << start_ << " and length "
+ << length_ << " for FD#" << transport_ -> fd()
+ << ".\n" << logofs_flush;
+ #endif
+
+ int located = locateMessage(buffer_ + start_, buffer_ + start_ + length_,
+ controlLength, dataLength, trailerLength);
+
+ if (located == 0)
+ {
+ //
+ // No more complete messages are in
+ // the buffer.
+ //
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: No message was located "
+ << "for FD#" << transport_ -> fd()
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (owner_ == 0)
+ {
+ //
+ // Must move the remaining bytes in
+ // our own buffer.
+ //
+
+ convertBuffer();
+ }
+
+ return NULL;
+ }
+ else
+ {
+ const unsigned char *result = buffer_ + start_;
+
+ if (dataLength > 0)
+ {
+ //
+ // Message contains data, so go to the
+ // first byte of payload.
+ //
+
+ result += trailerLength;
+
+ start_ += (dataLength + trailerLength);
+ length_ -= (dataLength + trailerLength);
+ }
+ else
+ {
+ //
+ // It is a control message.
+ //
+
+ start_ += (controlLength + trailerLength);
+ length_ -= (controlLength + trailerLength);
+ }
+
+ #ifdef DEBUG
+ *logofs << "ReadBuffer: Located message for FD#"
+ << transport_ -> fd() << " with control length "
+ << controlLength << " and data length "
+ << dataLength << ".\n" << logofs_flush;
+ #endif
+
+ remaining_ = 0;
+
+ return result;
+ }
+}
+
+int ReadBuffer::setSize(int initialReadSize, int maximumBufferSize)
+{
+ initialReadSize_ = initialReadSize;
+ maximumBufferSize_ = maximumBufferSize;
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: WARNING! Set buffer parameters to "
+ << initialReadSize_ << "/" << maximumBufferSize_
+ << " for object at "<< this << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ReadBuffer::fullReset()
+{
+ #ifdef TEST
+
+ if (owner_ == 0)
+ {
+ *logofs << "ReadBuffer: PANIC! Class for FD#"
+ << transport_ -> fd() << " doesn't "
+ << "appear to be the owner of the buffer "
+ << "in reset.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ if (length_ == 0 && size_ > maximumBufferSize_)
+ {
+ #ifdef TEST
+ *logofs << "ReadBuffer: Resizing buffer for FD#"
+ << transport_ -> fd() << " in reset from "
+ << size_ << " to " << maximumBufferSize_
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ delete [] buffer_;
+
+ int newSize = maximumBufferSize_;
+
+ unsigned char *newBuffer = allocateBuffer(newSize);
+
+ buffer_ = newBuffer;
+ size_ = newSize;
+
+ transport_ -> pendingReset();
+
+ owner_ = 1;
+ start_ = 0;
+ }
+}
+
+unsigned char *ReadBuffer::allocateBuffer(unsigned int newSize)
+{
+ unsigned char *newBuffer = new unsigned char[newSize];
+
+ if (newBuffer == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "ReadBuffer: PANIC! Can't allocate "
+ << newSize << " bytes of memory for buffer "
+ << "in context [B].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "read buffer in context [B].\n";
+
+ HandleCleanup();
+ }
+
+ #ifdef VALGRIND
+
+ memset(newBuffer, '\0', newSize);
+
+ #endif
+
+ return newBuffer;
+}
+
+void ReadBuffer::appendBuffer(const unsigned char *message, unsigned int length)
+{
+ if (start_ + length_ + length > size_)
+ {
+ unsigned int newSize = length_ + length + initialReadSize_;
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: WARNING! Resizing buffer "
+ << "for FD#" << transport_ -> fd()
+ << " from " << size_ << " to " << newSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ unsigned char *newBuffer = allocateBuffer(newSize);
+
+ memcpy(newBuffer, buffer_ + start_, length_);
+
+ delete [] buffer_;
+
+ buffer_ = newBuffer;
+ size_ = newSize;
+
+ start_ = 0;
+ }
+
+ memcpy(buffer_ + start_ + length_, message, length);
+
+ length_ += length;
+
+ transport_ -> pendingReset();
+
+ owner_ = 1;
+}
+
+void ReadBuffer::convertBuffer()
+{
+ unsigned int newSize = length_ + initialReadSize_;
+
+ #ifdef TEST
+ *logofs << "ReadBuffer: WARNING! Converting "
+ << length_ << " bytes to own buffer "
+ << "for FD#" << transport_ -> fd()
+ << " with new size " << newSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ unsigned char *newBuffer = allocateBuffer(newSize);
+
+ memcpy(newBuffer, buffer_ + start_, length_);
+
+ buffer_ = newBuffer;
+ size_ = newSize;
+
+ transport_ -> pendingReset();
+
+ owner_ = 1;
+ start_ = 0;
+}
diff --git a/nxcomp/src/ReadBuffer.h b/nxcomp/src/ReadBuffer.h
new file mode 100644
index 000000000..a12bcc9b3
--- /dev/null
+++ b/nxcomp/src/ReadBuffer.h
@@ -0,0 +1,128 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ReadBuffer_H
+#define ReadBuffer_H
+
+#include "Misc.h"
+#include "Timestamp.h"
+#include "Transport.h"
+
+#define READ_BUFFER_DEFAULT_SIZE 8192
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+class ReadBuffer
+{
+ public:
+
+ ReadBuffer(Transport *transport);
+
+ virtual ~ReadBuffer();
+
+ int readMessage();
+
+ void readMessage(const unsigned char *message, unsigned int length);
+
+ const unsigned char *getMessage(unsigned int &dataLength)
+ {
+ unsigned int controlLength;
+
+ return getMessage(controlLength, dataLength);
+ }
+
+ const unsigned char *getMessage(unsigned int &controlLength, unsigned int &dataLength);
+
+ unsigned int getLength() const
+ {
+ return length_;
+ }
+
+ unsigned int getRemaining() const
+ {
+ return remaining_;
+ }
+
+ int setSize(int initialReadSize, int initialbufferSize);
+
+ void fullReset();
+
+ //
+ // Check if there is a complete
+ // message in the buffer.
+ //
+
+ int checkMessage()
+ {
+ if (length_ == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ unsigned int controlLength;
+ unsigned int dataLength;
+ unsigned int trailerLength;
+
+ return (locateMessage(buffer_ + start_, buffer_ + start_ + length_,
+ controlLength, dataLength, trailerLength));
+ }
+ }
+
+ protected:
+
+ virtual unsigned int suggestedLength(unsigned int pendingLength) = 0;
+
+ virtual int locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength) = 0;
+
+ unsigned char *allocateBuffer(unsigned int newSize);
+
+ void appendBuffer(const unsigned char *message, unsigned int length);
+
+ void convertBuffer();
+
+ Transport *transport_;
+
+ unsigned char *buffer_;
+
+ unsigned int length_;
+ unsigned int size_;
+ unsigned int start_;
+ unsigned int remaining_;
+
+ int owner_;
+
+ unsigned int initialReadSize_;
+ unsigned int maximumBufferSize_;
+};
+
+#endif /* ReadBuffer_H */
diff --git a/nxcomp/src/RenderAddGlyphs.cpp b/nxcomp/src/RenderAddGlyphs.cpp
new file mode 100644
index 000000000..ef13d6890
--- /dev/null
+++ b/nxcomp/src/RenderAddGlyphs.cpp
@@ -0,0 +1,233 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "RenderAddGlyphs.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 4, bigEndian), 29,
+ clientCache -> renderGlyphSetCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 32,
+ clientCache -> renderNumGlyphsCache, 8);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache -> renderGlyphSetCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderNumGlyphsCache, 8);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.add_glyphs.type = *(buffer + 1);
+
+ renderExtension -> data.add_glyphs.set_id = GetULONG(buffer + 4, bigEndian);
+ renderExtension -> data.add_glyphs.num_elm = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.add_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.add_glyphs.type;
+
+ PutULONG(renderExtension -> data.add_glyphs.set_id, buffer + 4, bigEndian);
+ PutULONG(renderExtension -> data.add_glyphs.num_elm, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.add_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+ md5_append(md5_state, buffer + 8, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(renderExtension -> data.add_glyphs.set_id, 29,
+ clientCache -> renderGlyphSetCache);
+
+ cachedRenderExtension -> data.add_glyphs.set_id =
+ renderExtension -> data.add_glyphs.set_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.add_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(renderExtension -> data.add_glyphs.set_id, 29,
+ clientCache -> renderGlyphSetCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.add_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderAddGlyphs.h b/nxcomp/src/RenderAddGlyphs.h
new file mode 100644
index 000000000..d3c8cd158
--- /dev/null
+++ b/nxcomp/src/RenderAddGlyphs.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderAddGlyphs_H
+#define RenderAddGlyphs_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderAddGlyphs"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderAddGlyphsStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderAddGlyphs_H */
diff --git a/nxcomp/src/RenderChangePicture.cpp b/nxcomp/src/RenderChangePicture.cpp
new file mode 100644
index 000000000..d202b92ad
--- /dev/null
+++ b/nxcomp/src/RenderChangePicture.cpp
@@ -0,0 +1,238 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "RenderChangePicture.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ #ifdef DEBUG
+
+ if (size == MESSAGE_OFFSET + 4)
+ {
+ *logofs << name() << ": Mask is " << GetULONG(buffer + 8, bigEndian)
+ << " value is " << GetULONG(buffer + 12, bigEndian)
+ << ".\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << name() << ": WARNING! Unexpected size. Mask is "
+ << GetULONG(buffer + 8, bigEndian) << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.change_picture.type = *(buffer + 1);
+
+ renderExtension -> data.change_picture.src_id = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.change_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.change_picture.type;
+
+ PutULONG(renderExtension -> data.change_picture.src_id, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.change_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.change_picture.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.change_picture.src_id =
+ renderExtension -> data.change_picture.src_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.change_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.change_picture.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.change_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderChangePicture.h b/nxcomp/src/RenderChangePicture.h
new file mode 100644
index 000000000..4bab1ef98
--- /dev/null
+++ b/nxcomp/src/RenderChangePicture.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderChangePicture_H
+#define RenderChangePicture_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderChangePicture"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderChangePictureStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 8
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderChangePicture_H */
diff --git a/nxcomp/src/RenderComposite.cpp b/nxcomp/src/RenderComposite.cpp
new file mode 100644
index 000000000..f82f80f06
--- /dev/null
+++ b/nxcomp/src/RenderComposite.cpp
@@ -0,0 +1,400 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+//
+// Include the template for
+// this message class.
+//
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "RenderComposite.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian),
+ clientCache -> renderMaskPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 16, bigEndian),
+ clientCache -> renderDstPictureCache);
+
+ //
+ // Src X and Y.
+ //
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+ //
+ // Mask X and Y.
+ //
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 24, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 26, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ //
+ // Dst X and Y.
+ //
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 28, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 30, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ //
+ // Width and height.
+ //
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 32, bigEndian), 16,
+ clientCache -> renderWidthCache, 11);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 34, bigEndian), 16,
+ clientCache -> renderHeightCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderMaskPictureCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderDstPictureCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ //
+ // Src X and Y.
+ //
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian);
+
+ //
+ // Mask X and Y.
+ //
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 24, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 26, bigEndian);
+
+ //
+ // Dst X and Y.
+ //
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 28, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 30, bigEndian);
+
+ //
+ // Width and height.
+ //
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> renderWidthCache, 11);
+
+ PutUINT(value, buffer + 32, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> renderHeightCache, 11);
+
+ PutUINT(value, buffer + 34, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.composite.type = *(buffer + 1);
+ renderExtension -> data.composite.op = *(buffer + 4);
+
+ renderExtension -> data.composite.src_id = GetULONG(buffer + 8, bigEndian);
+ renderExtension -> data.composite.msk_id = GetULONG(buffer + 12, bigEndian);
+ renderExtension -> data.composite.dst_id = GetULONG(buffer + 16, bigEndian);
+
+ renderExtension -> data.composite.src_x = GetUINT(buffer + 20, bigEndian);
+ renderExtension -> data.composite.src_y = GetUINT(buffer + 22, bigEndian);
+
+ renderExtension -> data.composite.msk_x = GetUINT(buffer + 24, bigEndian);
+ renderExtension -> data.composite.msk_y = GetUINT(buffer + 26, bigEndian);
+
+ renderExtension -> data.composite.dst_x = GetUINT(buffer + 28, bigEndian);
+ renderExtension -> data.composite.dst_y = GetUINT(buffer + 30, bigEndian);
+
+ renderExtension -> data.composite.width = GetUINT(buffer + 32, bigEndian);
+ renderExtension -> data.composite.height = GetUINT(buffer + 34, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.composite.type;
+ *(buffer + 4) = renderExtension -> data.composite.op;
+
+ PutULONG(renderExtension -> data.composite.src_id, buffer + 8, bigEndian);
+ PutULONG(renderExtension -> data.composite.msk_id, buffer + 12, bigEndian);
+ PutULONG(renderExtension -> data.composite.dst_id, buffer + 16, bigEndian);
+
+ PutUINT(renderExtension -> data.composite.src_x, buffer + 20, bigEndian);
+ PutUINT(renderExtension -> data.composite.src_y, buffer + 22, bigEndian);
+
+ PutUINT(renderExtension -> data.composite.msk_x, buffer + 24, bigEndian);
+ PutUINT(renderExtension -> data.composite.msk_y, buffer + 26, bigEndian);
+
+ PutUINT(renderExtension -> data.composite.dst_x, buffer + 28, bigEndian);
+ PutUINT(renderExtension -> data.composite.dst_y, buffer + 30, bigEndian);
+
+ PutUINT(renderExtension -> data.composite.width, buffer + 32, bigEndian);
+ PutUINT(renderExtension -> data.composite.height, buffer + 34, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include the minor opcode and size in the
+ // identity, plus the operator, the x and y
+ // of the source and mask and the width and
+ // height of the destination.
+ //
+
+ md5_append(md5_state, buffer + 1, 4);
+ md5_append(md5_state, buffer + 20, 8);
+ md5_append(md5_state, buffer + 32, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Source " << renderExtension -> data.composite.src_id
+ << " mask " << renderExtension -> data.composite.msk_id
+ << " destination " << renderExtension -> data.composite.msk_id
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.composite.src_id =
+ renderExtension -> data.composite.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite.msk_id,
+ clientCache -> renderMaskPictureCache);
+
+ cachedRenderExtension -> data.composite.msk_id =
+ renderExtension -> data.composite.msk_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ cachedRenderExtension -> data.composite.dst_id =
+ renderExtension -> data.composite.dst_id;
+
+ //
+ // Dst X and Y.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ value = renderExtension -> data.composite.dst_x;
+ previous = cachedRenderExtension -> data.composite.dst_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ cachedRenderExtension -> data.composite.dst_x = value;
+
+ value = renderExtension -> data.composite.dst_y;
+ previous = cachedRenderExtension -> data.composite.dst_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ cachedRenderExtension -> data.composite.dst_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite.msk_id,
+ clientCache -> renderMaskPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ //
+ // Dst X and Y.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ previous = renderExtension -> data.composite.dst_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ renderExtension -> data.composite.dst_x = value;
+
+ previous = renderExtension -> data.composite.dst_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ renderExtension -> data.composite.dst_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderComposite.h b/nxcomp/src/RenderComposite.h
new file mode 100644
index 000000000..aafa1e776
--- /dev/null
+++ b/nxcomp/src/RenderComposite.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderComposite_H
+#define RenderComposite_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderComposite"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCompositeStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 36
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 0
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 0
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderComposite_H */
diff --git a/nxcomp/src/RenderCompositeGlyphs.cpp b/nxcomp/src/RenderCompositeGlyphs.cpp
new file mode 100644
index 000000000..0949d3e2c
--- /dev/null
+++ b/nxcomp/src/RenderCompositeGlyphs.cpp
@@ -0,0 +1,629 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderCompositeGlyphs.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value "
+ << ((size - MESSAGE_OFFSET) >> 2) << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian),
+ clientCache -> renderDstPictureCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 20, bigEndian), 29,
+ clientCache -> renderGlyphSetCache);
+
+ unsigned int src_x = GetUINT(buffer + 24, bigEndian);
+ unsigned int src_y = GetUINT(buffer + 26, bigEndian);
+
+ // Since ProtoStep8 (#issue 108)
+ encodeBuffer.encodeDiffCachedValue(src_x,
+ clientCache -> renderGlyphX, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(src_y,
+ clientCache -> renderGlyphY, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded source X "
+ << GetUINT(buffer + 24, bigEndian) << " source Y "
+ << GetUINT(buffer + 26, bigEndian) << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Bytes from 28 to 36 contain in the order:
+ //
+ // 1 byte for the length of the first string.
+ // 3 bytes of padding.
+ // 2 bytes for the X offset.
+ // 2 bytes for the Y offset.
+ //
+ // Encode these bytes differentially to match
+ // all the strings that have equal glyphs.
+ //
+ // Only manage the first string of glyphs. The
+ // others strings should match, if they contain
+ // the same glyphs, since the offset are rela-
+ // tive to the first offset coordinates.
+ //
+
+ // Since ProtoStep8 (#issue 108)
+ if (size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ unsigned int numGlyphs = *(buffer + 28);
+
+ encodeBuffer.encodeCachedValue(numGlyphs, 8,
+ clientCache -> renderNumGlyphsCache);
+
+ unsigned int offset_x = GetUINT(buffer + 32, bigEndian);
+ unsigned int offset_y = GetUINT(buffer + 34, bigEndian);
+
+ if (offset_x == src_x && offset_y == src_y)
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ #ifdef TEST
+ *logofs << name() << ": Matched offset X "
+ << GetUINT(buffer + 32, bigEndian) << " offset Y "
+ << GetUINT(buffer + 34, bigEndian) << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeDiffCachedValue(offset_x,
+ clientCache -> renderGlyphX, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(offset_y,
+ clientCache -> renderGlyphY, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Missed offset X "
+ << GetUINT(buffer + 32, bigEndian) << " offset Y "
+ << GetUINT(buffer + 34, bigEndian) << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderDstPictureCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache -> renderGlyphSetCache);
+
+ PutULONG(value, buffer + 20, bigEndian);
+
+ unsigned int src_x;
+ unsigned int src_y;
+
+ // Since ProtoStep8 (#issue 108)
+ decodeBuffer.decodeDiffCachedValue(src_x,
+ clientCache -> renderGlyphX, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ decodeBuffer.decodeDiffCachedValue(src_y,
+ clientCache -> renderGlyphY, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ PutUINT(src_x, buffer + 24, bigEndian);
+ PutUINT(src_y, buffer + 26, bigEndian);
+
+ // Since ProtoStep8 (#issue 108)
+ if (size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ decodeBuffer.decodeCachedValue(value, 8,
+ clientCache -> renderNumGlyphsCache);
+
+ *(buffer + 28) = value;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value == 0)
+ {
+ PutUINT(src_x, buffer + 32, bigEndian);
+ PutUINT(src_y, buffer + 34, bigEndian);
+ }
+ else
+ {
+ decodeBuffer.decodeDiffCachedValue(src_x,
+ clientCache -> renderGlyphX, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ PutUINT(src_x, buffer + 32, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(src_y,
+ clientCache -> renderGlyphY, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ PutUINT(src_y, buffer + 34, bigEndian);
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ // Since ProtoStep8 (#issue 108)
+ if (size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET_IF_PROTO_STEP_8,
+ size, bigEndian, channelCache);
+ }
+ else if (size > MESSAGE_OFFSET)
+ {
+ encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of text data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ // Since ProtoStep8 (#issue 108)
+ if (size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET_IF_PROTO_STEP_8,
+ size, bigEndian, channelCache);
+ }
+ else if (size > MESSAGE_OFFSET)
+ {
+ decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.composite_glyphs.type = *(buffer + 1);
+ renderExtension -> data.composite_glyphs.op = *(buffer + 4);
+
+ renderExtension -> data.composite_glyphs.src_id = GetULONG(buffer + 8, bigEndian);
+ renderExtension -> data.composite_glyphs.dst_id = GetULONG(buffer + 12, bigEndian);
+
+ renderExtension -> data.composite_glyphs.format = GetULONG(buffer + 16, bigEndian);
+ renderExtension -> data.composite_glyphs.set_id = GetULONG(buffer + 20, bigEndian);
+
+ renderExtension -> data.composite_glyphs.src_x = GetUINT(buffer + 24, bigEndian);
+ renderExtension -> data.composite_glyphs.src_y = GetUINT(buffer + 26, bigEndian);
+
+ // Since ProtoStep8 (#issue 108)
+ if (size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ renderExtension -> data.composite_glyphs.num_elm = *(buffer + 28);
+
+ renderExtension -> data.composite_glyphs.offset_x = GetUINT(buffer + 32, bigEndian);
+ renderExtension -> data.composite_glyphs.offset_y = GetUINT(buffer + 34, bigEndian);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.composite_glyphs.type;
+ *(buffer + 4) = renderExtension -> data.composite_glyphs.op;
+
+ PutULONG(renderExtension -> data.composite_glyphs.src_id, buffer + 8, bigEndian);
+ PutULONG(renderExtension -> data.composite_glyphs.dst_id, buffer + 12, bigEndian);
+
+ PutULONG(renderExtension -> data.composite_glyphs.format, buffer + 16, bigEndian);
+ PutULONG(renderExtension -> data.composite_glyphs.set_id, buffer + 20, bigEndian);
+
+ PutUINT(renderExtension -> data.composite_glyphs.src_x, buffer + 24, bigEndian);
+ PutUINT(renderExtension -> data.composite_glyphs.src_y, buffer + 26, bigEndian);
+
+ // Since ProtoStep8 (#issue 108)
+ if (size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ *(buffer + 28) = renderExtension -> data.composite_glyphs.num_elm;
+
+ PutUINT(renderExtension -> data.composite_glyphs.offset_x, buffer + 32, bigEndian);
+ PutUINT(renderExtension -> data.composite_glyphs.offset_y, buffer + 34, bigEndian);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include minor opcode, size and
+ // the composite operator in the
+ // identity.
+ //
+
+ md5_append(md5_state, buffer + 1, 4);
+
+ //
+ // Include the format.
+ //
+
+ md5_append(md5_state, buffer + 16, 4);
+
+ //
+ // Also include the length of the
+ // first string.
+ //
+
+ // Since ProtoStep8 (#issue 108)
+ if (size >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ md5_append(md5_state, buffer + 28, 1);
+ }
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.composite_glyphs.src_id =
+ renderExtension -> data.composite_glyphs.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.composite_glyphs.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ cachedRenderExtension -> data.composite_glyphs.dst_id =
+ renderExtension -> data.composite_glyphs.dst_id;
+
+ encodeBuffer.encodeCachedValue(renderExtension -> data.composite_glyphs.set_id, 29,
+ clientCache -> renderGlyphSetCache);
+
+ cachedRenderExtension -> data.composite_glyphs.set_id =
+ renderExtension -> data.composite_glyphs.set_id;
+
+ //
+ // Src X and Y.
+ //
+ // The source X and Y coordinates are
+ // encoded as differerences in respect
+ // to the cached message.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ // Since ProtoStep8 (#issue 108)
+ value = renderExtension -> data.composite_glyphs.src_x;
+ previous = cachedRenderExtension -> data.composite_glyphs.src_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs.src_x = value;
+
+ value = renderExtension -> data.composite_glyphs.src_y;
+ previous = cachedRenderExtension -> data.composite_glyphs.src_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded source X "
+ << renderExtension -> data.composite_glyphs.src_x << " source Y "
+ << renderExtension -> data.composite_glyphs.src_y << ".\n"
+ << logofs_flush;
+ #endif
+
+ // Since ProtoStep8 (#issue 108)
+ if (renderExtension -> size_ >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ //
+ // Offset X and Y.
+ //
+
+ if (renderExtension -> data.composite_glyphs.offset_x ==
+ renderExtension -> data.composite_glyphs.src_x &&
+ renderExtension -> data.composite_glyphs.offset_y ==
+ renderExtension -> data.composite_glyphs.src_y)
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ cachedRenderExtension -> data.composite_glyphs.offset_x =
+ renderExtension -> data.composite_glyphs.offset_x;
+
+ cachedRenderExtension -> data.composite_glyphs.offset_y =
+ renderExtension -> data.composite_glyphs.offset_y;
+
+ #ifdef TEST
+ *logofs << name() << ": Matched offset X "
+ << renderExtension -> data.composite_glyphs.offset_x << " offset Y "
+ << renderExtension -> data.composite_glyphs.offset_y << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ value = renderExtension -> data.composite_glyphs.offset_x;
+ previous = cachedRenderExtension -> data.composite_glyphs.offset_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs.offset_x = value;
+
+ value = renderExtension -> data.composite_glyphs.offset_y;
+ previous = cachedRenderExtension -> data.composite_glyphs.offset_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ cachedRenderExtension -> data.composite_glyphs.offset_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Missed offset X "
+ << renderExtension -> data.composite_glyphs.offset_x << " offset Y "
+ << renderExtension -> data.composite_glyphs.offset_y << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.composite_glyphs.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ decodeBuffer.decodeCachedValue(renderExtension -> data.composite_glyphs.set_id, 29,
+ clientCache -> renderGlyphSetCache);
+
+ //
+ // Src X and Y.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ // Since ProtoStep8 (#issue 108)
+ previous = renderExtension -> data.composite_glyphs.src_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ renderExtension -> data.composite_glyphs.src_x = value;
+
+ previous = renderExtension -> data.composite_glyphs.src_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ renderExtension -> data.composite_glyphs.src_y = value;
+
+ // Since ProtoStep8 (#issue 108)
+ if (renderExtension -> size_ >= MESSAGE_OFFSET_IF_PROTO_STEP_8)
+ {
+ //
+ // Offset X and Y.
+ //
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value == 0)
+ {
+ renderExtension -> data.composite_glyphs.offset_x =
+ renderExtension -> data.composite_glyphs.src_x;
+
+ renderExtension -> data.composite_glyphs.offset_y =
+ renderExtension -> data.composite_glyphs.src_y;
+ }
+ else
+ {
+ previous = renderExtension -> data.composite_glyphs.offset_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphXCache, 11);
+
+ renderExtension -> data.composite_glyphs.offset_x = value;
+
+ previous = renderExtension -> data.composite_glyphs.offset_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderGlyphYCache, 11);
+
+ renderExtension -> data.composite_glyphs.offset_y = value;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.composite_glyphs.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderCompositeGlyphs.h b/nxcomp/src/RenderCompositeGlyphs.h
new file mode 100644
index 000000000..1062ee781
--- /dev/null
+++ b/nxcomp/src/RenderCompositeGlyphs.h
@@ -0,0 +1,100 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderCompositeGlyphs_H
+#define RenderCompositeGlyphs_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderCompositeGlyphs"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCompositeGlyphsStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 28
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Encode the first 8 bytes of the
+// data differentially in newer
+// protocol versions.
+//
+
+#undef MESSAGE_OFFSET_IF_PROTO_STEP_8
+#define MESSAGE_OFFSET_IF_PROTO_STEP_8 36
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ // Since ProtoStep8 (#issue 108)
+ unsigned int offset = MESSAGE_OFFSET_IF_PROTO_STEP_8;
+
+ return (size >= offset ? offset : size);
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderCompositeGlyphs_H */
diff --git a/nxcomp/src/RenderCreateGlyphSet.cpp b/nxcomp/src/RenderCreateGlyphSet.cpp
new file mode 100644
index 000000000..e3a842de9
--- /dev/null
+++ b/nxcomp/src/RenderCreateGlyphSet.cpp
@@ -0,0 +1,185 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderCreateGlyphSet.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderGlyphSetCache,
+ clientCache -> renderFreeGlyphSetCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 8, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeNewXidValue(value,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderGlyphSetCache,
+ clientCache -> renderFreeGlyphSetCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.create_set.type = *(buffer + 1);
+
+ renderExtension -> data.create_set.set_id = GetULONG(buffer + 4, bigEndian);
+ renderExtension -> data.create_set.format = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.create_set.type;
+
+ PutULONG(renderExtension -> data.create_set.set_id, buffer + 4, bigEndian);
+ PutULONG(renderExtension -> data.create_set.format, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+ md5_append(md5_state, buffer + 8, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeNewXidValue(renderExtension -> data.create_set.set_id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderGlyphSetCache,
+ clientCache -> renderFreeGlyphSetCache);
+
+ cachedRenderExtension -> data.create_set.set_id =
+ renderExtension -> data.create_set.set_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeNewXidValue(renderExtension -> data.create_set.set_id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderGlyphSetCache,
+ clientCache -> renderFreeGlyphSetCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderCreateGlyphSet.h b/nxcomp/src/RenderCreateGlyphSet.h
new file mode 100644
index 000000000..10f5d6699
--- /dev/null
+++ b/nxcomp/src/RenderCreateGlyphSet.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderCreateGlyphSet_H
+#define RenderCreateGlyphSet_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderCreateGlyphSet"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCreateGlyphSetStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 0
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 0
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderCreateGlyphSet_H */
diff --git a/nxcomp/src/RenderCreatePicture.cpp b/nxcomp/src/RenderCreatePicture.cpp
new file mode 100644
index 000000000..f3f9f8b8a
--- /dev/null
+++ b/nxcomp/src/RenderCreatePicture.cpp
@@ -0,0 +1,278 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderCreatePicture.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeNewXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderSrcPictureCache,
+ clientCache -> renderFreePictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> drawableCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 12, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32,
+ clientCache -> renderValueMaskCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeNewXidValue(value,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderSrcPictureCache,
+ clientCache -> renderFreePictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> drawableCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderValueMaskCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.create_picture.type = *(buffer + 1);
+
+ renderExtension -> data.create_picture.src_id = GetULONG(buffer + 4, bigEndian);
+ renderExtension -> data.create_picture.dst_id = GetULONG(buffer + 8, bigEndian);
+
+ renderExtension -> data.create_picture.format = GetULONG(buffer + 12, bigEndian);
+ renderExtension -> data.create_picture.mask = GetULONG(buffer + 16, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.create_picture.type;
+
+ PutULONG(renderExtension -> data.create_picture.src_id, buffer + 4, bigEndian);
+ PutULONG(renderExtension -> data.create_picture.dst_id, buffer + 8, bigEndian);
+
+ PutULONG(renderExtension -> data.create_picture.format, buffer + 12, bigEndian);
+ PutULONG(renderExtension -> data.create_picture.mask, buffer + 16, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+ md5_append(md5_state, buffer + 12, 8);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding new id value "
+ << renderExtension -> data.create_picture.src_id
+ << ".\n";
+ #endif
+
+ encodeBuffer.encodeNewXidValue(renderExtension -> data.create_picture.src_id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderSrcPictureCache,
+ clientCache -> renderFreePictureCache);
+
+ cachedRenderExtension -> data.create_picture.src_id =
+ renderExtension -> data.create_picture.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.create_picture.dst_id,
+ clientCache -> drawableCache);
+
+ cachedRenderExtension -> data.create_picture.dst_id =
+ renderExtension -> data.create_picture.dst_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeNewXidValue(renderExtension -> data.create_picture.src_id,
+ clientCache -> lastId, clientCache -> lastIdCache,
+ clientCache -> renderSrcPictureCache,
+ clientCache -> renderFreePictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.create_picture.dst_id,
+ clientCache -> drawableCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.create_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderCreatePicture.h b/nxcomp/src/RenderCreatePicture.h
new file mode 100644
index 000000000..ae2f583a0
--- /dev/null
+++ b/nxcomp/src/RenderCreatePicture.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderCreatePicture_H
+#define RenderCreatePicture_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderCreatePicture"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderCreatePictureStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 20
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderCreatePicture_H */
diff --git a/nxcomp/src/RenderExtension.cpp b/nxcomp/src/RenderExtension.cpp
new file mode 100644
index 000000000..64761bc05
--- /dev/null
+++ b/nxcomp/src/RenderExtension.cpp
@@ -0,0 +1,427 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "NXrender.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+#include "RenderExtension.h"
+
+#include "RenderGenericRequest.h"
+#include "RenderCreatePicture.h"
+#include "RenderChangePicture.h"
+#include "RenderFreePicture.h"
+#include "RenderPictureClip.h"
+#include "RenderPictureTransform.h"
+#include "RenderPictureFilter.h"
+#include "RenderCreateGlyphSet.h"
+#include "RenderFreeGlyphSet.h"
+#include "RenderAddGlyphs.h"
+#include "RenderComposite.h"
+#include "RenderCompositeGlyphs.h"
+#include "RenderFillRectangles.h"
+#include "RenderTrapezoids.h"
+#include "RenderTriangles.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Constructor and destructor.
+//
+
+RenderExtensionStore::RenderExtensionStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = RENDEREXTENSION_ENABLE_CACHE;
+ enableData = RENDEREXTENSION_ENABLE_DATA;
+ enableSplit = RENDEREXTENSION_ENABLE_SPLIT;
+ enableCompress = RENDEREXTENSION_ENABLE_COMPRESS;
+
+ generic_ = new RenderGenericRequestStore();
+
+ for (int i = 0; i < RENDEREXTENSION_MINOR_OPCODE_LIMIT; i++)
+ {
+ minors_[i] = generic_;
+ }
+
+ minors_[X_RenderChangePicture] = new RenderChangePictureStore();
+ minors_[X_RenderFillRectangles] = new RenderFillRectanglesStore();
+ minors_[X_RenderAddGlyphs] = new RenderAddGlyphsStore();
+
+ // Since ProtoStep7 (#issue 108)
+ minors_[X_RenderCreatePicture] = new RenderCreatePictureStore();
+ minors_[X_RenderFreePicture] = new RenderFreePictureStore();
+ minors_[X_RenderSetPictureClipRectangles] = new RenderPictureClipStore();
+ minors_[X_RenderCreateGlyphSet] = new RenderCreateGlyphSetStore();
+ minors_[X_RenderComposite] = new RenderCompositeStore();
+ minors_[X_RenderCompositeGlyphs8] = new RenderCompositeGlyphsStore();
+ minors_[X_RenderCompositeGlyphs16] = new RenderCompositeGlyphsStore();
+ minors_[X_RenderCompositeGlyphs32] = new RenderCompositeGlyphsStore();
+
+ minors_[X_RenderSetPictureTransform] = new RenderPictureTransformStore();
+ minors_[X_RenderSetPictureFilter] = new RenderPictureFilterStore();
+ minors_[X_RenderFreeGlyphSet] = new RenderFreeGlyphSetStore();
+ minors_[X_RenderTrapezoids] = new RenderTrapezoidsStore();
+ minors_[X_RenderTriangles] = new RenderTrianglesStore();
+
+ dataLimit = RENDEREXTENSION_DATA_LIMIT;
+ dataOffset = RENDEREXTENSION_DATA_OFFSET;
+
+ // Since ProtoStep7 (#issue 108)
+ cacheSlots = RENDEREXTENSION_CACHE_SLOTS_IF_PROTO_STEP_7;
+
+ cacheThreshold = RENDEREXTENSION_CACHE_THRESHOLD;
+ cacheLowerThreshold = RENDEREXTENSION_CACHE_LOWER_THRESHOLD;
+
+ opcode_ = X_NXInternalRenderExtension;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+RenderExtensionStore::~RenderExtensionStore()
+{
+ for (int i = 0; i < RENDEREXTENSION_MINOR_OPCODE_LIMIT; i++)
+ {
+ if (minors_[i] != generic_)
+ {
+ delete minors_[i];
+ }
+ }
+
+ delete generic_;
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+int RenderExtensionStore::validateMessage(const unsigned char *buffer, int size)
+{
+ #ifdef TEST
+ *logofs << name() << ": Encoding message OPCODE#"
+ << (unsigned) *buffer << " MINOR#" << (unsigned)
+ *(buffer + 1) << " with size " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (size >= control -> MinimumMessageSize &&
+ size <= control -> MaximumMessageSize);
+}
+
+//
+// Here are the methods to handle the messages' content.
+//
+
+int RenderExtensionStore::identitySize(const unsigned char *buffer, unsigned int size)
+{
+ return minors_[*(buffer + 1)] -> identitySize(buffer, size);
+}
+
+int RenderExtensionStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ encodeBuffer.encodeOpcodeValue(*(buffer + 1),
+ ((ClientCache *) channelCache) -> renderOpcodeCache);
+
+ minors_[*(buffer + 1)] -> encodeMessage(encodeBuffer, buffer, size,
+ bigEndian, channelCache);
+
+ return 1;
+}
+
+int RenderExtensionStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ unsigned char type;
+
+ decodeBuffer.decodeOpcodeValue(type,
+ ((ClientCache *) channelCache) -> renderOpcodeCache);
+
+ minors_[type] -> decodeMessage(decodeBuffer, buffer, size, type,
+ bigEndian, writeBuffer, channelCache);
+
+ return 1;
+}
+
+int RenderExtensionStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ return minors_[*(buffer + 1)] -> parseIdentity(message, buffer, size, bigEndian);
+}
+
+int RenderExtensionStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ return minors_[((RenderExtensionMessage *) message) -> data.any.type] ->
+ unparseIdentity(message, buffer, size, bigEndian);
+}
+
+void RenderExtensionStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ minors_[*(buffer + 1)] -> identityChecksum(message, buffer, size, md5_state_, bigEndian);
+}
+
+void RenderExtensionStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ minors_[((RenderExtensionMessage *) message) -> data.any.type] ->
+ updateIdentity(encodeBuffer, message, cachedMessage, channelCache);
+}
+
+void RenderExtensionStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ minors_[((RenderExtensionMessage *) message) -> data.any.type] ->
+ updateIdentity(decodeBuffer, message, channelCache);
+}
+
+void RenderExtensionStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ #ifdef WARNING
+ *logofs << name() << ": WARNING! Dump of identity not implemented.\n"
+ << logofs_flush;
+ #endif
+
+ #endif
+}
+
+//
+// TODO: The following encoding and decoding functions
+// could be generalized further, for example by passing
+// the pointer to the data cache, the number of caches
+// made available by the caller and the first cache to
+// iterate through.
+//
+
+void RenderMinorExtensionStore::encodeLongData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeLongData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - offset
+ << " bytes of long data.\n" << logofs_flush;
+ #endif
+}
+
+void RenderMinorExtensionStore::encodeIntData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeIntData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - offset
+ << " bytes of int data.\n" << logofs_flush;
+ #endif
+}
+
+void RenderMinorExtensionStore::encodeCharData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - offset
+ << " bytes of text data.\n" << logofs_flush;
+ #endif
+}
+
+void RenderMinorExtensionStore::decodeLongData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeLongData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - offset
+ << " bytes of long data.\n" << logofs_flush;
+ #endif
+}
+
+void RenderMinorExtensionStore::decodeIntData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeIntData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - offset
+ << " bytes of int data.\n" << logofs_flush;
+ #endif
+}
+
+void RenderMinorExtensionStore::decodeCharData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(buffer + offset, size - offset);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - offset
+ << " bytes of text data.\n" << logofs_flush;
+ #endif
+}
+
+void RenderMinorExtensionStore::parseIntData(const Message *message, const unsigned char *buffer,
+ unsigned int offset, unsigned int size,
+ int bigEndian) const
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_);
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Parsing int with i = " << i << " c = "
+ << c << ".\n" << logofs_flush;
+ #endif
+
+ renderExtension -> data.any.short_data[c] = GetUINT(buffer + i, bigEndian);
+
+ if (++c == 16) c = 0;
+ }
+}
+
+void RenderMinorExtensionStore::unparseIntData(const Message *message, unsigned char *buffer,
+ unsigned int offset, unsigned int size,
+ int bigEndian) const
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_);
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsing int with i = " << i << " c = "
+ << c << ".\n" << logofs_flush;
+ #endif
+
+ PutUINT(renderExtension -> data.any.short_data[c], buffer + i, bigEndian);
+
+ if (++c == 16) c = 0;
+ }
+}
+
+void RenderMinorExtensionStore::updateIntData(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage, unsigned int offset,
+ unsigned int size, ChannelCache *channelCache) const
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_);
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding int update with i = " << i
+ << " c = " << c << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(renderExtension -> data.any.short_data[c], 16,
+ *clientCache -> renderDataCache[c]);
+
+ cachedRenderExtension -> data.any.short_data[c] =
+ renderExtension -> data.any.short_data[c];
+
+ if (++c == 16) c = 0;
+ }
+}
+
+void RenderMinorExtensionStore::updateIntData(DecodeBuffer &decodeBuffer, const Message *message,
+ unsigned int offset, unsigned int size,
+ ChannelCache *channelCache) const
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int last = ((unsigned) message -> i_size_ > size ? size : message -> i_size_);
+
+ unsigned int value;
+
+ for (unsigned int i = offset, c = (offset - 4) % 16; i < last; i += 2)
+ {
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding int update with i = " << i
+ << " c = " << c << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> renderDataCache[c]);
+
+ renderExtension -> data.any.short_data[c] = value;
+
+ if (++c == 16) c = 0;
+ }
+}
diff --git a/nxcomp/src/RenderExtension.h b/nxcomp/src/RenderExtension.h
new file mode 100644
index 000000000..aa9db1b55
--- /dev/null
+++ b/nxcomp/src/RenderExtension.h
@@ -0,0 +1,504 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderExtension_H
+#define RenderExtension_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Compression of data part is not enabled as
+// most messages of this type are smaller than
+// the current data size compression threshold.
+//
+
+#define RENDEREXTENSION_ENABLE_CACHE 1
+#define RENDEREXTENSION_ENABLE_DATA 0
+#define RENDEREXTENSION_ENABLE_SPLIT 0
+#define RENDEREXTENSION_ENABLE_COMPRESS 0
+
+#define RENDEREXTENSION_DATA_LIMIT 6144
+#define RENDEREXTENSION_DATA_OFFSET 36
+
+#define RENDEREXTENSION_CACHE_THRESHOLD 20
+#define RENDEREXTENSION_CACHE_LOWER_THRESHOLD 10
+
+#define RENDEREXTENSION_CACHE_SLOTS_IF_PROTO_STEP_7 8000
+
+//
+// Used to build the table of minor opcodes.
+//
+
+#define RENDEREXTENSION_MINOR_OPCODE_LIMIT 256
+
+//
+// The message class.
+//
+
+class RenderMinorExtensionStore;
+
+class RenderExtensionMessage : public Message
+{
+ friend class RenderExtensionStore;
+ friend class RenderMinorExtensionStore;
+
+ friend class RenderGenericRequestStore;
+ friend class RenderCreatePictureStore;
+ friend class RenderChangePictureStore;
+ friend class RenderFreePictureStore;
+ friend class RenderPictureClipStore;
+ friend class RenderPictureTransformStore;
+ friend class RenderPictureFilterStore;
+ friend class RenderCreateGlyphSetStore;
+ friend class RenderFreeGlyphSetStore;
+ friend class RenderAddGlyphsStore;
+ friend class RenderCompositeStore;
+ friend class RenderCompositeGlyphsStore;
+ friend class RenderFillRectanglesStore;
+ friend class RenderTrapezoidsStore;
+ friend class RenderTrianglesStore;
+
+ public:
+
+ RenderExtensionMessage()
+ {
+ }
+
+ ~RenderExtensionMessage()
+ {
+ }
+
+ //
+ // We consider for this message a data offset of 36,
+ // that is size of the biggest among all requests of
+ // this extension. The most common requests have a
+ // specific differential encoding, others are simply
+ // encoded through an array of int or char caches.
+ //
+
+ private:
+
+ union
+ {
+ struct
+ {
+ unsigned char type;
+
+ unsigned char char_data[32];
+ unsigned short short_data[16];
+ unsigned short long_data[8];
+ }
+ any;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+ unsigned int dst_id;
+
+ unsigned int format;
+ unsigned int mask;
+ }
+ create_picture;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+ }
+ change_picture;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+ }
+ free_picture;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+
+ unsigned short src_x;
+ unsigned short src_y;
+ }
+ picture_clip;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+ }
+ picture_transform;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int src_id;
+ unsigned int num_elm;
+ }
+ picture_filter;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int set_id;
+ unsigned int format;
+ }
+ create_set;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int set_id;
+ }
+ free_set;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned int set_id;
+ unsigned int num_elm;
+ }
+ add_glyphs;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned int src_id;
+ unsigned int msk_id;
+ unsigned int dst_id;
+
+ unsigned short src_x;
+ unsigned short src_y;
+
+ unsigned short msk_x;
+ unsigned short msk_y;
+
+ unsigned short dst_x;
+ unsigned short dst_y;
+
+ unsigned short width;
+ unsigned short height;
+ }
+ composite;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned char num_elm;
+
+ unsigned int src_id;
+ unsigned int dst_id;
+
+ unsigned int format;
+ unsigned int set_id;
+
+ unsigned short src_x;
+ unsigned short src_y;
+
+ unsigned short offset_x;
+ unsigned short offset_y;
+ }
+ composite_glyphs;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned int dst_id;
+ }
+ fill_rectangles;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned int src_id;
+ unsigned int dst_id;
+
+ unsigned int format;
+
+ unsigned short src_x;
+ unsigned short src_y;
+ }
+ trapezoids;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned int src_id;
+ unsigned int dst_id;
+
+ unsigned int format;
+
+ unsigned short src_x;
+ unsigned short src_y;
+ }
+ triangles;
+
+ struct
+ {
+ unsigned char type;
+
+ unsigned char op;
+
+ unsigned char num_elm;
+
+ unsigned int src_id;
+ unsigned int dst_id;
+
+ unsigned int format;
+ unsigned int set_id;
+
+ unsigned short src_x;
+ unsigned short src_y;
+
+ unsigned short delta_x;
+ unsigned short delta_y;
+ }
+ composite_glyphs_compat;
+
+ }
+ data;
+};
+
+class RenderExtensionStore : public MessageStore
+{
+ public:
+
+ RenderExtensionStore(StaticCompressor *compressor);
+
+ virtual ~RenderExtensionStore();
+
+ virtual const char *name() const
+ {
+ return "RenderExtension";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return opcode_;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(RenderExtensionMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new RenderExtensionMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new RenderExtensionMessage((const RenderExtensionMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (RenderExtensionMessage *) message;
+ }
+
+ //
+ // Determine if the message must be stored
+ // in the cache.
+ //
+
+ virtual int validateMessage(const unsigned char *buffer, int size);
+
+ //
+ // Since protocol step 5 these methods are
+ // specialized in their minor opcode stores.
+ //
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size);
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+
+ private:
+
+ unsigned char opcode_;
+
+ //
+ // Keep pointers to specialized classes.
+ //
+
+ RenderMinorExtensionStore *minors_[RENDEREXTENSION_MINOR_OPCODE_LIMIT];
+
+ RenderMinorExtensionStore *generic_;
+};
+
+class RenderMinorExtensionStore : public MinorMessageStore
+{
+ public:
+
+ virtual const char *name() const = 0;
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size) = 0;
+
+ virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const = 0;
+
+ virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const = 0;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const = 0;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const = 0;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const = 0;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, md5_state_t *md5_state,
+ int bigEndian) const = 0;
+
+ //
+ // Internal encode and decode utilities.
+ //
+
+ protected:
+
+ void encodeLongData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ void encodeIntData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ void encodeCharData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ void decodeLongData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ void decodeIntData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ void decodeCharData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int offset, unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ /*
+ * The following methods are only used in the
+ * encoding of the generic render request. To
+ * be removed in future.
+ */
+
+ void parseIntData(const Message *message, const unsigned char *buffer,
+ unsigned int offset, unsigned int size,
+ int bigEndian) const;
+
+ void unparseIntData(const Message *message, unsigned char *buffer,
+ unsigned int offset, unsigned int size,
+ int bigEndian) const;
+
+ void updateIntData(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage, unsigned int offset,
+ unsigned int size, ChannelCache *channelCache) const;
+
+ void updateIntData(DecodeBuffer &decodeBuffer, const Message *message,
+ unsigned int offset, unsigned int size,
+ ChannelCache *channelCache) const;
+};
+
+#endif /* RenderExtension_H */
diff --git a/nxcomp/src/RenderFillRectangles.cpp b/nxcomp/src/RenderFillRectangles.cpp
new file mode 100644
index 000000000..db05887ab
--- /dev/null
+++ b/nxcomp/src/RenderFillRectangles.cpp
@@ -0,0 +1,237 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderFillRectangles.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ //
+ // The color structure (4 components, 2 bytes
+ // each) is included in the data part, so that
+ // it gets into the checksum. The rectangles
+ // are in the format x, y, width, height with
+ // 2 bytes per each field, so each request is
+ // at least 12 + 8 + 8 = 28 bytes long.
+ //
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value, clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.fill_rectangles.type = *(buffer + 1);
+ renderExtension -> data.fill_rectangles.op = *(buffer + 4);
+
+ renderExtension -> data.fill_rectangles.dst_id = GetULONG(buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.fill_rectangles.type << " size is "
+ << renderExtension -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.fill_rectangles.type;
+ *(buffer + 4) = renderExtension -> data.fill_rectangles.op;
+
+ PutULONG(renderExtension -> data.fill_rectangles.dst_id, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.fill_rectangles.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.fill_rectangles.dst_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.fill_rectangles.dst_id =
+ renderExtension -> data.fill_rectangles.dst_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.fill_rectangles.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.fill_rectangles.dst_id,
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.fill_rectangles.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderFillRectangles.h b/nxcomp/src/RenderFillRectangles.h
new file mode 100644
index 000000000..9efaeffa8
--- /dev/null
+++ b/nxcomp/src/RenderFillRectangles.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderFillRectangles_H
+#define RenderFillRectangles_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderFillRectangles"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderFillRectanglesStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderFillRectangles_H */
diff --git a/nxcomp/src/RenderFreeGlyphSet.cpp b/nxcomp/src/RenderFreeGlyphSet.cpp
new file mode 100644
index 000000000..88dfb791a
--- /dev/null
+++ b/nxcomp/src/RenderFreeGlyphSet.cpp
@@ -0,0 +1,166 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderFreeGlyphSet.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeFreeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderFreeGlyphSetCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ unsigned int value;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeFreeXidValue(value,
+ clientCache -> renderFreeGlyphSetCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.free_set.type = *(buffer + 1);
+
+ renderExtension -> data.free_set.set_id = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.free_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.free_set.type;
+
+ PutULONG(renderExtension -> data.free_set.set_id, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.free_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeFreeXidValue(renderExtension -> data.free_set.set_id,
+ clientCache -> renderFreeGlyphSetCache);
+
+ cachedRenderExtension -> data.free_set.set_id =
+ renderExtension -> data.free_set.set_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.free_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeFreeXidValue(renderExtension -> data.free_set.set_id,
+ clientCache -> renderFreeGlyphSetCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.free_set.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderFreeGlyphSet.h b/nxcomp/src/RenderFreeGlyphSet.h
new file mode 100644
index 000000000..8817e8d99
--- /dev/null
+++ b/nxcomp/src/RenderFreeGlyphSet.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderFreeGlyphSet_H
+#define RenderFreeGlyphSet_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderFreeGlyphSet"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderFreeGlyphSetStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 8
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 0
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 0
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderFreeGlyphSet_H */
diff --git a/nxcomp/src/RenderFreePicture.cpp b/nxcomp/src/RenderFreePicture.cpp
new file mode 100644
index 000000000..31662a7e2
--- /dev/null
+++ b/nxcomp/src/RenderFreePicture.cpp
@@ -0,0 +1,166 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderFreePicture.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeFreeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderFreePictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ unsigned int value;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeFreeXidValue(value,
+ clientCache -> renderFreePictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.free_picture.type = *(buffer + 1);
+
+ renderExtension -> data.free_picture.src_id = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.free_picture.type;
+
+ PutULONG(renderExtension -> data.free_picture.src_id, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeFreeXidValue(renderExtension -> data.free_picture.src_id,
+ clientCache -> renderFreePictureCache);
+
+ cachedRenderExtension -> data.free_picture.src_id =
+ renderExtension -> data.free_picture.src_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeFreeXidValue(renderExtension -> data.free_picture.src_id,
+ clientCache -> renderFreePictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.free_picture.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderFreePicture.h b/nxcomp/src/RenderFreePicture.h
new file mode 100644
index 000000000..b50191a72
--- /dev/null
+++ b/nxcomp/src/RenderFreePicture.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderFreePicture_H
+#define RenderFreePicture_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderFreePicture"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderFreePictureStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 8
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 0
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 0
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderFreePicture_H */
diff --git a/nxcomp/src/RenderGenericRequest.cpp b/nxcomp/src/RenderGenericRequest.cpp
new file mode 100644
index 000000000..cff34e61e
--- /dev/null
+++ b/nxcomp/src/RenderGenericRequest.cpp
@@ -0,0 +1,270 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "NXrender.h"
+
+#include "RenderExtension.h"
+#include "RenderGenericRequest.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Here are the methods to handle the messages' content.
+//
+
+int RenderGenericRequestStore::encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message.\n"
+ << logofs_flush;
+
+ unsigned char type = *(buffer + 1);
+
+ #endif
+
+ encodeBuffer.encodeCachedValue(size >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full unhandled message. "
+ << "Type is " << (unsigned int) type << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ encodeIntData(encodeBuffer, buffer, 4, size,
+ bigEndian, clientCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int RenderGenericRequestStore::decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message.\n"
+ << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size <<= 2;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ *(buffer + 1) = type;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full unhandled message. "
+ << "Type is " << (unsigned int) type << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+
+ decodeIntData(decodeBuffer, buffer, 4, size,
+ bigEndian, clientCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void RenderGenericRequestStore::encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+}
+
+void RenderGenericRequestStore::decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+}
+
+int RenderGenericRequestStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ #ifdef DEBUG
+ *logofs << name() << ": Parsing identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ unsigned char type = *(buffer + 1);
+
+ renderExtension -> data.any.type = type;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsing unhandled identity. "
+ << "Type is " << (unsigned int) renderExtension -> data.any.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ parseIntData(message, buffer, 4, size, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int RenderGenericRequestStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsing identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ unsigned char type = renderExtension -> data.any.type;
+
+ *(buffer + 1) = type;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsing unhandled identity. "
+ << "Type is " << (unsigned int) renderExtension -> data.any.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ unparseIntData(message, buffer, 4, size, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void RenderGenericRequestStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, md5_state_t *md5_state,
+ int bigEndian) const
+{
+ //
+ // Include the minor opcode in the checksum.
+ // Because the data offset can be beyond the
+ // real end of the message, we need to include
+ // the size or we will match any message whose
+ // size is less or equal to the data offset.
+ //
+
+ md5_append(md5_state, buffer + 1, 3);
+}
+
+void RenderGenericRequestStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ #ifdef DEBUG
+ *logofs << name() << ": Updating identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding unhandled update. "
+ << "Type is " << (unsigned int) renderExtension -> data.any.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ updateIntData(encodeBuffer, message, cachedMessage, 4,
+ renderExtension -> size_, channelCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Updated identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+}
+
+void RenderGenericRequestStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ #ifdef DEBUG
+ *logofs << name() << ": Updating identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding unhandled update. "
+ << "Type is " << (unsigned int) renderExtension -> data.any.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ updateIntData(decodeBuffer, message, 4,
+ renderExtension -> size_, channelCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Updated identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/RenderGenericRequest.h b/nxcomp/src/RenderGenericRequest.h
new file mode 100644
index 000000000..fdf5ca876
--- /dev/null
+++ b/nxcomp/src/RenderGenericRequest.h
@@ -0,0 +1,89 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderGenericRequest_H
+#define RenderGenericRequest_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+class RenderGenericRequestStore : public RenderMinorExtensionStore
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return "RenderGenericRequest";
+ }
+
+ virtual int identitySize(const unsigned char *buffer, unsigned int size)
+ {
+ return RENDEREXTENSION_DATA_OFFSET;
+ }
+
+ virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const;
+
+ virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, md5_state_t *md5_state,
+ int bigEndian) const;
+};
+
+#endif /* RenderGenericRequest_H */
diff --git a/nxcomp/src/RenderMinorExtensionHeaders.h b/nxcomp/src/RenderMinorExtensionHeaders.h
new file mode 100644
index 000000000..b7f6efc5a
--- /dev/null
+++ b/nxcomp/src/RenderMinorExtensionHeaders.h
@@ -0,0 +1,42 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderMinorExtensionHeaders_H
+#define RenderMinorExtensionHeaders_H
+
+#include "NXrender.h"
+
+#include "Message.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+#include "RenderExtension.h"
+
+#endif /* RenderMinorExtensionHeaders_H */
diff --git a/nxcomp/src/RenderMinorExtensionMethods.h b/nxcomp/src/RenderMinorExtensionMethods.h
new file mode 100644
index 000000000..d272337e0
--- /dev/null
+++ b/nxcomp/src/RenderMinorExtensionMethods.h
@@ -0,0 +1,81 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+//
+// This file is included multiple times,
+// one for each message inheriting the
+// parent class.
+//
+
+public:
+
+#if MESSAGE_HAS_SIZE
+
+virtual void encodeSize(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+virtual void decodeSize(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const;
+
+#endif
+
+#if MESSAGE_HAS_DATA
+
+virtual void encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+virtual void decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer,
+ unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+#endif
+
+virtual int encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+virtual int decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, unsigned char type, int bigEndian,
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const;
+
+virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, md5_state_t *md5_state,
+ int bigEndian) const;
diff --git a/nxcomp/src/RenderMinorExtensionTags.h b/nxcomp/src/RenderMinorExtensionTags.h
new file mode 100644
index 000000000..c24a99638
--- /dev/null
+++ b/nxcomp/src/RenderMinorExtensionTags.h
@@ -0,0 +1,194 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderMinorExtensionTags_H
+#define RenderMinorExtensionTags_H
+
+//
+// Set in the message header file.
+//
+
+#if MESSAGE_HAS_SIZE
+
+#define MESSAGE_ENCODE_SIZE encodeSize(encodeBuffer, buffer, size, bigEndian, channelCache)
+#define MESSAGE_DECODE_SIZE decodeSize(decodeBuffer, buffer, size, type, bigEndian, writeBuffer, channelCache)
+
+#else
+
+#define MESSAGE_ENCODE_SIZE
+#define MESSAGE_DECODE_SIZE size = MESSAGE_OFFSET; buffer = writeBuffer -> addMessage(size);
+
+#endif
+
+#if MESSAGE_HAS_DATA
+
+#define MESSAGE_ENCODE_DATA encodeData(encodeBuffer, buffer, size, bigEndian, channelCache)
+#define MESSAGE_DECODE_DATA decodeData(decodeBuffer, buffer, size, bigEndian, channelCache)
+
+#else
+
+#define MESSAGE_ENCODE_DATA
+#define MESSAGE_DECODE_DATA
+
+#endif
+
+//
+// Prologue an epilogue of the message
+// handling functions.
+//
+
+#define MESSAGE_BEGIN_ENCODE_SIZE \
+\
+void MESSAGE_STORE::encodeSize(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \
+ const unsigned int size, int bigEndian, \
+ ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_ENCODE_SIZE \
+\
+}
+
+#define MESSAGE_BEGIN_DECODE_SIZE \
+\
+void MESSAGE_STORE::decodeSize(DecodeBuffer &decodeBuffer, unsigned char *&buffer, \
+ unsigned int &size, unsigned char type, int bigEndian, \
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_DECODE_SIZE \
+\
+}
+
+#define MESSAGE_BEGIN_ENCODE_MESSAGE \
+\
+int MESSAGE_STORE::encodeMessage(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \
+ const unsigned int size, int bigEndian, \
+ ChannelCache *channelCache) const \
+{ \
+ MESSAGE_ENCODE_SIZE;
+
+
+#define MESSAGE_END_ENCODE_MESSAGE \
+\
+ MESSAGE_ENCODE_DATA; \
+\
+ return 1; \
+}
+
+#define MESSAGE_BEGIN_DECODE_MESSAGE \
+\
+int MESSAGE_STORE::decodeMessage(DecodeBuffer &decodeBuffer, unsigned char *&buffer, \
+ unsigned int &size, unsigned char type, int bigEndian, \
+ WriteBuffer *writeBuffer, ChannelCache *channelCache) const \
+{ \
+ MESSAGE_DECODE_SIZE;
+
+
+#define MESSAGE_END_DECODE_MESSAGE \
+\
+ MESSAGE_DECODE_DATA; \
+\
+ return 1; \
+}
+
+#define MESSAGE_BEGIN_ENCODE_DATA \
+\
+void MESSAGE_STORE::encodeData(EncodeBuffer &encodeBuffer, const unsigned char *buffer, \
+ unsigned int size, int bigEndian, \
+ ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_ENCODE_DATA \
+\
+}
+
+#define MESSAGE_BEGIN_DECODE_DATA \
+\
+void MESSAGE_STORE::decodeData(DecodeBuffer &decodeBuffer, unsigned char *buffer, \
+ unsigned int size, int bigEndian, \
+ ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_DECODE_DATA \
+\
+}
+
+#define MESSAGE_BEGIN_PARSE_IDENTITY \
+\
+int MESSAGE_STORE::parseIdentity(Message *message, const unsigned char *buffer, \
+ unsigned int size, int bigEndian) const \
+{
+
+#define MESSAGE_END_PARSE_IDENTITY \
+\
+ return 1; \
+\
+}
+
+#define MESSAGE_BEGIN_UNPARSE_IDENTITY \
+\
+int MESSAGE_STORE::unparseIdentity(const Message *message, unsigned char *buffer, \
+ unsigned int size, int bigEndian) const \
+{
+
+#define MESSAGE_END_UNPARSE_IDENTITY \
+\
+ return 1; \
+\
+}
+
+#define MESSAGE_BEGIN_IDENTITY_CHECKSUM \
+\
+void MESSAGE_STORE::identityChecksum(const Message *message, const unsigned char *buffer, \
+ unsigned int size, md5_state_t *md5_state, \
+ int bigEndian) const \
+{
+
+#define MESSAGE_END_IDENTITY_CHECKSUM \
+\
+}
+
+#define MESSAGE_BEGIN_ENCODE_UPDATE \
+\
+void MESSAGE_STORE::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message, \
+ const Message *cachedMessage, \
+ ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_ENCODE_UPDATE \
+\
+}
+
+#define MESSAGE_BEGIN_DECODE_UPDATE \
+\
+void MESSAGE_STORE::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message, \
+ ChannelCache *channelCache) const \
+{
+
+#define MESSAGE_END_DECODE_UPDATE \
+\
+}
+
+#endif /* RenderMinorExtensionTags_H */
diff --git a/nxcomp/src/RenderPictureClip.cpp b/nxcomp/src/RenderPictureClip.cpp
new file mode 100644
index 000000000..0d6b505eb
--- /dev/null
+++ b/nxcomp/src/RenderPictureClip.cpp
@@ -0,0 +1,303 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderPictureClip.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ //
+ // The data is constituted by a number of
+ // rectangles. Each rectangle is in the
+ // format x, y, width, height with 2 bytes
+ // per each field, so each request is at
+ // least 12 + 8 = 20 bytes long.
+ //
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 8, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 10, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 10, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeIntData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeIntData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.picture_clip.type = *(buffer + 1);
+
+ renderExtension -> data.picture_clip.src_id = GetULONG(buffer + 4, bigEndian);
+
+ renderExtension -> data.picture_clip.src_x = GetUINT(buffer + 8, bigEndian);
+ renderExtension -> data.picture_clip.src_y = GetUINT(buffer + 10, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.picture_clip.type;
+
+ PutULONG(renderExtension -> data.picture_clip.src_id, buffer + 4, bigEndian);
+
+ PutUINT(renderExtension -> data.picture_clip.src_x, buffer + 8, bigEndian);
+ PutUINT(renderExtension -> data.picture_clip.src_y, buffer + 10, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Encode the picture id and the
+ // source x and y differentially.
+ //
+
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.picture_clip.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.picture_clip.src_id =
+ renderExtension -> data.picture_clip.src_id;
+
+ //
+ // The source x and y coordinates are
+ // encoded as differerences in respect
+ // to the previous cached value.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ value = renderExtension -> data.picture_clip.src_x;
+ previous = cachedRenderExtension -> data.picture_clip.src_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ cachedRenderExtension -> data.picture_clip.src_x = value;
+
+ value = renderExtension -> data.picture_clip.src_y;
+ previous = cachedRenderExtension -> data.picture_clip.src_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ cachedRenderExtension -> data.picture_clip.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.picture_clip.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ unsigned int value;
+ unsigned int previous;
+
+ previous = renderExtension -> data.picture_clip.src_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ renderExtension -> data.picture_clip.src_x = value;
+
+ previous = renderExtension -> data.picture_clip.src_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ renderExtension -> data.picture_clip.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_clip.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderPictureClip.h b/nxcomp/src/RenderPictureClip.h
new file mode 100644
index 000000000..bd811dfcd
--- /dev/null
+++ b/nxcomp/src/RenderPictureClip.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderPictureClip_H
+#define RenderPictureClip_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderPictureClip"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderPictureClipStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderPictureClip_H */
diff --git a/nxcomp/src/RenderPictureFilter.cpp b/nxcomp/src/RenderPictureFilter.cpp
new file mode 100644
index 000000000..ce40e051a
--- /dev/null
+++ b/nxcomp/src/RenderPictureFilter.cpp
@@ -0,0 +1,278 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderPictureFilter.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value "
+ << ((size - MESSAGE_OFFSET) >> 2) << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 8, bigEndian), 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache -> renderLengthCache, 5);
+
+ PutUINT(value, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeCharData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeCharData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.picture_filter.type = *(buffer + 1);
+
+ renderExtension -> data.picture_filter.src_id = GetULONG(buffer + 4, bigEndian);
+ renderExtension -> data.picture_filter.num_elm = GetUINT(buffer + 8, bigEndian);
+
+ //
+ // Clean the padding bytes. This
+ // should be the purpose of the
+ // filter.
+ //
+
+ #ifdef TEST
+ *logofs << name() << ": Cleaning "
+ << RoundUp4(renderExtension -> data.picture_filter.num_elm) -
+ renderExtension -> data.picture_filter.num_elm << " bytes "
+ << "at offset " << MESSAGE_OFFSET + renderExtension ->
+ data.picture_filter.num_elm << " with " << renderExtension ->
+ data.picture_filter.num_elm << " elements and size "
+ << renderExtension -> size_ << ".\n" << logofs_flush;
+ #endif
+
+ if (size >= MESSAGE_OFFSET + renderExtension ->
+ data.picture_filter.num_elm)
+ {
+ unsigned char *next = (unsigned char *) buffer +
+ MESSAGE_OFFSET + renderExtension ->
+ data.picture_filter.num_elm;
+
+ while (next < buffer + size)
+ {
+ *next++ = '\0';
+ }
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_filter.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.picture_filter.type;
+
+ PutULONG(renderExtension -> data.picture_filter.src_id, buffer + 4, bigEndian);
+ PutUINT(renderExtension -> data.picture_filter.num_elm, buffer + 8, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_filter.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include the length of the filter name
+ // in the checksum.
+ //
+
+ md5_append(md5_state, buffer + 1, 3);
+ md5_append(md5_state, buffer + 8, 2);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.picture_filter.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.picture_filter.src_id =
+ renderExtension -> data.picture_filter.src_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_filter.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.picture_filter.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_filter.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderPictureFilter.h b/nxcomp/src/RenderPictureFilter.h
new file mode 100644
index 000000000..a3e37538b
--- /dev/null
+++ b/nxcomp/src/RenderPictureFilter.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderPictureFilter_H
+#define RenderPictureFilter_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderPictureFilter"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderPictureFilterStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 12
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderPictureFilter_H */
diff --git a/nxcomp/src/RenderPictureTransform.cpp b/nxcomp/src/RenderPictureTransform.cpp
new file mode 100644
index 000000000..0048e61f4
--- /dev/null
+++ b/nxcomp/src/RenderPictureTransform.cpp
@@ -0,0 +1,214 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderPictureTransform.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ //
+ // Size is always 44. The identity size
+ // is set to 8, so we encode the 36 bytes
+ // of the transformation matrix as our
+ // data.
+ //
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ size = MESSAGE_OFFSET + 36;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 4, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.picture_transform.type = *(buffer + 1);
+
+ renderExtension -> data.picture_transform.src_id = GetULONG(buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_transform.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.picture_transform.type;
+
+ PutULONG(renderExtension -> data.picture_transform.src_id, buffer + 4, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.picture_transform.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ md5_append(md5_state, buffer + 1, 3);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.picture_transform.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.picture_transform.src_id =
+ renderExtension -> data.picture_transform.src_id;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_transform.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.picture_transform.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.picture_transform.type
+ << " size is " << renderExtension -> size_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderPictureTransform.h b/nxcomp/src/RenderPictureTransform.h
new file mode 100644
index 000000000..649cd05d3
--- /dev/null
+++ b/nxcomp/src/RenderPictureTransform.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderPictureTransform_H
+#define RenderPictureTransform_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderPictureTransform"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderPictureTransformStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 8
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return MESSAGE_OFFSET;
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderPictureTransform_H */
diff --git a/nxcomp/src/RenderTrapezoids.cpp b/nxcomp/src/RenderTrapezoids.cpp
new file mode 100644
index 000000000..bf84509dc
--- /dev/null
+++ b/nxcomp/src/RenderTrapezoids.cpp
@@ -0,0 +1,372 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderTrapezoids.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ //
+ // The trapezoid data is made up of a structure
+ // containing a top and bottom coordinate in 4
+ // bytes format, plus two lines, each represent-
+ // ed as four points in 4 bytes format. Thus
+ // each trapezoid is 4 * 2 + (4 * 4) * 2 = 40
+ // bytes. Bytes are all padded to an long int,
+ // so we don't need to clean the message.
+ //
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value "
+ << ((size - MESSAGE_OFFSET) >> 2) << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian),
+ clientCache -> renderDstPictureCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderDstPictureCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ if (size > MESSAGE_OFFSET)
+ {
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of text data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ if (size > MESSAGE_OFFSET)
+ {
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.trapezoids.type = *(buffer + 1);
+ renderExtension -> data.trapezoids.op = *(buffer + 4);
+
+ renderExtension -> data.trapezoids.src_id = GetULONG(buffer + 8, bigEndian);
+ renderExtension -> data.trapezoids.dst_id = GetULONG(buffer + 12, bigEndian);
+
+ renderExtension -> data.trapezoids.format = GetULONG(buffer + 16, bigEndian);
+
+ renderExtension -> data.trapezoids.src_x = GetUINT(buffer + 20, bigEndian);
+ renderExtension -> data.trapezoids.src_y = GetUINT(buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.trapezoids.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.trapezoids.type;
+ *(buffer + 4) = renderExtension -> data.trapezoids.op;
+
+ PutULONG(renderExtension -> data.trapezoids.src_id, buffer + 8, bigEndian);
+ PutULONG(renderExtension -> data.trapezoids.dst_id, buffer + 12, bigEndian);
+
+ PutULONG(renderExtension -> data.trapezoids.format, buffer + 16, bigEndian);
+
+ PutUINT(renderExtension -> data.trapezoids.src_x, buffer + 20, bigEndian);
+ PutUINT(renderExtension -> data.trapezoids.src_y, buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.trapezoids.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include minor opcode, size and the
+ // operator in the identity.
+ //
+
+ md5_append(md5_state, buffer + 1, 4);
+
+ //
+ // Also include the format but not the
+ // x and y source.
+ //
+
+ md5_append(md5_state, buffer + 16, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.trapezoids.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.trapezoids.src_id =
+ renderExtension -> data.trapezoids.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.trapezoids.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ cachedRenderExtension -> data.trapezoids.dst_id =
+ renderExtension -> data.trapezoids.dst_id;
+
+ //
+ // The source x and y coordinates are
+ // encoded as differerences in respect
+ // to the previous cached value.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ value = renderExtension -> data.trapezoids.src_x;
+ previous = cachedRenderExtension -> data.trapezoids.src_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ cachedRenderExtension -> data.trapezoids.src_x = value;
+
+ value = renderExtension -> data.trapezoids.src_y;
+ previous = cachedRenderExtension -> data.trapezoids.src_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ cachedRenderExtension -> data.trapezoids.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.trapezoids.type
+ << " size is " << renderExtension -> size_ << " source x "
+ << renderExtension -> data.trapezoids.src_x << " y "
+ << renderExtension -> data.trapezoids.src_y << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.trapezoids.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.trapezoids.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ unsigned int value;
+ unsigned int previous;
+
+ previous = renderExtension -> data.trapezoids.src_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ renderExtension -> data.trapezoids.src_x = value;
+
+ previous = renderExtension -> data.trapezoids.src_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ renderExtension -> data.trapezoids.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.trapezoids.type
+ << " size is " << renderExtension -> size_ << " source x "
+ << renderExtension -> data.trapezoids.src_x << " y "
+ << renderExtension -> data.trapezoids.src_y << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderTrapezoids.h b/nxcomp/src/RenderTrapezoids.h
new file mode 100644
index 000000000..faf524c76
--- /dev/null
+++ b/nxcomp/src/RenderTrapezoids.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderTrapezoids_H
+#define RenderTrapezoids_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderTrapezoids"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderTrapezoidsStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 24
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size);
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderTrapezoids_H */
diff --git a/nxcomp/src/RenderTriangles.cpp b/nxcomp/src/RenderTriangles.cpp
new file mode 100644
index 000000000..a5eb4d83d
--- /dev/null
+++ b/nxcomp/src/RenderTriangles.cpp
@@ -0,0 +1,362 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+//
+// Include the template for
+// this message class.
+//
+
+#include "RenderTriangles.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#include MESSAGE_TAGS
+
+//
+// Message handling methods.
+//
+
+MESSAGE_BEGIN_ENCODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding value "
+ << ((size - MESSAGE_OFFSET) >> 2) << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((size - MESSAGE_OFFSET) >> 2, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_SIZE
+
+MESSAGE_BEGIN_DECODE_SIZE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(size, 16,
+ clientCache -> renderLengthCache, 5);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = MESSAGE_OFFSET + (size << 2);
+
+ buffer = writeBuffer -> addMessage(size);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded size with value "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_SIZE
+
+MESSAGE_BEGIN_ENCODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 8, bigEndian),
+ clientCache -> renderSrcPictureCache);
+
+ encodeBuffer.encodeXidValue(GetULONG(buffer + 12, bigEndian),
+ clientCache -> renderDstPictureCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(buffer + 16, bigEndian), 32,
+ clientCache -> renderFormatCache);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 20, bigEndian),
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ encodeBuffer.encodeDiffCachedValue(GetUINT(buffer + 22, bigEndian),
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded message. Type is "
+ << (unsigned int) *(buffer + 1) << " size is "
+ << size << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_MESSAGE
+
+MESSAGE_BEGIN_DECODE_MESSAGE
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ *(buffer + 1) = type;
+
+ decodeBuffer.decodeCachedValue(*(buffer + 4), 8,
+ clientCache -> renderOpCache);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderSrcPictureCache);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ decodeBuffer.decodeXidValue(value,
+ clientCache -> renderDstPictureCache);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache -> renderFormatCache);
+
+ PutULONG(value, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastX, 16,
+ clientCache -> renderXCache, 11);
+
+ PutUINT(clientCache -> renderLastX, buffer + 20, bigEndian);
+
+ decodeBuffer.decodeDiffCachedValue(value,
+ clientCache -> renderLastY, 16,
+ clientCache -> renderYCache, 11);
+
+ PutUINT(clientCache -> renderLastY, buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded message. Type is "
+ << (unsigned int) type << " size is " << size
+ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_MESSAGE
+
+MESSAGE_BEGIN_ENCODE_DATA
+{
+ if (size > MESSAGE_OFFSET)
+ {
+ encodeLongData(encodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded " << size - MESSAGE_OFFSET
+ << " bytes of text data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_DATA
+
+MESSAGE_BEGIN_DECODE_DATA
+{
+ if (size > MESSAGE_OFFSET)
+ {
+ decodeLongData(decodeBuffer, buffer, MESSAGE_OFFSET,
+ size, bigEndian, channelCache);
+ }
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded " << size - MESSAGE_OFFSET
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_DATA
+
+MESSAGE_BEGIN_PARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ renderExtension -> data.triangles.type = *(buffer + 1);
+ renderExtension -> data.triangles.op = *(buffer + 4);
+
+ renderExtension -> data.triangles.src_id = GetULONG(buffer + 8, bigEndian);
+ renderExtension -> data.triangles.dst_id = GetULONG(buffer + 12, bigEndian);
+
+ renderExtension -> data.triangles.format = GetULONG(buffer + 16, bigEndian);
+
+ renderExtension -> data.triangles.src_x = GetUINT(buffer + 20, bigEndian);
+ renderExtension -> data.triangles.src_y = GetUINT(buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Parsed identity. Type is "
+ << (unsigned int) renderExtension -> data.triangles.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_PARSE_IDENTITY
+
+MESSAGE_BEGIN_UNPARSE_IDENTITY
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ *(buffer + 1) = renderExtension -> data.triangles.type;
+ *(buffer + 4) = renderExtension -> data.triangles.op;
+
+ PutULONG(renderExtension -> data.triangles.src_id, buffer + 8, bigEndian);
+ PutULONG(renderExtension -> data.triangles.dst_id, buffer + 12, bigEndian);
+
+ PutULONG(renderExtension -> data.triangles.format, buffer + 16, bigEndian);
+
+ PutUINT(renderExtension -> data.triangles.src_x, buffer + 20, bigEndian);
+ PutUINT(renderExtension -> data.triangles.src_y, buffer + 22, bigEndian);
+
+ #ifdef TEST
+ *logofs << name() << ": Unparsed identity. Type is "
+ << (unsigned int) renderExtension -> data.triangles.type
+ << " size is " << renderExtension -> size_ << " identity size "
+ << renderExtension -> i_size_ << ".\n" << logofs_flush;
+ #endif
+}
+MESSAGE_END_UNPARSE_IDENTITY
+
+MESSAGE_BEGIN_IDENTITY_CHECKSUM
+{
+ //
+ // Include minor opcode, size and the
+ // operator in the identity.
+ //
+
+ md5_append(md5_state, buffer + 1, 4);
+
+ //
+ // Also include the format but not the
+ // x and y source.
+ //
+
+ md5_append(md5_state, buffer + 16, 4);
+}
+MESSAGE_END_IDENTITY_CHECKSUM
+
+MESSAGE_BEGIN_ENCODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+ RenderExtensionMessage *cachedRenderExtension = (RenderExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.triangles.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ cachedRenderExtension -> data.triangles.src_id =
+ renderExtension -> data.triangles.src_id;
+
+ encodeBuffer.encodeXidValue(renderExtension -> data.triangles.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ cachedRenderExtension -> data.triangles.dst_id =
+ renderExtension -> data.triangles.dst_id;
+
+ //
+ // The source x and y coordinates are
+ // encoded as differerences in respect
+ // to the previous cached value.
+ //
+
+ unsigned int value;
+ unsigned int previous;
+
+ value = renderExtension -> data.triangles.src_x;
+ previous = cachedRenderExtension -> data.triangles.src_x;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ cachedRenderExtension -> data.triangles.src_x = value;
+
+ value = renderExtension -> data.triangles.src_y;
+ previous = cachedRenderExtension -> data.triangles.src_y;
+
+ encodeBuffer.encodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ cachedRenderExtension -> data.triangles.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoded update. Type is "
+ << (unsigned int) renderExtension -> data.triangles.type
+ << " size is " << renderExtension -> size_ << " source x "
+ << renderExtension -> data.triangles.src_x << " y "
+ << renderExtension -> data.triangles.src_y << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_ENCODE_UPDATE
+
+MESSAGE_BEGIN_DECODE_UPDATE
+{
+ RenderExtensionMessage *renderExtension = (RenderExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.triangles.src_id,
+ clientCache -> renderSrcPictureCache);
+
+ decodeBuffer.decodeXidValue(renderExtension -> data.triangles.dst_id,
+ clientCache -> renderDstPictureCache);
+
+ unsigned int value;
+ unsigned int previous;
+
+ previous = renderExtension -> data.triangles.src_x;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderXCache, 11);
+
+ renderExtension -> data.triangles.src_x = value;
+
+ previous = renderExtension -> data.triangles.src_y;
+
+ decodeBuffer.decodeDiffCachedValue(value, previous, 16,
+ clientCache -> renderYCache, 11);
+
+ renderExtension -> data.triangles.src_y = value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded update. Type is "
+ << (unsigned int) renderExtension -> data.triangles.type
+ << " size is " << renderExtension -> size_ << " source x "
+ << renderExtension -> data.triangles.src_x << " y "
+ << renderExtension -> data.triangles.src_y << ".\n"
+ << logofs_flush;
+ #endif
+}
+MESSAGE_END_DECODE_UPDATE
diff --git a/nxcomp/src/RenderTriangles.h b/nxcomp/src/RenderTriangles.h
new file mode 100644
index 000000000..d73efb7b8
--- /dev/null
+++ b/nxcomp/src/RenderTriangles.h
@@ -0,0 +1,88 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef RenderTriangles_H
+#define RenderTriangles_H
+
+//
+// Define the characteristics
+// of this message class here.
+//
+
+#undef MESSAGE_NAME
+#define MESSAGE_NAME "RenderTriangles"
+
+#undef MESSAGE_STORE
+#define MESSAGE_STORE RenderTrianglesStore
+
+#undef MESSAGE_CLASS
+#define MESSAGE_CLASS RenderMinorExtensionStore
+
+#undef MESSAGE_METHODS
+#define MESSAGE_METHODS "RenderMinorExtensionMethods.h"
+
+#undef MESSAGE_HEADERS
+#define MESSAGE_HEADERS "RenderMinorExtensionHeaders.h"
+
+#undef MESSAGE_TAGS
+#define MESSAGE_TAGS "RenderMinorExtensionTags.h"
+
+#undef MESSAGE_OFFSET
+#define MESSAGE_OFFSET 24
+
+#undef MESSAGE_HAS_SIZE
+#define MESSAGE_HAS_SIZE 1
+
+#undef MESSAGE_HAS_DATA
+#define MESSAGE_HAS_DATA 1
+
+#undef MESSAGE_HAS_FILTER
+#define MESSAGE_HAS_FILTER 0
+
+//
+// Declare the message class.
+//
+
+#include MESSAGE_HEADERS
+
+class MESSAGE_STORE : public MESSAGE_CLASS
+{
+ public:
+
+ virtual const char *name() const
+ {
+ return MESSAGE_NAME;
+ }
+
+ virtual int identitySize(const unsigned char *buffer,
+ unsigned int size)
+ {
+ return (size >= MESSAGE_OFFSET ? MESSAGE_OFFSET : size);
+ }
+
+ #include MESSAGE_METHODS
+};
+
+#endif /* RenderTriangles_H */
diff --git a/nxcomp/src/Rgb.cpp b/nxcomp/src/Rgb.cpp
new file mode 100644
index 000000000..c98fa5147
--- /dev/null
+++ b/nxcomp/src/Rgb.cpp
@@ -0,0 +1,106 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Misc.h"
+#include "Rgb.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int UnpackRgb(T_geometry *geometry, unsigned char method, unsigned char *src_data,
+ int src_size, int dst_bpp, int dst_width, int dst_height,
+ unsigned char *dst_data, int dst_size)
+{
+ if (*src_data == 0)
+ {
+ if (dst_size != src_size - 1)
+ {
+ #ifdef TEST
+ *logofs << "UnpackRgb: PANIC! Invalid destination size "
+ << dst_size << " with source " << src_size
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackRgb: Expanding " << src_size - 1
+ << " bytes of plain RGB data.\n" << logofs_flush;
+ #endif
+
+ memcpy(dst_data, src_data + 1, src_size - 1);
+
+ return 1;
+ }
+
+ unsigned int check_size = dst_size;
+
+ int result = ZDecompress(&unpackStream, dst_data, &check_size,
+ src_data + 1, src_size - 1);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackRgb: PANIC! Failure decompressing RGB data. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decompressing RGB data. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+ }
+ else if (check_size != (unsigned int) dst_size)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackRgb: PANIC! Size mismatch in RGB data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size mismatch in RGB data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackRgb: Decompressed " << src_size - 1
+ << " bytes to " << dst_size << " bytes of RGB data.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/Rgb.h b/nxcomp/src/Rgb.h
new file mode 100644
index 000000000..ec088dd1b
--- /dev/null
+++ b/nxcomp/src/Rgb.h
@@ -0,0 +1,36 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Rgb_H
+#define Rgb_H
+
+#include "Unpack.h"
+
+int UnpackRgb(T_geometry *geometry, unsigned char method,
+ unsigned char *src_data, int src_size, int dst_bpp,
+ int dst_width, int dst_height, unsigned char *dst_data,
+ int dst_size);
+
+#endif /* Rgb_H */
diff --git a/nxcomp/src/Rle.cpp b/nxcomp/src/Rle.cpp
new file mode 100644
index 000000000..2a145aa0c
--- /dev/null
+++ b/nxcomp/src/Rle.cpp
@@ -0,0 +1,106 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Misc.h"
+#include "Rle.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+int UnpackRle(T_geometry *geometry, unsigned char method, unsigned char *src_data,
+ int src_size, int dst_bpp, int dst_width, int dst_height,
+ unsigned char *dst_data, int dst_size)
+{
+ if (*src_data == 0)
+ {
+ if (dst_size != src_size - 1)
+ {
+ #ifdef TEST
+ *logofs << "UnpackRle: PANIC! Invalid destination size "
+ << dst_size << " with source " << src_size
+ << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackRle: Expanding " << src_size - 1
+ << " bytes of plain RLE data.\n" << logofs_flush;
+ #endif
+
+ memcpy(dst_data, src_data + 1, src_size - 1);
+
+ return 1;
+ }
+
+ unsigned int check_size = dst_size;
+
+ int result = ZDecompress(&unpackStream, dst_data, &check_size,
+ src_data + 1, src_size - 1);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackRle: PANIC! Failure decompressing RLE data. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decompressing RLE data. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+ }
+ else if (check_size != (unsigned int) dst_size)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackRle: PANIC! Size mismatch in RLE data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Size mismatch in RLE data. "
+ << "Resulting size is " << check_size << " with "
+ << "expected size " << dst_size << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "UnpackRle: Decompressed " << src_size - 1
+ << " bytes to " << dst_size << " bytes of RLE data.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/Rle.h b/nxcomp/src/Rle.h
new file mode 100644
index 000000000..cc5dab0e8
--- /dev/null
+++ b/nxcomp/src/Rle.h
@@ -0,0 +1,36 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Rle_H
+#define Rle_H
+
+#include "Unpack.h"
+
+int UnpackRle(T_geometry *geometry, unsigned char method,
+ unsigned char *src_data, int src_size, int dst_bpp,
+ int dst_width, int dst_height, unsigned char *dst_data,
+ int dst_size);
+
+#endif /* Rle_H */
diff --git a/nxcomp/src/SendEvent.cpp b/nxcomp/src/SendEvent.cpp
new file mode 100644
index 000000000..8867d0a9e
--- /dev/null
+++ b/nxcomp/src/SendEvent.cpp
@@ -0,0 +1,304 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SendEvent.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "IntCache.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SendEventStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SendEventMessage *sendEvent = (SendEventMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ sendEvent -> propagate = *(buffer + 1);
+
+ sendEvent -> window = GetULONG(buffer + 4, bigEndian);
+ sendEvent -> mask = GetULONG(buffer + 8, bigEndian);
+
+ sendEvent -> code = *(buffer + 12);
+ sendEvent -> byte_data = *(buffer + 13);
+
+ sendEvent -> sequence = GetUINT(buffer + 14, bigEndian);
+
+ sendEvent -> int_data = GetULONG(buffer + 16, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SendEventStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SendEventMessage *sendEvent = (SendEventMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = sendEvent -> propagate;
+
+ PutULONG(sendEvent -> window, buffer + 4, bigEndian);
+ PutULONG(sendEvent -> mask, buffer + 8, bigEndian);
+
+ *(buffer + 12) = sendEvent -> code;
+ *(buffer + 13) = sendEvent -> byte_data;
+
+ PutUINT(sendEvent -> sequence, buffer + 14, bigEndian);
+
+ PutULONG(sendEvent -> int_data, buffer + 16, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SendEventStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SendEventMessage *sendEvent = (SendEventMessage *) message;
+
+ *logofs << name() << ": Identity propagate " << (unsigned int) sendEvent -> propagate
+ << ", window " << sendEvent -> window << ", mask " << sendEvent -> mask
+ << ", code " << (unsigned int) sendEvent -> code << ", byte_data "
+ << (unsigned int) sendEvent -> byte_data << ", sequence "
+ << sendEvent -> sequence << ", int_data " << sendEvent -> int_data
+ << ", size " << sendEvent -> size_ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void SendEventStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+}
+
+void SendEventStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SendEventMessage *sendEvent = (SendEventMessage *) message;
+ SendEventMessage *cachedSendEvent = (SendEventMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << (unsigned int) sendEvent -> propagate
+ << " as propagate field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeBoolValue(sendEvent -> propagate);
+
+ cachedSendEvent -> propagate = sendEvent -> propagate;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> window
+ << " as window field.\n" << logofs_flush;
+ #endif
+
+ if (sendEvent -> window == 0 || sendEvent -> window == 1)
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeBoolValue(sendEvent -> window);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ encodeBuffer.encodeXidValue(sendEvent -> window, clientCache -> windowCache);
+ }
+
+ cachedSendEvent -> window = sendEvent -> window;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> mask
+ << " as mask field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sendEvent -> mask, 32,
+ clientCache -> sendEventMaskCache);
+
+ cachedSendEvent -> mask = sendEvent -> mask;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> code
+ << " as code field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sendEvent -> code, 8,
+ clientCache -> sendEventCodeCache);
+
+ cachedSendEvent -> code = sendEvent -> code;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> byte_data
+ << " as byte_data field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sendEvent -> byte_data, 8,
+ clientCache -> sendEventByteDataCache);
+
+ cachedSendEvent -> byte_data = sendEvent -> byte_data;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> sequence
+ << " as sequence field.\n" << logofs_flush;
+ #endif
+
+ unsigned int diffSeq = sendEvent -> sequence -
+ clientCache -> sendEventLastSequence;
+
+ clientCache -> sendEventLastSequence = sendEvent -> sequence;
+
+ encodeBuffer.encodeValue(diffSeq, 16, 4);
+
+ cachedSendEvent -> sequence = sendEvent -> sequence;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << sendEvent -> int_data
+ << " as int_data field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sendEvent -> int_data, 32,
+ clientCache -> sendEventIntDataCache);
+
+ cachedSendEvent -> int_data = sendEvent -> int_data;
+}
+
+void SendEventStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SendEventMessage *sendEvent = (SendEventMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ sendEvent -> propagate = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << (unsigned int) sendEvent -> propagate
+ << " as propagate field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ }
+ else
+ {
+ decodeBuffer.decodeXidValue(value, clientCache -> windowCache);
+ }
+
+ sendEvent -> window = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> window
+ << " as window field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(sendEvent -> mask, 32,
+ clientCache -> sendEventMaskCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> mask
+ << " as mask field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(sendEvent -> code, 8,
+ clientCache -> sendEventCodeCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> code
+ << " as code field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(sendEvent -> byte_data, 8,
+ clientCache -> sendEventByteDataCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> byte_data
+ << " as byte_data field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeValue(value, 16, 4);
+
+ clientCache -> sendEventLastSequence += value;
+ clientCache -> sendEventLastSequence &= 0xffff;
+
+ sendEvent -> sequence = clientCache -> sendEventLastSequence;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> sequence
+ << " as sequence field.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(sendEvent -> int_data, 32,
+ clientCache -> sendEventIntDataCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << sendEvent -> int_data
+ << " as int_data field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/SendEvent.h b/nxcomp/src/SendEvent.h
new file mode 100644
index 000000000..a8841a706
--- /dev/null
+++ b/nxcomp/src/SendEvent.h
@@ -0,0 +1,195 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef SendEvent_H
+#define SendEvent_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SENDEVENT_ENABLE_CACHE 1
+#define SENDEVENT_ENABLE_DATA 0
+#define SENDEVENT_ENABLE_SPLIT 0
+#define SENDEVENT_ENABLE_COMPRESS 0
+
+#define SENDEVENT_DATA_LIMIT 24
+#define SENDEVENT_DATA_OFFSET 20
+
+#define SENDEVENT_CACHE_SLOTS 2000
+#define SENDEVENT_CACHE_THRESHOLD 2
+#define SENDEVENT_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class SendEventMessage : public Message
+{
+ friend class SendEventStore;
+
+ public:
+
+ SendEventMessage()
+ {
+ }
+
+ ~SendEventMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char propagate;
+ unsigned int window;
+ unsigned int mask;
+
+ //
+ // These are part of the event data.
+ //
+
+ unsigned char code;
+ unsigned char byte_data;
+ unsigned short sequence;
+ unsigned int int_data;
+};
+
+class SendEventStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ SendEventStore() : MessageStore()
+ {
+ enableCache = SENDEVENT_ENABLE_CACHE;
+ enableData = SENDEVENT_ENABLE_DATA;
+ enableSplit = SENDEVENT_ENABLE_SPLIT;
+ enableCompress = SENDEVENT_ENABLE_COMPRESS;
+
+ dataLimit = SENDEVENT_DATA_LIMIT;
+ dataOffset = SENDEVENT_DATA_OFFSET;
+
+ cacheSlots = SENDEVENT_CACHE_SLOTS;
+ cacheThreshold = SENDEVENT_CACHE_THRESHOLD;
+ cacheLowerThreshold = SENDEVENT_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~SendEventStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "SendEvent";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_SendEvent;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SendEventMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new SendEventMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SendEventMessage((const SendEventMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SendEventMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SendEvent_H */
diff --git a/nxcomp/src/SequenceQueue.cpp b/nxcomp/src/SequenceQueue.cpp
new file mode 100644
index 000000000..a045875d7
--- /dev/null
+++ b/nxcomp/src/SequenceQueue.cpp
@@ -0,0 +1,174 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SequenceQueue.h"
+
+static const unsigned int INITIAL_SIZE_ = 16;
+static const unsigned int GROWTH_INCREMENT = 16;
+
+SequenceQueue::SequenceQueue()
+
+ : queue_(new RequestSequence[INITIAL_SIZE_]), size_(INITIAL_SIZE_),
+ length_(0), start_(0), end_(0)
+{
+}
+
+SequenceQueue::~SequenceQueue()
+{
+ delete [] queue_;
+}
+
+void SequenceQueue::push(unsigned short int sequence, unsigned char opcode,
+ unsigned int data1, unsigned int data2,
+ unsigned int data3)
+{
+ if (length_ == 0)
+ {
+ start_ = end_ = 0;
+
+ queue_[0].opcode = opcode;
+ queue_[0].sequence = sequence;
+
+ queue_[0].data1 = data1;
+ queue_[0].data2 = data2;
+ queue_[0].data3 = data3;
+
+ length_ = 1;
+
+ return;
+ }
+
+ if (length_ == size_)
+ {
+ size_ += GROWTH_INCREMENT;
+
+ RequestSequence *newQueue = new RequestSequence[size_];
+
+ for (int i = start_; (unsigned int) i < length_; i++)
+ {
+ newQueue[i - start_] = queue_[i];
+ }
+
+ for (int i1 = 0; (unsigned int) i1 < start_; i1++)
+ {
+ newQueue[i1 + length_ - start_] = queue_[i1];
+ }
+
+ delete [] queue_;
+
+ queue_ = newQueue;
+
+ start_ = 0;
+
+ end_ = length_ - 1;
+ }
+
+ end_++;
+
+ if (end_ == size_)
+ {
+ end_ = 0;
+ }
+
+ queue_[end_].opcode = opcode;
+ queue_[end_].sequence = sequence;
+
+ queue_[end_].data1 = data1;
+ queue_[end_].data2 = data2;
+ queue_[end_].data3 = data3;
+
+ length_++;
+}
+
+int SequenceQueue::peek(unsigned short int &sequence,
+ unsigned char &opcode)
+{
+ if (length_ == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ opcode = queue_[start_].opcode;
+ sequence = queue_[start_].sequence;
+
+ return 1;
+ }
+}
+
+int SequenceQueue::peek(unsigned short int &sequence, unsigned char &opcode,
+ unsigned int &data1, unsigned int &data2,
+ unsigned int &data3)
+{
+ if (length_ == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ opcode = queue_[start_].opcode;
+ sequence = queue_[start_].sequence;
+
+ data1 = queue_[start_].data1;
+ data2 = queue_[start_].data2;
+ data3 = queue_[start_].data3;
+
+ return 1;
+ }
+}
+
+int SequenceQueue::pop(unsigned short int &sequence, unsigned char &opcode,
+ unsigned int &data1, unsigned int &data2,
+ unsigned int &data3)
+{
+ if (length_ == 0)
+ {
+ return 0;
+ }
+ else
+ {
+ opcode = queue_[start_].opcode;
+ sequence = queue_[start_].sequence;
+
+ data1 = queue_[start_].data1;
+ data2 = queue_[start_].data2;
+ data3 = queue_[start_].data3;
+
+ start_++;
+
+ if (start_ == size_)
+ {
+ start_ = 0;
+ }
+
+ length_--;
+
+ return 1;
+ }
+}
diff --git a/nxcomp/src/SequenceQueue.h b/nxcomp/src/SequenceQueue.h
new file mode 100644
index 000000000..2a72bc3fe
--- /dev/null
+++ b/nxcomp/src/SequenceQueue.h
@@ -0,0 +1,91 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef SequenceQueue_H
+#define SequenceQueue_H
+
+//
+// List of outstanding request messages which
+// are waiting for a reply. This class is used
+// in X client and server channels to correlate
+// the replies sequence numbers to the original
+// request type.
+//
+
+class SequenceQueue
+{
+ public:
+
+ SequenceQueue();
+
+ virtual ~SequenceQueue();
+
+ void push(unsigned short int sequence, unsigned char opcode,
+ unsigned int data1 = 0, unsigned int data2 = 0,
+ unsigned int data3 = 0);
+
+ int peek(unsigned short int &sequence, unsigned char &opcode);
+
+ int peek(unsigned short int &sequence, unsigned char &opcode,
+ unsigned int &data1, unsigned int &data2,
+ unsigned int &data3);
+
+ int pop(unsigned short int &sequence, unsigned char &opcode,
+ unsigned int &data1, unsigned int &data2,
+ unsigned int &data3);
+
+ int pop(unsigned short int &sequence, unsigned char &opcode)
+ {
+ unsigned int data1, data2, data3;
+
+ return pop(sequence, opcode, data1, data2, data3);
+ }
+
+ int length()
+ {
+ return length_;
+ }
+
+ private:
+
+ struct RequestSequence
+ {
+ unsigned short int sequence;
+ unsigned char opcode;
+ unsigned int data1;
+ unsigned int data2;
+ unsigned int data3;
+ };
+
+ RequestSequence *queue_;
+
+ unsigned int size_;
+ unsigned int length_;
+
+ unsigned int start_;
+ unsigned int end_;
+};
+
+#endif /* SequenceQueue_H */
diff --git a/nxcomp/src/ServerCache.cpp b/nxcomp/src/ServerCache.cpp
new file mode 100644
index 000000000..c4c088dca
--- /dev/null
+++ b/nxcomp/src/ServerCache.cpp
@@ -0,0 +1,195 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ServerCache.h"
+
+//
+// Some global caches used to store information
+// common to all X connections.
+//
+
+BlockCache ServerCache::lastInitReply;
+BlockCache ServerCache::lastKeymap;
+unsigned char ServerCache::getKeyboardMappingLastKeysymsPerKeycode = 0;
+BlockCache ServerCache::getKeyboardMappingLastMap;
+BlockCache ServerCache::getModifierMappingLastMap;
+BlockCache ServerCache::xResources;
+BlockCacheSet ServerCache::queryFontFontCache(16);
+
+ServerCache::ServerCache() :
+
+ replySequenceCache(6), eventSequenceCache(6),
+ lastTimestamp(0), visualCache(8), colormapCache(8),
+
+ errorMinorCache(8),
+
+ colormapNotifyWindowCache(8), colormapNotifyColormapCache(8),
+
+ createNotifyWindowCache(8), createNotifyLastWindow(0),
+
+ exposeWindowCache(12),
+
+ focusInWindowCache(8),
+
+ keyPressLastKey(0),
+
+ mapNotifyEventCache(8), mapNotifyWindowCache(8),
+
+ motionNotifyTimestampCache(8), motionNotifyLastRootX(0),
+ motionNotifyLastRootY(0), motionNotifyRootXCache(8),
+ motionNotifyRootYCache(8), motionNotifyEventXCache(8),
+ motionNotifyEventYCache(8), motionNotifyStateCache(8),
+
+ noExposeDrawableCache(8), noExposeMinorCache(8),
+
+ propertyNotifyWindowCache(8), propertyNotifyAtomCache(8),
+
+ reparentNotifyWindowCache(8),
+
+ selectionClearWindowCache(8), selectionClearAtomCache(8),
+
+ visibilityNotifyWindowCache(8),
+
+ getGeometryRootCache(8),
+
+ getInputFocusWindowCache(8),
+
+ getKeyboardMappingKeysymCache(8),
+
+ getPropertyTypeCache(8),
+
+ getSelectionOwnerCache(8),
+
+ getWindowAttributesClassCache(8), getWindowAttributesPlanesCache(8),
+ getWindowAttributesPixelCache(8), getWindowAttributesAllEventsCache(8),
+ getWindowAttributesYourEventsCache(8),
+ getWindowAttributesDontPropagateCache(8),
+
+ queryPointerRootCache(8), queryPointerChildCache(8),
+
+ translateCoordsChildCache(8), translateCoordsXCache(8),
+ translateCoordsYCache(8),
+
+ queryTreeWindowCache(8)
+
+{
+ unsigned int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ configureNotifyWindowCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ configureNotifyGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ exposeGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ motionNotifyWindowCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ getGeometryGeomCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 23; i++)
+ {
+ keyPressCache[i] = 0;
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ queryFontCharInfoCache[i] = new IntCache(8);
+ queryFontLastCharInfo[i] = 0;
+ }
+
+ for (i = 0; i < 12; i++)
+ {
+ genericReplyIntCache[i] = new IntCache(8);
+ }
+
+ for (i = 0; i < 14; i++)
+ {
+ genericEventIntCache[i] = new IntCache(8);
+ }
+}
+
+
+ServerCache::~ServerCache()
+{
+ unsigned int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ delete configureNotifyWindowCache[i];
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ delete configureNotifyGeomCache[i];
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ delete exposeGeomCache[i];
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ delete motionNotifyWindowCache[i];
+ }
+
+ for (i = 0; i < 5; i++)
+ {
+ delete getGeometryGeomCache[i];
+ }
+
+ for (i = 0; i < 6; i++)
+ {
+ delete queryFontCharInfoCache[i];
+ }
+
+ for (i = 0; i < 12; i++)
+ {
+ delete genericReplyIntCache[i];
+ }
+
+ for (i = 0; i < 14; i++)
+ {
+ delete genericEventIntCache[i];
+ }
+}
diff --git a/nxcomp/src/ServerCache.h b/nxcomp/src/ServerCache.h
new file mode 100644
index 000000000..c6e2b81b6
--- /dev/null
+++ b/nxcomp/src/ServerCache.h
@@ -0,0 +1,303 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ServerCache_H
+#define ServerCache_H
+
+#include "Misc.h"
+
+#include "IntCache.h"
+#include "CharCache.h"
+#include "OpcodeCache.h"
+#include "BlockCache.h"
+#include "BlockCacheSet.h"
+
+#include "ChannelCache.h"
+
+class ServerCache : public ChannelCache
+{
+ public:
+
+ ServerCache();
+
+ ~ServerCache();
+
+ //
+ // Opcode prediction caches.
+ //
+
+ OpcodeCache opcodeCache;
+
+ //
+ // General-purpose caches.
+ //
+
+ IntCache replySequenceCache;
+ IntCache eventSequenceCache;
+ unsigned int lastTimestamp;
+ CharCache depthCache;
+ IntCache visualCache;
+ IntCache colormapCache;
+ CharCache resourceCache;
+
+ //
+ // X connection startup.
+ //
+
+ static BlockCache lastInitReply;
+
+ //
+ // X errors.
+ //
+
+ CharCache errorCodeCache;
+ IntCache errorMinorCache;
+ CharCache errorMajorCache;
+
+ //
+ // ButtonPress and ButtonRelease events.
+ //
+
+ CharCache buttonCache;
+
+ //
+ // ColormapNotify event.
+ //
+
+ IntCache colormapNotifyWindowCache;
+ IntCache colormapNotifyColormapCache;
+
+ //
+ // ConfigureNotify event.
+ //
+
+ IntCache *configureNotifyWindowCache[3];
+ IntCache *configureNotifyGeomCache[5];
+
+ //
+ // CreateNotify event.
+ //
+
+ IntCache createNotifyWindowCache;
+ unsigned int createNotifyLastWindow;
+
+ //
+ // Expose event.
+ //
+
+ IntCache exposeWindowCache;
+ IntCache *exposeGeomCache[5];
+
+ //
+ // FocusIn event (also used for FocusOut).
+ //
+
+ IntCache focusInWindowCache;
+
+ //
+ // KeymapNotify event.
+ //
+
+ static BlockCache lastKeymap;
+
+ //
+ // KeyPress event.
+ //
+
+ unsigned char keyPressLastKey;
+ unsigned char keyPressCache[23];
+
+ //
+ // MapNotify event (also used for UnmapNotify).
+ //
+
+ IntCache mapNotifyEventCache;
+ IntCache mapNotifyWindowCache;
+
+ //
+ // MotionNotify event (also used for KeyPress,
+ // KeyRelease, ButtonPress, ButtonRelease,
+ // EnterNotify, and LeaveNotify events and
+ // QueryPointer reply).
+ //
+
+ IntCache motionNotifyTimestampCache;
+ unsigned int motionNotifyLastRootX;
+ unsigned int motionNotifyLastRootY;
+ IntCache motionNotifyRootXCache;
+ IntCache motionNotifyRootYCache;
+ IntCache motionNotifyEventXCache;
+ IntCache motionNotifyEventYCache;
+ IntCache motionNotifyStateCache;
+ IntCache *motionNotifyWindowCache[3];
+
+ //
+ // NoExpose event.
+ //
+
+ IntCache noExposeDrawableCache;
+ IntCache noExposeMinorCache;
+ CharCache noExposeMajorCache;
+
+ //
+ // PropertyNotify event.
+ //
+
+ IntCache propertyNotifyWindowCache;
+ IntCache propertyNotifyAtomCache;
+
+ //
+ // ReparentNotify event.
+ //
+
+ IntCache reparentNotifyWindowCache;
+
+ //
+ // SelectionClear event.
+ //
+
+ IntCache selectionClearWindowCache;
+ IntCache selectionClearAtomCache;
+
+ //
+ // VisibilityNotify event.
+ //
+
+ IntCache visibilityNotifyWindowCache;
+
+ //
+ // GetGeometry reply.
+ //
+
+ IntCache getGeometryRootCache;
+ IntCache *getGeometryGeomCache[5];
+
+ //
+ // GetInputFocus reply.
+ //
+
+ IntCache getInputFocusWindowCache;
+
+ //
+ // GetKeyboardMapping reply.
+ //
+
+ static unsigned char getKeyboardMappingLastKeysymsPerKeycode;
+ static BlockCache getKeyboardMappingLastMap;
+ IntCache getKeyboardMappingKeysymCache;
+ CharCache getKeyboardMappingLastByteCache;
+
+ //
+ // GetModifierMapping reply.
+ //
+
+ static BlockCache getModifierMappingLastMap;
+
+ //
+ // GetProperty reply.
+ //
+
+ CharCache getPropertyFormatCache;
+ IntCache getPropertyTypeCache;
+ static BlockCache xResources;
+
+ //
+ // GetSelection reply.
+ //
+
+ IntCache getSelectionOwnerCache;
+
+ //
+ // GetWindowAttributes reply.
+ //
+
+ IntCache getWindowAttributesClassCache;
+ CharCache getWindowAttributesBitGravityCache;
+ CharCache getWindowAttributesWinGravityCache;
+ IntCache getWindowAttributesPlanesCache;
+ IntCache getWindowAttributesPixelCache;
+ IntCache getWindowAttributesAllEventsCache;
+ IntCache getWindowAttributesYourEventsCache;
+ IntCache getWindowAttributesDontPropagateCache;
+
+ //
+ // QueryColors reply.
+ //
+
+ BlockCache queryColorsLastReply;
+
+ //
+ // QueryFont reply.
+ //
+
+ static BlockCacheSet queryFontFontCache;
+ IntCache *queryFontCharInfoCache[6];
+ unsigned int queryFontLastCharInfo[6];
+
+ //
+ // QueryPointer reply.
+ //
+
+ IntCache queryPointerRootCache;
+ IntCache queryPointerChildCache;
+
+ //
+ // TranslateCoords reply.
+ //
+
+ IntCache translateCoordsChildCache;
+ IntCache translateCoordsXCache;
+ IntCache translateCoordsYCache;
+
+ //
+ // QueryTree reply.
+ //
+
+ IntCache queryTreeWindowCache;
+
+ //
+ // Generic reply. Use short data
+ // in protocol versions >= 3.
+ //
+
+ CharCache genericReplyCharCache;
+ IntCache *genericReplyIntCache[12];
+
+ //
+ // Generic event. Only in protocol
+ // versions >= 3.
+ //
+
+ CharCache genericEventCharCache;
+ IntCache *genericEventIntCache[14];
+
+ //
+ // Used in the abort split events.
+ //
+
+ OpcodeCache abortOpcodeCache;
+};
+
+#endif /* ServerCache_H */
diff --git a/nxcomp/src/ServerChannel.cpp b/nxcomp/src/ServerChannel.cpp
new file mode 100644
index 000000000..830985295
--- /dev/null
+++ b/nxcomp/src/ServerChannel.cpp
@@ -0,0 +1,7946 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#ifndef ANDROID
+#include <sys/shm.h>
+#endif
+
+#include <X11/X.h>
+#include <X11/Xatom.h>
+
+#include "NXproto.h"
+#include "NXalert.h"
+#include "NXpack.h"
+#include "NXmitshm.h"
+
+#include "ServerChannel.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "StaticCompressor.h"
+
+#include "Statistics.h"
+#include "Proxy.h"
+
+#include "Auth.h"
+#include "Unpack.h"
+
+//
+// Available unpack methods.
+//
+
+#include "Alpha.h"
+#include "Colormap.h"
+#include "Bitmap.h"
+#include "Jpeg.h"
+#include "Pgn.h"
+#include "Rgb.h"
+#include "Rle.h"
+
+extern Proxy *proxy;
+
+//
+// Set the verbosity level. You also
+// need to define OPCODES in Misc.cpp
+// if you want literals instead of
+// opcodes' numbers.
+//
+
+#define PANIC
+#define WARNING
+#undef OPCODES
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Log the important tracepoints related
+// to writing packets to the peer proxy.
+//
+
+#undef FLUSH
+
+//
+// Log the operations related to splits.
+//
+
+#undef SPLIT
+
+//
+// Define this to log when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Define this to exit and suspend the
+// session after a given number of X
+// messages decoded by the proxy.
+//
+
+#undef SUSPEND
+
+//
+// Define these to hide the server extensions.
+//
+
+#define HIDE_MIT_SHM_EXTENSION
+#define HIDE_BIG_REQUESTS_EXTENSION
+#define HIDE_XFree86_Bigfont_EXTENSION
+#undef HIDE_SHAPE_EXTENSION
+#undef HIDE_XKEYBOARD_EXTENSION
+
+//
+// Known reasons of connection failures.
+//
+
+#define INVALID_COOKIE_DATA "Invalid MIT-MAGIC-COOKIE-1 key"
+#define INVALID_COOKIE_SIZE ((int) sizeof(INVALID_COOKIE_DATA) - 1)
+
+#define NO_AUTH_PROTO_DATA "No protocol specified"
+#define NO_AUTH_PROTO_SIZE ((int) sizeof(NO_AUTH_PROTO_DATA) - 1)
+
+//
+// Here are the static members.
+//
+
+#ifdef REFERENCES
+
+int ServerChannel::references_ = 0;
+
+#endif
+
+ServerChannel::ServerChannel(Transport *transport, StaticCompressor *compressor)
+
+ : Channel(transport, compressor), readBuffer_(transport_, this)
+{
+ //
+ // Sequence number of the next message
+ // being encoded or decoded.
+ //
+
+ clientSequence_ = 0;
+ serverSequence_ = 0;
+
+ //
+ // Save the last motion event and flush
+ // it only when the timeout expires.
+ //
+
+ lastMotion_[0] = '\0';
+
+ //
+ // Clear the queue of sequence numbers
+ // of split commits. Used to mask the
+ // errors.
+ //
+
+ initCommitQueue();
+
+ //
+ // Do we enable or not sending of expose
+ // events to the X client.
+ //
+
+ enableExpose_ = 1;
+ enableGraphicsExpose_ = 1;
+ enableNoExpose_ = 1;
+
+ //
+ // Track data of image currently being
+ // decompressed.
+ //
+
+ imageState_ = NULL;
+
+ //
+ // Track MIT-SHM resources.
+ //
+
+ shmemState_ = NULL;
+
+ //
+ // Store the unpack state for each agent
+ // resource.
+ //
+
+ for (int i = 0; i < CONNECTIONS_LIMIT; i++)
+ {
+ unpackState_[i] = NULL;
+ }
+
+ //
+ // Data about the split parameters requested
+ // by the encoding side.
+ //
+
+ splitState_.resource = nothing;
+ splitState_.current = 0;
+ splitState_.save = 1;
+ splitState_.load = 1;
+ splitState_.commit = 0;
+
+ //
+ // It will be eventually set by
+ // the server proxy.
+ //
+
+ fontPort_ = -1;
+
+ #ifdef REFERENCES
+ *logofs << "ServerChannel: Created new object at "
+ << this << " for FD#" << fd_ << " out of "
+ << ++references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+ServerChannel::~ServerChannel()
+{
+ #ifdef TEST
+ *logofs << "ServerChannel: Freeing image state information.\n"
+ << logofs_flush;
+ #endif
+
+ handleImageStateRemove();
+
+ #ifdef TEST
+ *logofs << "ServerChannel: Freeing shared memory information.\n"
+ << logofs_flush;
+ #endif
+
+ handleShmemStateRemove();
+
+ #ifdef TEST
+ *logofs << "ServerChannel: Freeing unpack state information.\n"
+ << logofs_flush;
+ #endif
+
+ for (int i = 0; i < CONNECTIONS_LIMIT; i++)
+ {
+ handleUnpackStateRemove(i);
+ }
+
+ #ifdef TEST
+ *logofs << "ServerChannel: Freeing channel caches.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef REFERENCES
+ *logofs << "ServerChannel: Deleted object at "
+ << this << " for FD#" << fd_ << " out of "
+ << --references_ << " allocated channels.\n"
+ << logofs_flush;
+ #endif
+}
+
+//
+// Beginning of handleRead().
+//
+
+int ServerChannel::handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length)
+{
+ #ifdef DEBUG
+ *logofs << "handleRead: Called for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Pointer to located message and
+ // its size in bytes.
+ //
+
+ const unsigned char *inputMessage;
+ unsigned int inputLength;
+
+ //
+ // Set when message is found in
+ // cache.
+ //
+
+ int hit;
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: Trying to read from FD#"
+ << fd_ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = readBuffer_.readMessage();
+
+ #if defined(DEBUG) || defined(INFO)
+ *logofs << "handleRead: Read result on FD#" << fd_
+ << " is " << result << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (result < 0)
+ {
+ //
+ // Let the proxy close the channel.
+ //
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+
+ //
+ // This can happen because we have the descriptor
+ // selected in the read set but we already read
+ // the data asynchronously, while decoding data
+ // read from the proxy.
+ //
+
+ *logofs << "handleRead: WARNING! No data read from FD#"
+ << fd_ << " while encoding messages.\n"
+ << logofs_flush;
+
+ #endif
+
+ return 0;
+ }
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleRead: Encoding messages for FD#" << fd_
+ << " with " << readBuffer_.getLength() << " bytes "
+ << "in the buffer.\n" << logofs_flush;
+ #endif
+
+ //
+ // Extract any complete message which
+ // is available in the buffer.
+ //
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ while ((inputMessage = readBuffer_.getMessage(inputLength)) != NULL)
+ {
+ hit = 0;
+
+ if (firstReply_)
+ {
+ //
+ // Handle the X server's authorization reply.
+ //
+
+ if (handleAuthorization(inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+
+ imageByteOrder_ = inputMessage[30];
+ bitmapBitOrder_ = inputMessage[31];
+ scanlineUnit_ = inputMessage[32];
+ scanlinePad_ = inputMessage[33];
+
+ encodeBuffer.encodeValue((unsigned int) inputMessage[0], 8);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 8);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 2, bigEndian_), 16);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 4, bigEndian_), 16);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 6, bigEndian_), 16);
+
+ if (ServerCache::lastInitReply.compare(inputLength - 8, inputMessage + 8))
+ {
+ encodeBuffer.encodeBoolValue(1);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ for (unsigned int i = 8; i < inputLength; i++)
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8);
+ }
+ }
+
+ firstReply_ = 0;
+
+ #if defined(TEST) || defined(OPCODES)
+
+ int bits = encodeBuffer.diffBits();
+
+ *logofs << "handleRead: Handled first reply. " << inputLength
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+
+ #endif
+
+ priority_++;
+
+ }
+ else
+ {
+ //
+ // NX client needs this line to consider
+ // the initialization phase successfully
+ // completed.
+ //
+
+ if (firstClient_ == -1)
+ {
+ cerr << "Info" << ": Established X server connection.\n" ;
+
+ firstClient_ = fd_;
+ }
+
+ //
+ // Check if this is a reply.
+ //
+
+ if (*inputMessage == X_Reply)
+ {
+ int bits = 0;
+
+ unsigned char inputOpcode = *inputMessage;
+
+ unsigned short int requestSequenceNum;
+ unsigned char requestOpcode;
+ unsigned int requestData[3];
+
+ unsigned int sequenceNum = GetUINT(inputMessage + 2, bigEndian_);
+
+ #ifdef SUSPEND
+
+ if (sequenceNum >= 1000)
+ {
+ cerr << "Warning" << ": Exiting to test the resilience of the agent.\n";
+
+ sleep(2);
+
+ HandleAbort();
+ }
+
+ #endif
+
+ //
+ // We managed all the events and errors caused
+ // by the previous requests. We can now reset
+ // the queue of split commits.
+ //
+
+ clearCommitQueue();
+
+ //
+ // Encode opcode and difference between
+ // current sequence and the last one.
+ //
+
+ encodeBuffer.encodeOpcodeValue(inputOpcode, serverCache_ -> opcodeCache);
+
+ unsigned int sequenceDiff = sequenceNum - serverSequence_;
+
+ serverSequence_ = sequenceNum;
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Last server sequence number for FD#"
+ << fd_ << " is " << serverSequence_ << " with "
+ << "difference " << sequenceDiff << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sequenceDiff, 16,
+ serverCache_ -> replySequenceCache, 7);
+
+ //
+ // Now handle the data part.
+ //
+
+ if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) &&
+ requestSequenceNum == sequenceNum)
+ {
+ //
+ // We've found the request that generated this reply.
+ // It is possible to compress the reply based on the
+ // specific request type.
+ //
+
+ sequenceQueue_.pop(requestSequenceNum, requestOpcode,
+ requestData[0], requestData[1], requestData[2]);
+
+ //
+ // If differential compression is disabled
+ // then use the most simple encoding.
+ //
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ int _result = handleFastReadReply(encodeBuffer, requestOpcode,
+ inputMessage, inputLength);
+ if (_result < 0)
+ {
+ return -1;
+ }
+ else if (_result > 0)
+ {
+ continue;
+ }
+ }
+
+ switch (requestOpcode)
+ {
+ case X_AllocColor:
+ {
+ const unsigned char *nextSrc = inputMessage + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ unsigned int colorValue = GetUINT(nextSrc, bigEndian_);
+ nextSrc += 2;
+ if (colorValue == requestData[i])
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue(colorValue - colorValue, 16, 6);
+ }
+ }
+ unsigned int pixel = GetULONG(inputMessage + 16, bigEndian_);
+ encodeBuffer.encodeValue(pixel, 32, 9);
+
+ priority_++;
+ }
+ break;
+ case X_GetAtomName:
+ {
+ unsigned int nameLength = GetUINT(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(nameLength, 16, 6);
+ const unsigned char *nextSrc = inputMessage + 32;
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, nameLength);
+
+ priority_++;
+ }
+ break;
+ case X_GetGeometry:
+ {
+ //
+ // TODO: This obtains a satisfactory 10:1, but
+ // could be cached to leverage the big amount
+ // of such requests issued by QT clients.
+ //
+
+ encodeBuffer.encodeCachedValue(inputMessage[1], 8,
+ serverCache_ -> depthCache);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> getGeometryRootCache, 9);
+ const unsigned char *nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *serverCache_ -> getGeometryGeomCache[i], 8);
+ nextSrc += 2;
+ }
+
+ priority_++;
+ }
+ break;
+ case X_GetInputFocus:
+ {
+ //
+ // Is it a real X_GetInputFocus or a
+ // masqueraded reply?
+ //
+
+ if (requestData[0] == X_GetInputFocus)
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> getInputFocusWindowCache);
+
+ priority_++;
+ }
+ else
+ {
+ //
+ // TODO: We are not setting priority in case
+ // of replies other than real X_GetInputFocus
+ // or X_NXGetUnpackParameters. We should check
+ // once again that this is OK.
+ //
+
+ #ifdef TEST
+ *logofs << "handleRead: Received tainted X_GetInputFocus reply "
+ << "for request OPCODE#" << requestData[0] << " with "
+ << "sequence " << sequenceNum << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Don't encode any data in case of sync
+ // messages or any other reply for which
+ // opcode is enough.
+ //
+
+ if (requestData[0] == opcodeStore_ -> getUnpackParameters)
+ {
+ for (int i = 0; i < PACK_METHOD_LIMIT; i++)
+ {
+ encodeBuffer.encodeBoolValue(control -> LocalUnpackMethods[i]);
+ }
+
+ priority_++;
+ }
+ else if (requestData[0] == opcodeStore_ -> getShmemParameters)
+ {
+ if (handleShmemReply(encodeBuffer, requestOpcode, requestData[1],
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+
+ priority_++;
+ }
+ else if (requestData[0] == opcodeStore_ -> getFontParameters)
+ {
+ if (handleFontReply(encodeBuffer, requestOpcode,
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // Account this data to the original opcode.
+ //
+
+ requestOpcode = requestData[0];
+ }
+ }
+ break;
+ case X_GetKeyboardMapping:
+ {
+ unsigned int keysymsPerKeycode = (unsigned int) inputMessage[1];
+ if (ServerCache::getKeyboardMappingLastMap.compare(inputLength - 32,
+ inputMessage + 32) && (keysymsPerKeycode ==
+ ServerCache::getKeyboardMappingLastKeysymsPerKeycode))
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ priority_++;
+
+ break;
+ }
+ ServerCache::getKeyboardMappingLastKeysymsPerKeycode = keysymsPerKeycode;
+ encodeBuffer.encodeBoolValue(0);
+ unsigned int numKeycodes =
+ (((inputLength - 32) / keysymsPerKeycode) >> 2);
+ encodeBuffer.encodeValue(numKeycodes, 8);
+ encodeBuffer.encodeValue(keysymsPerKeycode, 8, 4);
+ const unsigned char *nextSrc = inputMessage + 32;
+ unsigned char previous = 0;
+ for (unsigned int count = numKeycodes * keysymsPerKeycode;
+ count; --count)
+ {
+ unsigned int keysym = GetULONG(nextSrc, bigEndian_);
+ nextSrc += 4;
+ if (keysym == NoSymbol)
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ unsigned int first3Bytes = (keysym >> 8);
+ encodeBuffer.encodeCachedValue(first3Bytes, 24,
+ serverCache_ -> getKeyboardMappingKeysymCache, 9);
+ unsigned char lastByte = (unsigned char) (keysym & 0xff);
+ encodeBuffer.encodeCachedValue(lastByte - previous, 8,
+ serverCache_ -> getKeyboardMappingLastByteCache, 5);
+ previous = lastByte;
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_GetModifierMapping:
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 8);
+ const unsigned char *nextDest = inputMessage + 32;
+ if (ServerCache::getModifierMappingLastMap.compare(inputLength - 32,
+ nextDest))
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ priority_++;
+
+ break;
+ }
+ encodeBuffer.encodeBoolValue(0);
+ for (unsigned int count = inputLength - 32; count; count--)
+ {
+ unsigned char next = *nextDest++;
+ if (next == 0)
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue(next, 8);
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_GetProperty:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_GetProperty);
+
+ hit = handleEncode(encodeBuffer, serverCache_, messageStore,
+ requestOpcode, inputMessage, inputLength);
+
+ priority_++;
+ }
+ break;
+ case X_GetSelectionOwner:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> getSelectionOwnerCache, 9);
+ priority_++;
+ }
+ break;
+ case X_GetWindowAttributes:
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 2);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> visualCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_),
+ 16, serverCache_ -> getWindowAttributesClassCache, 3);
+ encodeBuffer.encodeCachedValue(inputMessage[14], 8,
+ serverCache_ -> getWindowAttributesBitGravityCache);
+ encodeBuffer.encodeCachedValue(inputMessage[15], 8,
+ serverCache_ -> getWindowAttributesWinGravityCache);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_),
+ 32, serverCache_ -> getWindowAttributesPlanesCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 20, bigEndian_),
+ 32, serverCache_ -> getWindowAttributesPixelCache, 9);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[24]);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[25]);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[26], 2);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[27]);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 28, bigEndian_),
+ 29, serverCache_ -> colormapCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 32, bigEndian_),
+ 32, serverCache_ -> getWindowAttributesAllEventsCache);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 36, bigEndian_),
+ 32, serverCache_ -> getWindowAttributesYourEventsCache);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 40, bigEndian_),
+ 16, serverCache_ -> getWindowAttributesDontPropagateCache);
+
+ priority_++;
+ }
+ break;
+ case X_GrabKeyboard:
+ case X_GrabPointer:
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 3);
+
+ priority_++;
+ }
+ break;
+ case X_InternAtom:
+ {
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 8, bigEndian_), 29, 9);
+
+ priority_++;
+ }
+ break;
+ case X_ListExtensions:
+ {
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8);
+ unsigned int numExtensions = (unsigned int) inputMessage[1];
+ encodeBuffer.encodeValue(numExtensions, 8);
+ const unsigned char *nextSrc = inputMessage + 32;
+
+ for (; numExtensions; numExtensions--)
+ {
+ unsigned int _length = (unsigned int) (*nextSrc++);
+
+ encodeBuffer.encodeValue(_length, 8);
+
+ #ifdef HIDE_MIT_SHM_EXTENSION
+
+ if (!strncmp((char *) nextSrc, "MIT-SHM", 7))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding MIT-SHM extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-MIT-", 7);
+ }
+
+ #endif
+
+ #ifdef HIDE_BIG_REQUESTS_EXTENSION
+
+ if (!strncmp((char *) nextSrc, "BIG-REQUESTS", 12))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding BIG-REQUESTS extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-BIG-REQUE", 12);
+ }
+
+ #endif
+
+ #ifdef HIDE_XKEYBOARD_EXTENSION
+
+ if (!strncmp((char *) nextSrc, "XKEYBOARD", 9))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding XKEYBOARD extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-XKEYBO", 9);
+ }
+
+ #endif
+
+ #ifdef HIDE_XFree86_Bigfont_EXTENSION
+
+ if (!strncmp((char *) nextSrc, "XFree86-Bigfont", 15))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding XFree86-Bigfont extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-XFree86-Bigf", 15);
+ }
+
+ #endif
+
+ #ifdef HIDE_SHAPE_EXTENSION
+
+ if (!strncmp((char *) nextSrc, "SHAPE", 5))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding SHAPE extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-SH", 5);
+ }
+
+ #endif
+
+ //
+ // Check if user disabled RENDER extension.
+ //
+
+ if (control -> HideRender == 1 &&
+ !strncmp((char *) nextSrc, "RENDER", 6))
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Hiding RENDER extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) nextSrc, "NO-REN", 6);
+ }
+
+ for (; length; length--)
+ {
+ encodeBuffer.encodeValue((unsigned int) (*nextSrc++), 8);
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_ListFonts:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_ListFonts);
+
+ if (handleEncodeCached(encodeBuffer, serverCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8);
+ unsigned int numFonts = GetUINT(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(numFonts, 16, 6);
+
+ // Differential encoding.
+ encodeBuffer.encodeBoolValue(1);
+
+ const unsigned char* nextSrc = inputMessage + 32;
+ for (; numFonts; numFonts--)
+ {
+ unsigned int _length = (unsigned int) (*nextSrc++);
+ encodeBuffer.encodeValue(_length, 8);
+
+ // Since ProtoStep7 (#issue 108)
+ encodeBuffer.encodeTextData(nextSrc, _length);
+
+ nextSrc += _length;
+ }
+
+ priority_++;
+ }
+ break;
+ case X_LookupColor:
+ case X_AllocNamedColor:
+ {
+ const unsigned char *nextSrc = inputMessage + 8;
+ if (requestOpcode == X_AllocNamedColor)
+ {
+ encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 9);
+ nextSrc += 4;
+ }
+ unsigned int count = 3;
+ do
+ {
+ unsigned int exactColor = GetUINT(nextSrc, bigEndian_);
+ encodeBuffer.encodeValue(exactColor, 16, 9);
+ unsigned int visualColor = GetUINT(nextSrc + 6, bigEndian_) -
+ exactColor;
+ encodeBuffer.encodeValue(visualColor, 16, 5);
+ nextSrc += 2;
+ }
+ while (--count);
+
+ priority_++;
+ }
+ break;
+ case X_QueryBestSize:
+ {
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 8, bigEndian_), 16, 8);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 10, bigEndian_), 16, 8);
+
+ priority_++;
+ }
+ break;
+ case X_QueryColors:
+ {
+ // Differential encoding.
+ encodeBuffer.encodeBoolValue(1);
+
+ unsigned int numColors = ((inputLength - 32) >> 3);
+ const unsigned char *nextSrc1 = inputMessage + 40;
+ unsigned char *nextDest = (unsigned char *) inputMessage + 38;
+ for (unsigned int c = 1; c < numColors; c++)
+ {
+ for (unsigned int i = 0; i < 6; i++)
+ *nextDest++ = *nextSrc1++;
+ nextSrc1 += 2;
+ }
+ unsigned int colorsLength = numColors * 6;
+ if (serverCache_ -> queryColorsLastReply.compare(colorsLength,
+ inputMessage + 32))
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ const unsigned char *nextSrc2 = inputMessage + 32;
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue(numColors, 16, 5);
+ for (numColors *= 3; numColors; numColors--)
+ {
+ encodeBuffer.encodeValue(GetUINT(nextSrc2, bigEndian_), 16);
+ nextSrc2 += 2;
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_QueryExtension:
+ {
+ if (requestData[0] == X_QueryExtension)
+ {
+ //
+ // Value in requestData[0] will be nonzero
+ // if the request is for an extension that
+ // we should hide to the X client.
+ //
+
+ if (requestData[1])
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue(0, 8);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[8]);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[9], 8);
+ }
+
+ encodeBuffer.encodeValue((unsigned int) inputMessage[10], 8);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[11], 8);
+
+ if (requestData[2] == X_NXInternalShapeExtension)
+ {
+ opcodeStore_ -> shapeExtension = inputMessage[9];
+
+ #ifdef TEST
+ *logofs << "handleRead: Shape extension opcode for FD#" << fd_
+ << " is " << (unsigned int) opcodeStore_ -> shapeExtension
+ << ".\n" << logofs_flush;
+ #endif
+ }
+ else if (requestData[2] == X_NXInternalRenderExtension)
+ {
+ opcodeStore_ -> renderExtension = inputMessage[9];
+
+ #ifdef TEST
+ *logofs << "handleRead: Render extension opcode for FD#" << fd_
+ << " is " << (unsigned int) opcodeStore_ -> renderExtension
+ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ priority_++;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Received tainted X_QueryExtension reply "
+ << "for request OPCODE#" << requestData[0] << " with "
+ << "sequence " << sequenceNum << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (requestData[0] == opcodeStore_ -> getShmemParameters)
+ {
+ if (handleShmemReply(encodeBuffer, requestOpcode, requestData[1],
+ inputMessage, inputLength) < 0)
+ {
+ return -1;
+ }
+
+ priority_++;
+ }
+ //
+ // Account this data to the original opcode.
+ //
+
+ requestOpcode = requestData[0];
+ }
+ }
+ break;
+ case X_QueryFont:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_QueryFont);
+
+ if (handleEncodeCached(encodeBuffer, serverCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ // Differential encoding.
+ encodeBuffer.encodeBoolValue(1);
+
+ unsigned int numProperties = GetUINT(inputMessage + 46, bigEndian_);
+ unsigned int numCharInfos = GetULONG(inputMessage + 56, bigEndian_);
+ encodeBuffer.encodeValue(numProperties, 16, 8);
+ encodeBuffer.encodeValue(numCharInfos, 32, 10);
+ handleEncodeCharInfo(inputMessage + 8, encodeBuffer);
+ handleEncodeCharInfo(inputMessage + 24, encodeBuffer);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 40, bigEndian_), 16, 9);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 42, bigEndian_), 16, 9);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 44, bigEndian_), 16, 9);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[48]);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[49], 8);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[50], 8);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[51]);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 52, bigEndian_), 16, 9);
+ encodeBuffer.encodeValue(GetUINT(inputMessage + 54, bigEndian_), 16, 9);
+ const unsigned char *nextSrc = inputMessage + 60;
+ unsigned int index;
+
+ int end = 0;
+
+ if (ServerCache::queryFontFontCache.lookup(numProperties * 8 +
+ numCharInfos * 12, nextSrc, index))
+ {
+ encodeBuffer.encodeBoolValue(1);
+ encodeBuffer.encodeValue(index, 4);
+
+ end = 1;
+ }
+
+ if (end == 0)
+ {
+ encodeBuffer.encodeBoolValue(0);
+ for (; numProperties; numProperties--)
+ {
+ encodeBuffer.encodeValue(GetULONG(nextSrc, bigEndian_), 32, 9);
+ encodeBuffer.encodeValue(GetULONG(nextSrc + 4, bigEndian_), 32, 9);
+ nextSrc += 8;
+ }
+ for (; numCharInfos; numCharInfos--)
+ {
+ handleEncodeCharInfo(nextSrc, encodeBuffer);
+
+ nextSrc += 12;
+ }
+ }
+
+ priority_++;
+ }
+ break;
+ case X_QueryPointer:
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> queryPointerRootCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_),
+ 29, serverCache_ -> queryPointerChildCache, 9);
+ unsigned int rootX = GetUINT(inputMessage + 16, bigEndian_);
+ unsigned int rootY = GetUINT(inputMessage + 18, bigEndian_);
+ unsigned int eventX = GetUINT(inputMessage + 20, bigEndian_);
+ unsigned int eventY = GetUINT(inputMessage + 22, bigEndian_);
+ eventX -= rootX;
+ eventY -= rootY;
+ encodeBuffer.encodeCachedValue(
+ rootX - serverCache_ -> motionNotifyLastRootX, 16,
+ serverCache_ -> motionNotifyRootXCache, 8);
+ serverCache_ -> motionNotifyLastRootX = rootX;
+ encodeBuffer.encodeCachedValue(
+ rootY - serverCache_ -> motionNotifyLastRootY, 16,
+ serverCache_ -> motionNotifyRootYCache, 8);
+ serverCache_ -> motionNotifyLastRootY = rootY;
+ encodeBuffer.encodeCachedValue(eventX, 16,
+ serverCache_ -> motionNotifyEventXCache, 8);
+ encodeBuffer.encodeCachedValue(eventY, 16,
+ serverCache_ -> motionNotifyEventYCache, 8);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 24, bigEndian_),
+ 16, serverCache_ -> motionNotifyStateCache);
+ priority_++;
+ }
+ break;
+ case X_QueryTree:
+ {
+ //
+ // This was very inefficient. In practice
+ // it just copied data on the output. Now
+ // it obtains an average 7:1 compression
+ // and could optionally be cached.
+ //
+
+ unsigned int children = GetUINT(inputMessage + 16, bigEndian_);
+
+ encodeBuffer.encodeValue(children, 16, 8);
+
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
+ serverCache_ -> queryTreeWindowCache);
+
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_), 29,
+ serverCache_ -> queryTreeWindowCache);
+
+ const unsigned char *next = inputMessage + 32;
+
+ for (unsigned int i = 0; i < children; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(next + (i * 4), bigEndian_), 29,
+ serverCache_ -> queryTreeWindowCache);
+ }
+
+ priority_++;
+ }
+ break;
+ case X_TranslateCoords:
+ {
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[1]);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> translateCoordsChildCache, 9);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 12, bigEndian_),
+ 16, serverCache_ -> translateCoordsXCache, 8);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 14, bigEndian_),
+ 16, serverCache_ -> translateCoordsYCache, 8);
+ priority_++;
+ }
+ break;
+ case X_GetImage:
+ {
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_GetImage);
+
+ if (handleEncodeCached(encodeBuffer, serverCache_, messageStore,
+ inputMessage, inputLength))
+ {
+ priority_++;
+
+ hit = 1;
+
+ break;
+ }
+
+ // Depth.
+ encodeBuffer.encodeCachedValue(inputMessage[1], 8,
+ serverCache_ -> depthCache);
+ // Reply length.
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 9);
+
+ // Visual.
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_), 29,
+ serverCache_ -> visualCache);
+
+ // Since ProtoStep8 (#issue 108)
+ handleCopy(encodeBuffer, requestOpcode, messageStore ->
+ dataOffset, inputMessage, inputLength);
+
+ priority_++;
+ }
+ break;
+ case X_GetPointerMapping:
+ {
+ encodeBuffer.encodeValue(inputMessage[1], 8, 4);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 4);
+ for (unsigned int i = 32; i < inputLength; i++)
+ encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8, 4);
+
+ priority_++;
+ }
+ break;
+ case X_GetKeyboardControl:
+ {
+ encodeBuffer.encodeValue(inputMessage[1], 8, 2);
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 8);
+ for (unsigned int i = 8; i < inputLength; i++)
+ encodeBuffer.encodeValue((unsigned int) inputMessage[i], 8, 4);
+
+ priority_++;
+ }
+ break;
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "ServerChannel: PANIC! No matching request with "
+ << "OPCODE#" << (unsigned int) requestOpcode
+ << " for reply with sequence number "
+ << requestSequenceNum << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": No matching request with OPCODE#"
+ << (unsigned int) requestOpcode << " for reply with "
+ << "sequence number " << requestSequenceNum << ".\n";
+
+ return -1;
+ }
+ }
+
+ bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ const char *cacheString = (hit ? "cached " : "");
+
+ *logofs << "handleRead: Handled " << cacheString << "reply to OPCODE#"
+ << (unsigned int) requestOpcode << " (" << DumpOpcode(requestOpcode)
+ << ") for FD#" << fd_ << " sequence " << serverSequence_
+ << ". " << inputLength << " bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+
+ #endif
+
+ } // End of if (sequenceQueue_.peek(requestSequenceNum, requestOpcode) && ...
+ else
+ {
+ //
+ // We didn't push the request opcode.
+ // Check if fast encoding is required.
+ //
+
+ requestOpcode = X_Reply;
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ int _result = handleFastReadReply(encodeBuffer, requestOpcode,
+ inputMessage, inputLength);
+ if (_result < 0)
+ {
+ return -1;
+ }
+ else if (_result > 0)
+ {
+ continue;
+ }
+ }
+
+ //
+ // Group all replies whose opcode was not
+ // pushed in sequence number queue under
+ // the category 'generic reply'.
+ //
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Identified generic reply.\n"
+ << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = serverStore_ ->
+ getReplyStore(X_NXInternalGenericReply);
+
+ hit = handleEncode(encodeBuffer, serverCache_, messageStore,
+ requestOpcode, inputMessage, inputLength);
+
+ priority_++;
+
+ bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ const char *cacheString = (hit ? "cached " : "");
+
+ *logofs << "handleRead: Handled " << cacheString << "generic reply "
+ << "OPCODE#" << X_NXInternalGenericReply << " for FD#" << fd_
+ << " sequence " << serverSequence_ << ". " << inputLength
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+
+ #endif
+ }
+
+ if (hit)
+ {
+ statistics -> addCachedReply(requestOpcode);
+ }
+
+ statistics -> addReplyBits(requestOpcode, inputLength << 3, bits);
+
+ } // End of if (inputMessage[0] == 1) ...
+ else
+ {
+ //
+ // Event or error.
+ //
+
+ unsigned char inputOpcode = *inputMessage;
+
+ unsigned int inputSequence = GetUINT(inputMessage + 2, bigEndian_);
+
+ //
+ // Check if this is an event which we can discard.
+ //
+
+ if ((inputOpcode == Expose && enableExpose_ == 0) ||
+ (inputOpcode == GraphicsExpose && enableGraphicsExpose_ == 0) ||
+ (inputOpcode == NoExpose && enableNoExpose_ == 0))
+ {
+ continue;
+ }
+ else if (shmemState_ != NULL && shmemState_ -> enabled == 1 &&
+ inputOpcode == shmemState_ -> event &&
+ checkShmemEvent(inputOpcode, inputSequence,
+ inputMessage) > 0)
+ {
+ continue;
+ }
+ else if (inputOpcode == MotionNotify)
+ {
+ //
+ // Save the motion event and send when another
+ // event or error is received or the motion ti-
+ // meout is elapsed. If a previous motion event
+ // was already saved, we replace it with the
+ // new one and don't reset the timeout, so we
+ // still have a motion event every given ms.
+ //
+
+ memcpy(lastMotion_, inputMessage, 32);
+
+ #ifdef TEST
+ *logofs << "handleRead: Saved suppressed motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else if (inputOpcode == X_Error)
+ {
+ //
+ // Check if this is an error that matches a
+ // sequence number for which we are expecting
+ // a reply.
+ //
+
+ unsigned short int errorSequenceNum;
+ unsigned char errorOpcode;
+
+ if (sequenceQueue_.peek(errorSequenceNum, errorOpcode) &&
+ ((unsigned int) errorSequenceNum == inputSequence))
+ {
+ sequenceQueue_.pop(errorSequenceNum, errorOpcode);
+ }
+
+ //
+ // Check if error is due to an image commit
+ // generated at the end of a split.
+ //
+
+ if (checkCommitError(*(inputMessage + 1), inputSequence, inputMessage) > 0)
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Skipping error on image commit for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+
+ //
+ // Check if it's an error generated by a request
+ // concerning shared memory support.
+ //
+
+ else if (shmemState_ != NULL && (shmemState_ -> sequence ==
+ inputSequence || (shmemState_ -> enabled == 1 &&
+ (shmemState_ -> opcode == *(inputMessage + 10) ||
+ shmemState_ -> error == *(inputMessage + 1)))) &&
+ checkShmemError(*(inputMessage + 1), inputSequence,
+ inputMessage) > 0)
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Skipping error on shmem operation for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+ }
+ //
+ // Check if user pressed the CTRL+ALT+SHIFT+ESC key
+ // sequence because was unable to kill the session
+ // through the normal procedure.
+ //
+
+ if (inputOpcode == KeyPress)
+ {
+ if (checkKeyboardEvent(inputOpcode, inputSequence, inputMessage) == 1)
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Removing the key sequence from the "
+ << "event stream for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ }
+
+ //
+ // We are going to handle an event or error
+ // that's not a mouse motion. Prepend any
+ // saved motion to it.
+ //
+
+ if (lastMotion_[0] != '\0')
+ {
+ if (handleMotion(encodeBuffer) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleRead: PANIC! Can't encode motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't encode motion event for FD#"
+ << fd_ << ".\n";
+
+ return -1;
+ }
+ }
+
+ //
+ // Encode opcode and difference between
+ // current sequence and the last one.
+ //
+
+ encodeBuffer.encodeOpcodeValue(inputOpcode, serverCache_ -> opcodeCache);
+
+ unsigned int sequenceDiff = inputSequence - serverSequence_;
+
+ serverSequence_ = inputSequence;
+
+ #ifdef DEBUG
+ *logofs << "handleRead: Last server sequence number for FD#"
+ << fd_ << " is " << serverSequence_ << " with "
+ << "difference " << sequenceDiff << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sequenceDiff, 16,
+ serverCache_ -> eventSequenceCache, 7);
+
+ //
+ // If differential compression is disabled
+ // then use the most simple encoding.
+ //
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ int _result = handleFastReadEvent(encodeBuffer, inputOpcode,
+ inputMessage, inputLength);
+ if (_result < 0)
+ {
+ return -1;
+ }
+ else if (_result > 0)
+ {
+ continue;
+ }
+ }
+
+ switch (inputOpcode)
+ {
+ case X_Error:
+ {
+ //
+ // Set the priority flag in the case of
+ // a X protocol error. This may restart
+ // the client if it was waiting for the
+ // reply.
+ //
+
+ priority_++;
+
+ unsigned char errorCode = *(inputMessage + 1);
+
+ encodeBuffer.encodeCachedValue(errorCode, 8,
+ serverCache_ -> errorCodeCache);
+
+ if (errorCode != 11 && errorCode != 8 &&
+ errorCode != 15 && errorCode != 1)
+ {
+ encodeBuffer.encodeValue(GetULONG(inputMessage + 4, bigEndian_), 32, 16);
+ }
+
+ if (errorCode >= 18)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ serverCache_ -> errorMinorCache);
+ }
+
+ encodeBuffer.encodeCachedValue(inputMessage[10], 8,
+ serverCache_ -> errorMajorCache);
+
+ if (errorCode >= 18)
+ {
+ const unsigned char *nextSrc = inputMessage + 11;
+ for (unsigned int i = 11; i < 32; i++)
+ encodeBuffer.encodeValue(*nextSrc++, 8);
+ }
+ }
+ break;
+ case ButtonPress:
+ case ButtonRelease:
+ case KeyPress:
+ case KeyRelease:
+ case MotionNotify:
+ case EnterNotify:
+ case LeaveNotify:
+ {
+ //
+ // Set the priority in the case this is
+ // an event that the remote side may
+ // care to receive as soon as possible.
+ //
+
+ switch (inputOpcode)
+ {
+ case ButtonPress:
+ case ButtonRelease:
+ case KeyPress:
+ case KeyRelease:
+ {
+ priority_++;
+ }
+ }
+
+ unsigned char detail = inputMessage[1];
+ if (*inputMessage == MotionNotify)
+ encodeBuffer.encodeBoolValue((unsigned int) detail);
+ else if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) detail, 3);
+ else if (*inputMessage == KeyRelease)
+ {
+ if (detail == serverCache_ -> keyPressLastKey)
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue((unsigned int) detail, 8);
+ }
+ }
+ else if ((*inputMessage == ButtonPress) || (*inputMessage == ButtonRelease))
+ encodeBuffer.encodeCachedValue(detail, 8,
+ serverCache_ -> buttonCache);
+ else
+ encodeBuffer.encodeValue((unsigned int) detail, 8);
+ unsigned int _timestamp = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int timestampDiff =
+ _timestamp - serverCache_ -> lastTimestamp;
+ serverCache_ -> lastTimestamp = _timestamp;
+ encodeBuffer.encodeCachedValue(timestampDiff, 32,
+ serverCache_ -> motionNotifyTimestampCache, 9);
+ int skipRest = 0;
+ if (*inputMessage == KeyRelease)
+ {
+ skipRest = 1;
+ for (unsigned int i = 8; i < 31; i++)
+ {
+ if (inputMessage[i] != serverCache_ -> keyPressCache[i - 8])
+ {
+ skipRest = 0;
+ break;
+ }
+ }
+ encodeBuffer.encodeBoolValue(skipRest);
+ }
+
+ if (!skipRest)
+ {
+ const unsigned char *nextSrc = inputMessage + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29,
+ *serverCache_ -> motionNotifyWindowCache[i], 6);
+ nextSrc += 4;
+ }
+ unsigned int rootX = GetUINT(inputMessage + 20, bigEndian_);
+ unsigned int rootY = GetUINT(inputMessage + 22, bigEndian_);
+ unsigned int eventX = GetUINT(inputMessage + 24, bigEndian_);
+ unsigned int eventY = GetUINT(inputMessage + 26, bigEndian_);
+ eventX -= rootX;
+ eventY -= rootY;
+ encodeBuffer.encodeCachedValue(rootX -
+ serverCache_ -> motionNotifyLastRootX, 16,
+ serverCache_ -> motionNotifyRootXCache, 6);
+ serverCache_ -> motionNotifyLastRootX = rootX;
+ encodeBuffer.encodeCachedValue(rootY -
+ serverCache_ -> motionNotifyLastRootY, 16,
+ serverCache_ -> motionNotifyRootYCache, 6);
+ serverCache_ -> motionNotifyLastRootY = rootY;
+ encodeBuffer.encodeCachedValue(eventX, 16,
+ serverCache_ -> motionNotifyEventXCache, 6);
+ encodeBuffer.encodeCachedValue(eventY, 16,
+ serverCache_ -> motionNotifyEventYCache, 6);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 28, bigEndian_),
+ 16, serverCache_ -> motionNotifyStateCache);
+ if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) inputMessage[30], 2);
+ else
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[30]);
+ if ((*inputMessage == EnterNotify) || (*inputMessage == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) inputMessage[31], 2);
+ else if (*inputMessage == KeyPress)
+ {
+ serverCache_ -> keyPressLastKey = detail;
+ for (unsigned int i = 8; i < 31; i++)
+ {
+ serverCache_ -> keyPressCache[i - 8] = inputMessage[i];
+ }
+ }
+ }
+ }
+ break;
+ case ColormapNotify:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> colormapNotifyWindowCache, 8);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> colormapNotifyColormapCache, 8);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[13]);
+ }
+ break;
+ case ConfigureNotify:
+ {
+ const unsigned char *nextSrc = inputMessage + 4;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29,
+ *serverCache_ -> configureNotifyWindowCache[i], 9);
+ nextSrc += 4;
+ }
+ for (unsigned int j = 0; j < 5; j++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *serverCache_ -> configureNotifyGeomCache[j], 8);
+ nextSrc += 2;
+ }
+ encodeBuffer.encodeBoolValue(*nextSrc);
+ }
+ break;
+ case CreateNotify:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> createNotifyWindowCache, 9);
+ unsigned int window = GetULONG(inputMessage + 8, bigEndian_);
+ encodeBuffer.encodeValue(window -
+ serverCache_ -> createNotifyLastWindow, 29, 5);
+ serverCache_ -> createNotifyLastWindow = window;
+ const unsigned char* nextSrc = inputMessage + 12;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16, 9);
+ nextSrc += 2;
+ }
+ encodeBuffer.encodeBoolValue(*nextSrc);
+ }
+ break;
+ case Expose:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_), 29,
+ serverCache_ -> exposeWindowCache, 9);
+ const unsigned char *nextSrc = inputMessage + 8;
+ for (unsigned int i = 0; i < 5; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(nextSrc, bigEndian_), 16,
+ *serverCache_ -> exposeGeomCache[i], 6);
+ nextSrc += 2;
+ }
+ }
+ break;
+ case FocusIn:
+ case FocusOut:
+ {
+ encodeBuffer.encodeValue((unsigned int) inputMessage[1], 3);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> focusInWindowCache, 9);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[8], 2);
+ }
+ break;
+ case KeymapNotify:
+ {
+ if (ServerCache::lastKeymap.compare(31, inputMessage + 1))
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ const unsigned char *nextSrc = inputMessage + 1;
+ for (unsigned int i = 1; i < 32; i++)
+ encodeBuffer.encodeValue((unsigned int) *nextSrc++, 8);
+ }
+ }
+ break;
+ case MapNotify:
+ case UnmapNotify:
+ case DestroyNotify:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> mapNotifyEventCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> mapNotifyWindowCache, 9);
+ if ((*inputMessage == MapNotify) || (*inputMessage == UnmapNotify))
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[12]);
+ }
+ break;
+ case NoExpose:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> noExposeDrawableCache, 9);
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + 8, bigEndian_), 16,
+ serverCache_ -> noExposeMinorCache);
+ encodeBuffer.encodeCachedValue(inputMessage[10], 8,
+ serverCache_ -> noExposeMajorCache);
+ }
+ break;
+ case PropertyNotify:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> propertyNotifyWindowCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> propertyNotifyAtomCache, 9);
+ unsigned int _timestamp = GetULONG(inputMessage + 12, bigEndian_);
+ unsigned int timestampDiff =
+ _timestamp - serverCache_ -> lastTimestamp;
+ serverCache_ -> lastTimestamp = _timestamp;
+ encodeBuffer.encodeValue(timestampDiff, 32, 9);
+ encodeBuffer.encodeBoolValue((unsigned int) inputMessage[16]);
+ }
+ break;
+ case ReparentNotify:
+ {
+ const unsigned char* nextSrc = inputMessage + 4;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_),
+ 29, serverCache_ -> reparentNotifyWindowCache, 9);
+ nextSrc += 4;
+ }
+ encodeBuffer.encodeValue(GetUINT(nextSrc, bigEndian_), 16, 6);
+ encodeBuffer.encodeValue(GetUINT(nextSrc + 2, bigEndian_), 16, 6);
+ encodeBuffer.encodeBoolValue((unsigned int)inputMessage[20]);
+ }
+ break;
+ case SelectionClear:
+ {
+ unsigned int _timestamp = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int timestampDiff = _timestamp - serverCache_ -> lastTimestamp;
+ serverCache_ -> lastTimestamp = _timestamp;
+ encodeBuffer.encodeValue(timestampDiff, 32, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> selectionClearWindowCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_),
+ 29, serverCache_ -> selectionClearAtomCache, 9);
+ }
+ break;
+ case SelectionRequest:
+ {
+ unsigned int _timestamp = GetULONG(inputMessage + 4, bigEndian_);
+ unsigned int timestampDiff = _timestamp - serverCache_ -> lastTimestamp;
+ serverCache_ -> lastTimestamp = _timestamp;
+ encodeBuffer.encodeValue(timestampDiff, 32, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 8, bigEndian_),
+ 29, serverCache_ -> selectionClearWindowCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 12, bigEndian_),
+ 29, serverCache_ -> selectionClearWindowCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 16, bigEndian_),
+ 29, serverCache_ -> selectionClearAtomCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 20, bigEndian_),
+ 29, serverCache_ -> selectionClearAtomCache, 9);
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 24, bigEndian_),
+ 29, serverCache_ -> selectionClearAtomCache, 9);
+ }
+ break;
+ case VisibilityNotify:
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(inputMessage + 4, bigEndian_),
+ 29, serverCache_ -> visibilityNotifyWindowCache, 9);
+ encodeBuffer.encodeValue((unsigned int) inputMessage[8], 2);
+ }
+ break;
+ default:
+ {
+ #ifdef TEST
+ *logofs << "handleRead: Using generic event compression "
+ << "for OPCODE#" << (unsigned int) inputOpcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(*(inputMessage + 1), 8,
+ serverCache_ -> genericEventCharCache);
+
+ for (unsigned int i = 0; i < 14; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(inputMessage + i * 2 + 4, bigEndian_),
+ 16, *serverCache_ -> genericEventIntCache[i]);
+ }
+ }
+
+ } // switch (inputOpcode)...
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ if (*inputMessage == X_Error)
+ {
+ unsigned char code = *(inputMessage + 1);
+
+ *logofs << "handleRead: Handled error ERR_CODE#"
+ << (unsigned int) code << " for FD#" << fd_;
+
+ *logofs << " RES_ID#" << GetULONG(inputMessage + 4, bigEndian_);
+
+ *logofs << " MIN_OP#" << GetUINT(inputMessage + 8, bigEndian_);
+
+ *logofs << " MAJ_OP#" << (unsigned int) *(inputMessage + 10);
+
+ *logofs << " sequence " << inputSequence << ". " << inputLength
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleRead: Handled event OPCODE#"
+ << (unsigned int) *inputMessage << " for FD#" << fd_
+ << " sequence " << inputSequence << ". " << inputLength
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ }
+
+ #endif
+
+ statistics -> addEventBits(*inputMessage, inputLength << 3, bits);
+
+ } // End of if (inputMessage[0] == X_Reply) ... else ...
+
+ } // End of if (firstReply_) ... else ...
+
+ } // End of while ((inputMessage = readBuffer_.getMessage(inputLength)) != 0) ...
+
+ //
+ // Check if we need to flush because of
+ // prioritized data.
+ //
+
+ if (priority_ > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of " << priority_ << " prioritized "
+ << "messages for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncPriority() < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset the priority flag.
+ //
+
+ priority_ = 0;
+ }
+
+ //
+ // Flush if we produced enough data.
+ //
+
+ if (proxy -> canAsyncFlush() == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleRead: WARNING! Requesting flush "
+ << "because of token length exceeded.\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncFlush() < 0)
+ {
+ return -1;
+ }
+ }
+
+ #if defined(TEST) || defined(INFO)
+
+ if (transport_ -> pending() != 0 ||
+ readBuffer_.checkMessage() != 0)
+ {
+ *logofs << "handleRead: PANIC! Buffer for X descriptor FD#"
+ << fd_ << " has " << transport_ -> pending()
+ << " bytes to read.\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // Reset the read buffer.
+ //
+
+ readBuffer_.fullReset();
+
+ return 1;
+}
+
+//
+// End of handleRead().
+//
+
+//
+// Beginning of handleWrite().
+//
+
+int ServerChannel::handleWrite(const unsigned char *message, unsigned int length)
+{
+ #ifdef TEST
+ *logofs << "handleWrite: Called for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Create the buffer from which to
+ // decode messages.
+ //
+
+ DecodeBuffer decodeBuffer(message, length);
+
+ #if defined(TEST) || defined(INFO) || defined(FLUSH)
+ *logofs << "handleWrite: Decoding messages for FD#" << fd_
+ << " with " << length << " bytes in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ if (firstRequest_)
+ {
+ //
+ // Need to add the length of the first request
+ // because it was not present in the previous
+ // versions. Length of the first request was
+ // assumed to be the same as the encode buffer
+ // but this may be not the case if a different
+ // encoding is used.
+ //
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeValue(length, 8);
+
+ unsigned int nextByte;
+ unsigned char *outputMessage = writeBuffer_.addMessage(length);
+ unsigned char *nextDest = outputMessage;
+
+ for (unsigned int i = 0; i < length; i++)
+ {
+ decodeBuffer.decodeValue(nextByte, 8);
+
+ *nextDest++ = (unsigned char) nextByte;
+ }
+
+ if (*outputMessage == 0x42)
+ {
+ setBigEndian(1);
+ }
+ else
+ {
+ setBigEndian(0);
+ }
+
+ #ifdef TEST
+ *logofs << "handleWrite: First request detected.\n" << logofs_flush;
+ #endif
+
+ //
+ // Handle the fake authorization cookie.
+ //
+
+ if (handleAuthorization(outputMessage) < 0)
+ {
+ return -1;
+ }
+
+ firstRequest_ = 0;
+
+ } // End of if (firstRequest_)
+
+ //
+ // This was previously in a 'else' block.
+ // Due to the way the first request was
+ // handled, we could not decode multiple
+ // messages in the first frame.
+ //
+
+ { // Start of the decoding block.
+
+ unsigned char outputOpcode;
+
+ unsigned char *outputMessage;
+ unsigned int outputLength;
+
+ //
+ // Set when message is found in cache.
+ //
+
+ int hit;
+
+ while (decodeBuffer.decodeOpcodeValue(outputOpcode, clientCache_ -> opcodeCache, 1))
+ {
+ hit = 0;
+
+ //
+ // Splits are sent by client proxy outside the
+ // normal read loop. As we 'insert' splits in
+ // the real client-server X protocol, we must
+ // avoid to increment the sequence number or
+ // our clients would get confused.
+ //
+
+ if (outputOpcode != opcodeStore_ -> splitData)
+ {
+ clientSequence_++;
+ clientSequence_ &= 0xffff;
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Last client sequence number for FD#"
+ << fd_ << " is " << clientSequence_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // It's a split, not a normal
+ // burst of proxy data.
+ //
+
+ handleSplit(decodeBuffer);
+
+ continue;
+ }
+
+ #ifdef SUSPEND
+
+ if (clientSequence_ == 1000)
+ {
+ cerr << "Warning" << ": Exiting to test the resilience of the agent.\n";
+
+ sleep(2);
+
+ HandleAbort();
+ }
+
+ #endif
+
+ //
+ // Is differential encoding disabled?
+ //
+
+ if (control -> RemoteDeltaCompression == 0)
+ {
+ int result = handleFastWriteRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ continue;
+ }
+ }
+
+ //
+ // General-purpose temp variables for
+ // decoding ints and chars.
+ //
+
+ unsigned int value;
+ unsigned char cValue;
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Going to handle request OPCODE#"
+ << (unsigned int) outputOpcode << " (" << DumpOpcode(outputOpcode)
+ << ") for FD#" << fd_ << " sequence " << clientSequence_
+ << ".\n" << logofs_flush;
+ #endif
+
+ switch (outputOpcode)
+ {
+ case X_AllocColor:
+ {
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> colormapCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ unsigned int colorData[3];
+
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *(clientCache_ -> allocColorRGBCache[i]), 4);
+ PutUINT(value, nextDest, bigEndian_);
+ colorData[i] = value;
+ nextDest += 2;
+ }
+
+ sequenceQueue_.push(clientSequence_, outputOpcode,
+ colorData[0], colorData[1], colorData[2]);
+ }
+ break;
+ case X_ReparentWindow:
+ {
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 11);
+ PutUINT(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 11);
+ PutUINT(value, outputMessage + 14, bigEndian_);
+ }
+ break;
+ case X_ChangeProperty:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ChangeProperty);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned char format;
+ decodeBuffer.decodeCachedValue(format, 8,
+ clientCache_ -> changePropertyFormatCache);
+ unsigned int dataLength;
+ decodeBuffer.decodeValue(dataLength, 32, 6);
+ outputLength = 24 + RoundUp4(dataLength * (format >> 3));
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> changePropertyPropertyCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> changePropertyTypeCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ outputMessage[16] = format;
+ PutULONG(dataLength, outputMessage + 20, bigEndian_);
+ unsigned char *nextDest = outputMessage + 24;
+
+ if (format == 8)
+ {
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, dataLength);
+ }
+ else if (format == 32)
+ {
+ for (unsigned int i = 0; i < dataLength; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> changePropertyData32Cache);
+
+ PutULONG(value, nextDest, bigEndian_);
+
+ nextDest += 4;
+ }
+ }
+ else
+ {
+ for (unsigned int i = 0; i < dataLength; i++)
+ {
+ decodeBuffer.decodeValue(value, 16);
+
+ PutUINT(value, nextDest, bigEndian_);
+
+ nextDest += 2;
+ }
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_SendEvent:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_SendEvent);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 44;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ *(outputMessage + 1) = value;
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ }
+ else
+ {
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ }
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> sendEventMaskCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(*(outputMessage + 12), 8,
+ clientCache_ -> sendEventCodeCache);
+ decodeBuffer.decodeCachedValue(*(outputMessage + 13), 8,
+ clientCache_ -> sendEventByteDataCache);
+ decodeBuffer.decodeValue(value, 16, 4);
+ clientCache_ -> sendEventLastSequence += value;
+ clientCache_ -> sendEventLastSequence &= 0xffff;
+ PutUINT(clientCache_ -> sendEventLastSequence, outputMessage + 14, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> sendEventIntDataCache);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+
+ for (unsigned int i = 20; i < 44; i++)
+ {
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> sendEventEventCache);
+ *(outputMessage + i) = cValue;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ChangeWindowAttributes:
+ {
+ unsigned int numAttrs;
+ decodeBuffer.decodeValue(numAttrs, 4);
+ outputLength = 12 + (numAttrs << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned int bitmask;
+ decodeBuffer.decodeCachedValue(bitmask, 15,
+ clientCache_ -> createWindowBitmaskCache);
+ PutULONG(bitmask, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 15; i++)
+ {
+ if (bitmask & mask)
+ {
+ decodeBuffer.decodeCachedValue(value, 32,
+ *clientCache_ -> createWindowAttrCache[i]);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ mask <<= 1;
+ }
+ }
+ break;
+ case X_ClearArea:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ClearArea);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ for (unsigned int i = 0; i < 4; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> clearAreaGeomCache[i], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_CloseFont:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 29, 5);
+ clientCache_ -> lastFont += value;
+ clientCache_ -> lastFont &= 0x1fffffff;
+ PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_);
+ }
+ break;
+ case X_ConfigureWindow:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ConfigureWindow);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned int bitmask;
+ decodeBuffer.decodeCachedValue(bitmask, 7,
+ clientCache_ -> configureWindowBitmaskCache);
+ PutUINT(bitmask, outputMessage + 8, bigEndian_);
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 7; i++)
+ {
+ if (bitmask & mask)
+ {
+ unsigned char* nextDest = writeBuffer_.addMessage(4);
+ outputLength += 4;
+ decodeBuffer.decodeCachedValue(value, CONFIGUREWINDOW_FIELD_WIDTH[i],
+ *clientCache_ -> configureWindowAttrCache[i], 8);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ mask <<= 1;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ConvertSelection:
+ {
+ outputLength = 24;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> convertSelectionRequestorCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char* nextDest = outputMessage + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 29,
+ *(clientCache_ -> convertSelectionAtomCache[i]), 9);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ }
+ decodeBuffer.decodeValue(value, 32, 4);
+ clientCache_ -> convertSelectionLastTimestamp += value;
+ PutULONG(clientCache_ -> convertSelectionLastTimestamp,
+ nextDest, bigEndian_);
+ }
+ break;
+ case X_CopyArea:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CopyArea);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 28;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ unsigned char *nextDest = outputMessage + 16;
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> copyAreaGeomCache[i], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_CopyGC:
+ {
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 23,
+ clientCache_ -> createGCBitmaskCache);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ }
+ break;
+ case X_CopyPlane:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ unsigned char *nextDest = outputMessage + 16;
+ for (unsigned int i = 0; i < 6; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> copyPlaneGeomCache[i], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> copyPlaneBitPlaneCache, 10);
+ PutULONG(value, outputMessage + 28, bigEndian_);
+ }
+ break;
+ case X_CreateGC:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CreateGC);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId,
+ clientCache_ -> lastIdCache, clientCache_ -> gcCache,
+ clientCache_ -> freeGCCache);
+
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned int offset = 8;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + offset, bigEndian_);
+ offset += 4;
+ unsigned int bitmask;
+ decodeBuffer.decodeCachedValue(bitmask, 23,
+ clientCache_ -> createGCBitmaskCache);
+ PutULONG(bitmask, outputMessage + offset, bigEndian_);
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (bitmask & mask)
+ {
+ unsigned char* nextDest = writeBuffer_.addMessage(4);
+ outputLength += 4;
+ unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i];
+ if (fieldWidth <= 4)
+ decodeBuffer.decodeValue(value, fieldWidth);
+ else
+ decodeBuffer.decodeCachedValue(value, fieldWidth,
+ *clientCache_ -> createGCAttrCache[i]);
+ PutULONG(value, nextDest, bigEndian_);
+ }
+ mask <<= 1;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ChangeGC:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ChangeGC);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned int offset = 8;
+ unsigned int bitmask;
+ decodeBuffer.decodeCachedValue(bitmask, 23,
+ clientCache_ -> createGCBitmaskCache);
+ PutULONG(bitmask, outputMessage + offset, bigEndian_);
+ unsigned int mask = 0x1;
+ for (unsigned int i = 0; i < 23; i++)
+ {
+ if (bitmask & mask)
+ {
+ unsigned char* nextDest = writeBuffer_.addMessage(4);
+ outputLength += 4;
+ unsigned int fieldWidth = CREATEGC_FIELD_WIDTH[i];
+ if (fieldWidth <= 4)
+ decodeBuffer.decodeValue(value, fieldWidth);
+ else
+ decodeBuffer.decodeCachedValue(value, fieldWidth,
+ *clientCache_ -> createGCAttrCache[i]);
+ PutULONG(value, nextDest, bigEndian_);
+ }
+ mask <<= 1;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_CreatePixmap:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_CreatePixmap);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ }
+ break;
+ case X_CreateWindow:
+ {
+ outputLength = 32;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeCachedValue(cValue, 8, clientCache_ -> depthCache);
+ outputMessage[1] = cValue;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeNewXidValue(value, clientCache_ -> lastId,
+ clientCache_ -> lastIdCache, clientCache_ -> windowCache,
+ clientCache_ -> freeWindowCache);
+
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ unsigned int i;
+ for (i = 0; i < 6; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> createWindowGeomCache[i], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> visualCache);
+ PutULONG(value, outputMessage + 24, bigEndian_);
+ unsigned int bitmask;
+ decodeBuffer.decodeCachedValue(bitmask, 15,
+ clientCache_ -> createWindowBitmaskCache);
+ PutULONG(bitmask, outputMessage + 28, bigEndian_);
+ unsigned int mask = 0x1;
+ for (i = 0; i < 15; i++)
+ {
+ if (bitmask & mask)
+ {
+ nextDest = writeBuffer_.addMessage(4);
+ outputLength += 4;
+ decodeBuffer.decodeCachedValue(value, 32,
+ *clientCache_ -> createWindowAttrCache[i]);
+ PutULONG(value, nextDest, bigEndian_);
+ }
+ mask <<= 1;
+ }
+ writeBuffer_.unregisterPointer();
+ }
+ break;
+ case X_DeleteProperty:
+ {
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 29, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ }
+ break;
+ case X_FillPoly:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_FillPoly);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int numPoints;
+
+ // Since ProtoStep10 (#issue 108)
+ decodeBuffer.decodeCachedValue(numPoints, 16,
+ clientCache_ -> fillPolyNumPointsCache, 4);
+
+ outputLength = 16 + (numPoints << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[12] = (unsigned char) value;
+ unsigned int relativeCoordMode;
+ decodeBuffer.decodeBoolValue(relativeCoordMode);
+ outputMessage[13] = (unsigned char) relativeCoordMode;
+ unsigned char *nextDest = outputMessage + 16;
+ unsigned int pointIndex = 0;
+ for (unsigned int i = 0; i < numPoints; i++)
+ {
+ if (relativeCoordMode)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> fillPolyXRelCache[pointIndex], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> fillPolyYRelCache[pointIndex], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ else
+ {
+ unsigned int x, y;
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ {
+ decodeBuffer.decodeValue(value, 3);
+ x = clientCache_ -> fillPolyRecentX[value];
+ y = clientCache_ -> fillPolyRecentY[value];
+ }
+ else
+ {
+ decodeBuffer.decodeCachedValue(x, 16,
+ *clientCache_ -> fillPolyXAbsCache[pointIndex], 8);
+ decodeBuffer.decodeCachedValue(y, 16,
+ *clientCache_ -> fillPolyYAbsCache[pointIndex], 8);
+ clientCache_ -> fillPolyRecentX[clientCache_ -> fillPolyIndex] = x;
+ clientCache_ -> fillPolyRecentY[clientCache_ -> fillPolyIndex] = y;
+ clientCache_ -> fillPolyIndex++;
+ if (clientCache_ -> fillPolyIndex == 8)
+ clientCache_ -> fillPolyIndex = 0;
+ }
+ PutUINT(x, nextDest, bigEndian_);
+ nextDest += 2;
+ PutUINT(y, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+
+ if (++pointIndex == 10) pointIndex = 0;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_FreeColors:
+ {
+ unsigned int numPixels;
+ decodeBuffer.decodeValue(numPixels, 16, 4);
+ outputLength = 12 + (numPixels << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> colormapCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 4);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char* nextDest = outputMessage + 12;
+ while (numPixels)
+ {
+ decodeBuffer.decodeValue(value, 32, 8);
+ PutULONG(value, nextDest, bigEndian_);
+ nextDest += 4;
+ numPixels--;
+ }
+ }
+ break;
+ case X_FreeCursor:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> cursorCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ }
+ break;
+ case X_FreeGC:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeGCCache);
+
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ }
+ break;
+ case X_FreePixmap:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeDrawableCache);
+
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ }
+ break;
+ case X_GetAtomName:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 29, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GetGeometry:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GetInputFocus:
+ {
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode, outputOpcode);
+ }
+ break;
+ case X_GetModifierMapping:
+ {
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GetKeyboardMapping:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[4] = value;
+ decodeBuffer.decodeValue(value, 8);
+ outputMessage[5] = value;
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GetProperty:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_GetProperty);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ //
+ // Save a reference to identify the reply.
+ //
+
+ unsigned int property = GetULONG(outputMessage + 8, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode, property);
+
+ break;
+ }
+
+ outputLength = 24;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned int property;
+ decodeBuffer.decodeValue(property, 29, 9);
+ PutULONG(property, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 29, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 2);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 8);
+ PutULONG(value, outputMessage + 20, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode, property);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_GetSelectionOwner:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> getSelectionOwnerSelectionCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GrabButton:
+ case X_GrabPointer:
+ {
+ outputLength = 24;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> grabButtonEventMaskCache);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[10] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[11] = (unsigned char) value;
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> grabButtonConfineCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> cursorCache, 9);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+ if (outputOpcode == X_GrabButton)
+ {
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> grabButtonButtonCache);
+ outputMessage[20] = cValue;
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> grabButtonModifierCache);
+ PutUINT(value, outputMessage + 22, bigEndian_);
+ }
+ else
+ {
+ decodeBuffer.decodeValue(value, 32, 4);
+ clientCache_ -> grabKeyboardLastTimestamp += value;
+ PutULONG(clientCache_ -> grabKeyboardLastTimestamp,
+ outputMessage + 20, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ }
+ break;
+ case X_GrabKeyboard:
+ {
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 32, 4);
+ clientCache_ -> grabKeyboardLastTimestamp += value;
+ PutULONG(clientCache_ -> grabKeyboardLastTimestamp, outputMessage + 8,
+ bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[12] = (unsigned char) value;
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[13] = (unsigned char) value;
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GrabServer:
+ case X_UngrabServer:
+ case X_NoOperation:
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Managing (probably tainted) X_NoOperation request for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ }
+ break;
+ case X_PolyText8:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyText8);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> polyTextCacheX);
+ clientCache_ -> polyTextLastX += value;
+ clientCache_ -> polyTextLastX &= 0xffff;
+ PutUINT(clientCache_ -> polyTextLastX, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> polyTextCacheY);
+ clientCache_ -> polyTextLastY += value;
+ clientCache_ -> polyTextLastY &= 0xffff;
+ PutUINT(clientCache_ -> polyTextLastY, outputMessage + 14, bigEndian_);
+ unsigned int addedLength = 0;
+ writeBuffer_.registerPointer(&outputMessage);
+ for (;;)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (!value)
+ break;
+ unsigned int textLength;
+ decodeBuffer.decodeValue(textLength, 8);
+ if (textLength == 255)
+ {
+ addedLength += 5;
+ unsigned char *nextSegment = writeBuffer_.addMessage(5);
+ *nextSegment = (unsigned char) textLength;
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> polyTextFontCache);
+ PutULONG(value, nextSegment + 1, 1);
+ }
+ else
+ {
+ addedLength += (textLength + 2);
+ unsigned char *nextSegment =
+ writeBuffer_.addMessage(textLength + 2);
+ *nextSegment = (unsigned char) textLength;
+ unsigned char *nextDest = nextSegment + 1;
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> polyTextDeltaCache);
+ *nextDest++ = cValue;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, textLength);
+
+ nextDest += textLength;
+ }
+ }
+ outputLength += addedLength;
+ unsigned int mod4 = (addedLength & 0x3);
+ if (mod4)
+ {
+ unsigned int extra = 4 - mod4;
+ unsigned char *nextDest = writeBuffer_.addMessage(extra);
+ for (unsigned int i = 0; i < extra; i++)
+ *nextDest++ = 0;
+ outputLength += extra;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyText16:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyText16);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> polyTextCacheX);
+ clientCache_ -> polyTextLastX += value;
+ clientCache_ -> polyTextLastX &= 0xffff;
+ PutUINT(clientCache_ -> polyTextLastX, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> polyTextCacheY);
+ clientCache_ -> polyTextLastY += value;
+ clientCache_ -> polyTextLastY &= 0xffff;
+ PutUINT(clientCache_ -> polyTextLastY, outputMessage + 14, bigEndian_);
+ unsigned int addedLength = 0;
+ writeBuffer_.registerPointer(&outputMessage);
+ for (;;)
+ {
+ decodeBuffer.decodeBoolValue(value);
+ if (!value)
+ break;
+ unsigned int textLength;
+ decodeBuffer.decodeValue(textLength, 8);
+ if (textLength == 255)
+ {
+ addedLength += 5;
+ unsigned char *nextSegment = writeBuffer_.addMessage(5);
+ *nextSegment = (unsigned char) textLength;
+ decodeBuffer.decodeCachedValue(value, 29, clientCache_ -> polyTextFontCache);
+ PutULONG(value, nextSegment + 1, 1);
+ }
+ else
+ {
+ addedLength += (textLength * 2 + 2);
+ unsigned char *nextSegment =
+ writeBuffer_.addMessage(textLength * 2 + 2);
+ *nextSegment = (unsigned char) textLength;
+ unsigned char *nextDest = nextSegment + 1;
+ decodeBuffer.decodeCachedValue(cValue, 8, clientCache_ -> polyTextDeltaCache);
+ *nextDest++ = cValue;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, textLength * 2);
+
+ nextDest += textLength * 2;
+ }
+ }
+ outputLength += addedLength;
+
+ unsigned int mod4 = (addedLength & 0x3);
+ if (mod4)
+ {
+ unsigned int extra = 4 - mod4;
+ unsigned char *nextDest = writeBuffer_.addMessage(extra);
+ for (unsigned int i = 0; i < extra; i++)
+ *nextDest++ = 0;
+ outputLength += extra;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ImageText8:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ImageText8);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int textLength;
+ decodeBuffer.decodeCachedValue(textLength, 8,
+ clientCache_ -> imageTextLengthCache, 4);
+ outputLength = 16 + RoundUp4(textLength);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) textLength;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> imageTextCacheX);
+ clientCache_ -> imageTextLastX += value;
+ clientCache_ -> imageTextLastX &= 0xffff;
+ PutUINT(clientCache_ -> imageTextLastX, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> imageTextCacheY);
+ clientCache_ -> imageTextLastY += value;
+ clientCache_ -> imageTextLastY &= 0xffff;
+ PutUINT(clientCache_ -> imageTextLastY, outputMessage + 14, bigEndian_);
+ unsigned char *nextDest = outputMessage + 16;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, textLength);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ImageText16:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_ImageText16);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int textLength;
+ decodeBuffer.decodeCachedValue(textLength, 8,
+ clientCache_ -> imageTextLengthCache, 4);
+ outputLength = 16 + RoundUp4(textLength * 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ outputMessage[1] = (unsigned char) textLength;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> imageTextCacheX);
+ clientCache_ -> imageTextLastX += value;
+ clientCache_ -> imageTextLastX &= 0xffff;
+ PutUINT(clientCache_ -> imageTextLastX, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> imageTextCacheY);
+ clientCache_ -> imageTextLastY += value;
+ clientCache_ -> imageTextLastY &= 0xffff;
+ PutUINT(clientCache_ -> imageTextLastY, outputMessage + 14, bigEndian_);
+ unsigned char *nextDest = outputMessage + 16;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, textLength * 2);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_InternAtom:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_InternAtom);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ break;
+ }
+
+ unsigned int nameLength;
+ decodeBuffer.decodeValue(nameLength, 16, 6);
+ outputLength = RoundUp4(nameLength) + 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(nameLength, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeBoolValue(value);
+ outputMessage[1] = (unsigned char) value;
+ unsigned char *nextDest = outputMessage + 8;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, nameLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_ListExtensions:
+ {
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_ListFonts:
+ {
+ unsigned int textLength;
+ decodeBuffer.decodeValue(textLength, 16, 6);
+ outputLength = 8 + RoundUp4(textLength);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(textLength, outputMessage + 6, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 6);
+ PutUINT(value, outputMessage + 4, bigEndian_);
+ unsigned char* nextDest = outputMessage + 8;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, textLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_LookupColor:
+ case X_AllocNamedColor:
+ {
+ unsigned int textLength;
+ decodeBuffer.decodeValue(textLength, 16, 6);
+ outputLength = 12 + RoundUp4(textLength);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> colormapCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ PutUINT(textLength, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, textLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_MapWindow:
+ case X_UnmapWindow:
+ case X_MapSubwindows:
+ case X_GetWindowAttributes:
+ case X_DestroyWindow:
+ case X_DestroySubwindows:
+ case X_QueryPointer:
+ case X_QueryTree:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ if (outputOpcode == X_DestroyWindow) // Since ProtoStep7 (#issue 108)
+ {
+ decodeBuffer.decodeFreeXidValue(value, clientCache_ -> freeWindowCache);
+ }
+ else
+ {
+ decodeBuffer.decodeXidValue(value, clientCache_ -> windowCache);
+ }
+
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ if (outputOpcode == X_QueryPointer ||
+ outputOpcode == X_GetWindowAttributes ||
+ outputOpcode == X_QueryTree)
+ {
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ }
+ break;
+ case X_OpenFont:
+ {
+ unsigned int nameLength;
+ decodeBuffer.decodeValue(nameLength, 16, 7);
+ outputLength = RoundUp4(12 + nameLength);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(nameLength, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 29, 5);
+ clientCache_ -> lastFont += value;
+ clientCache_ -> lastFont &= 0x1fffffff;
+ PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeTextData(nextDest, nameLength);
+ }
+ break;
+ case X_PolyFillRectangle:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyFillRectangle);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0, lastWidth = 0, lastHeight = 0;
+ unsigned int numRectangles = 0;
+
+ for (;;)
+ {
+ outputLength += 8;
+ writeBuffer_.addMessage(8);
+ unsigned char *nextDest = outputMessage + 12 +
+ (numRectangles << 3);
+ numRectangles++;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillRectangleCacheX[index], 8);
+ value += lastX;
+ PutUINT(value, nextDest, bigEndian_);
+ lastX = value;
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillRectangleCacheY[index], 8);
+ value += lastY;
+ PutUINT(value, nextDest, bigEndian_);
+ lastY = value;
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillRectangleCacheWidth[index], 8);
+ value += lastWidth;
+ PutUINT(value, nextDest, bigEndian_);
+ lastWidth = value;
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillRectangleCacheHeight[index], 8);
+ value += lastHeight;
+ PutUINT(value, nextDest, bigEndian_);
+ lastHeight = value;
+ nextDest += 2;
+
+ if (++index == 4) index = 0;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (!value) break;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyFillArc:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyFillArc);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0,
+ lastWidth = 0, lastHeight = 0,
+ lastAngle1 = 0, lastAngle2 = 0;
+
+ unsigned int numArcs = 0;
+
+ for (;;)
+ {
+ outputLength += 12;
+ writeBuffer_.addMessage(12);
+
+ unsigned char *nextDest = outputMessage + 12 +
+ (numArcs * 12);
+ numArcs++;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheX[index], 8);
+ value += lastX;
+ PutUINT(value, nextDest, bigEndian_);
+ lastX = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheY[index], 8);
+ value += lastY;
+ PutUINT(value, nextDest, bigEndian_);
+ lastY = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheWidth[index], 8);
+ value += lastWidth;
+ PutUINT(value, nextDest, bigEndian_);
+ lastWidth = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheHeight[index], 8);
+ value += lastHeight;
+ PutUINT(value, nextDest, bigEndian_);
+ lastHeight = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheAngle1[index], 8);
+ value += lastAngle1;
+ PutUINT(value, nextDest, bigEndian_);
+ lastAngle1 = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyFillArcCacheAngle2[index], 8);
+ value += lastAngle2;
+ PutUINT(value, nextDest, bigEndian_);
+ lastAngle2 = value;
+ nextDest += 2;
+
+ if (++index == 2) index = 0;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (!value) break;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyArc:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyArc);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ writeBuffer_.registerPointer(&outputMessage);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0,
+ lastWidth = 0, lastHeight = 0,
+ lastAngle1 = 0, lastAngle2 = 0;
+
+ unsigned int numArcs = 0;
+
+ for (;;)
+ {
+ outputLength += 12;
+ writeBuffer_.addMessage(12);
+
+ unsigned char *nextDest = outputMessage + 12 +
+ (numArcs * 12);
+ numArcs++;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheX[index], 8);
+ value += lastX;
+ PutUINT(value, nextDest, bigEndian_);
+ lastX = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheY[index], 8);
+ value += lastY;
+ PutUINT(value, nextDest, bigEndian_);
+ lastY = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheWidth[index], 8);
+ value += lastWidth;
+ PutUINT(value, nextDest, bigEndian_);
+ lastWidth = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheHeight[index], 8);
+ value += lastHeight;
+ PutUINT(value, nextDest, bigEndian_);
+ lastHeight = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheAngle1[index], 8);
+ value += lastAngle1;
+ PutUINT(value, nextDest, bigEndian_);
+ lastAngle1 = value;
+ nextDest += 2;
+
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyArcCacheAngle2[index], 8);
+ value += lastAngle2;
+ PutUINT(value, nextDest, bigEndian_);
+ lastAngle2 = value;
+ nextDest += 2;
+
+ if (++index == 2) index = 0;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (!value) break;
+ }
+ writeBuffer_.unregisterPointer();
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyPoint:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyPoint);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int numPoints;
+ decodeBuffer.decodeValue(numPoints, 16, 4);
+ outputLength = (numPoints << 2) + 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned int relativeCoordMode;
+ decodeBuffer.decodeBoolValue(relativeCoordMode);
+ outputMessage[1] = (unsigned char) relativeCoordMode;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+
+ for (unsigned int i = 0; i < numPoints; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyPointCacheX[index], 8);
+ lastX += value;
+ PutUINT(lastX, nextDest, bigEndian_);
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyPointCacheY[index], 8);
+ lastY += value;
+ PutUINT(lastY, nextDest, bigEndian_);
+ nextDest += 2;
+
+ if (++index == 2) index = 0;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyLine:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolyLine);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int numPoints;
+ decodeBuffer.decodeValue(numPoints, 16, 4);
+ outputLength = (numPoints << 2) + 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ unsigned int relativeCoordMode;
+ decodeBuffer.decodeBoolValue(relativeCoordMode);
+ outputMessage[1] = (unsigned char) relativeCoordMode;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+
+ unsigned int index = 0;
+ unsigned int lastX = 0, lastY = 0;
+
+ for (unsigned int i = 0; i < numPoints; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyLineCacheX[index], 8);
+ lastX += value;
+ PutUINT(lastX, nextDest, bigEndian_);
+ nextDest += 2;
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyLineCacheY[index], 8);
+ lastY += value;
+ PutUINT(lastY, nextDest, bigEndian_);
+ nextDest += 2;
+
+ if (++index == 2) index = 0;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PolyRectangle:
+ {
+ unsigned int numRectangles;
+ decodeBuffer.decodeValue(numRectangles, 16, 3);
+ outputLength = (numRectangles << 3) + 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ for (unsigned int i = 0; i < numRectangles; i++)
+ for (unsigned int k = 0; k < 4; k++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> polyRectangleGeomCache[k], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ }
+ break;
+ case X_PolySegment:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PolySegment);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int numSegments;
+ decodeBuffer.decodeValue(numSegments, 16, 4);
+ outputLength = (numSegments << 3) + 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+
+ for (numSegments *= 2; numSegments; numSegments--)
+ {
+ unsigned int index;
+ decodeBuffer.decodeBoolValue(index);
+ unsigned int x;
+ decodeBuffer.decodeCachedValue(x, 16,
+ clientCache_ -> polySegmentCacheX, 6);
+ x += clientCache_ -> polySegmentLastX[index];
+ PutUINT(x, nextDest, bigEndian_);
+ nextDest += 2;
+
+ unsigned int y;
+ decodeBuffer.decodeCachedValue(y, 16,
+ clientCache_ -> polySegmentCacheY, 6);
+ y += clientCache_ -> polySegmentLastY[index];
+ PutUINT(y, nextDest, bigEndian_);
+ nextDest += 2;
+
+ clientCache_ -> polySegmentLastX[clientCache_ -> polySegmentCacheIndex] = x;
+ clientCache_ -> polySegmentLastY[clientCache_ -> polySegmentCacheIndex] = y;
+
+ if (clientCache_ -> polySegmentCacheIndex == 1)
+ clientCache_ -> polySegmentCacheIndex = 0;
+ else
+ clientCache_ -> polySegmentCacheIndex = 1;
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_PutImage:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_PutImage);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+
+ if (outputOpcode == X_PutImage)
+ {
+ handleImage(outputOpcode, outputMessage, outputLength);
+ }
+ }
+ break;
+ case X_QueryBestSize:
+ {
+ outputLength = 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[1] = (unsigned char)value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 8);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeValue(value, 16, 8);
+ PutUINT(value, outputMessage + 10, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_QueryColors:
+ {
+ // Differential or plain data compression?
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value)
+ {
+ unsigned int numColors;
+ decodeBuffer.decodeValue(numColors, 16, 5);
+ outputLength = (numColors << 2) + 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> colormapCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ unsigned int predictedPixel = clientCache_ -> queryColorsLastPixel;
+ for (unsigned int i = 0; i < numColors; i++)
+ {
+ unsigned int pixel;
+ decodeBuffer.decodeBoolValue(value);
+ if (value)
+ pixel = predictedPixel;
+ else
+ decodeBuffer.decodeValue(pixel, 32, 9);
+ PutULONG(pixel, nextDest, bigEndian_);
+ if (i == 0)
+ clientCache_ -> queryColorsLastPixel = pixel;
+ predictedPixel = pixel + 1;
+ nextDest += 4;
+ }
+ }
+ else
+ {
+ // Request length.
+ unsigned int requestLength;
+ decodeBuffer.decodeValue(requestLength, 16, 10);
+ outputLength = (requestLength << 2);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ const unsigned char *compressedData = NULL;
+ unsigned int compressedDataSize = 0;
+
+ int decompressed = handleDecompress(decodeBuffer, outputOpcode, 4,
+ outputMessage, outputLength, compressedData,
+ compressedDataSize);
+ if (decompressed < 0)
+ {
+ return -1;
+ }
+ }
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_QueryExtension:
+ {
+ unsigned int nameLength;
+ decodeBuffer.decodeValue(nameLength, 16, 6);
+ outputLength = 8 + RoundUp4(nameLength);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(nameLength, outputMessage + 4, bigEndian_);
+ unsigned char *nextDest = outputMessage + 8;
+ for (unsigned int i = 0; i < nameLength; i++)
+ {
+ decodeBuffer.decodeValue(value, 8);
+ *nextDest++ = (unsigned char) value;
+ }
+
+ unsigned int hide = 0;
+
+ #ifdef HIDE_MIT_SHM_EXTENSION
+
+ if (!strncmp((char *) outputMessage + 8, "MIT-SHM", 7))
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Going to hide MIT-SHM extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ #endif
+
+ #ifdef HIDE_BIG_REQUESTS_EXTENSION
+
+ if (!strncmp((char *) outputMessage + 8, "BIG-REQUESTS", 12))
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Going to hide BIG-REQUESTS extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ #endif
+
+ #ifdef HIDE_XKEYBOARD_EXTENSION
+
+ else if (!strncmp((char *) outputMessage + 8, "XKEYBOARD", 9))
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Going to hide XKEYBOARD extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ #endif
+
+ #ifdef HIDE_XFree86_Bigfont_EXTENSION
+
+ else if (!strncmp((char *) outputMessage + 8, "XFree86-Bigfont", 15))
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Going to hide XFree86-Bigfont extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ #endif
+
+ //
+ // This is if you want to experiment disabling SHAPE extensions.
+ //
+
+ #ifdef HIDE_SHAPE_EXTENSION
+
+ if (!strncmp((char *) outputMessage + 8, "SHAPE", 5))
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Going to hide SHAPE extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ #endif
+
+ //
+ // Check if user disabled RENDER extension.
+ //
+
+ if (control -> HideRender == 1 &&
+ strncmp((char *) outputMessage + 8, "RENDER", 6) == 0)
+ {
+ #ifdef TEST
+ *logofs << "handleWrite: Going to hide RENDER extension in reply.\n"
+ << logofs_flush;
+ #endif
+
+ hide = 1;
+ }
+
+ unsigned int extension = 0;
+
+ if (strncmp((char *) outputMessage + 8, "SHAPE", 5) == 0)
+ {
+ extension = X_NXInternalShapeExtension;
+ }
+ else if (strncmp((char *) outputMessage + 8, "RENDER", 6) == 0)
+ {
+ extension = X_NXInternalRenderExtension;
+ }
+
+ sequenceQueue_.push(clientSequence_, outputOpcode,
+ outputOpcode, hide, extension);
+ }
+ break;
+ case X_QueryFont:
+ {
+ outputLength = 8;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 29, 5);
+ clientCache_ -> lastFont += value;
+ clientCache_ -> lastFont &= 0x1fffffff;
+ PutULONG(clientCache_ -> lastFont, outputMessage + 4, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_SetClipRectangles:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_SetClipRectangles);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ break;
+ }
+
+ unsigned int numRectangles;
+
+ // Since ProtoStep9 (#issue 108)
+ decodeBuffer.decodeValue(numRectangles, 15, 4);
+
+ outputLength = (numRectangles << 3) + 12;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeValue(value, 2);
+ outputMessage[1] = (unsigned char) value;
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> setClipRectanglesXCache, 8);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> setClipRectanglesYCache, 8);
+ PutUINT(value, outputMessage + 10, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ for (unsigned int i = 0; i < numRectangles; i++)
+ {
+ for (unsigned int k = 0; k < 4; k++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache_ -> setClipRectanglesGeomCache[k], 8);
+ PutUINT(value, nextDest, bigEndian_);
+ nextDest += 2;
+ }
+ }
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_SetDashes:
+ {
+ unsigned int numDashes;
+ decodeBuffer.decodeCachedValue(numDashes, 16,
+ clientCache_ -> setDashesLengthCache, 5);
+ outputLength = 12 + RoundUp4(numDashes);
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ PutUINT(numDashes, outputMessage + 10, bigEndian_);
+ decodeBuffer.decodeXidValue(value, clientCache_ -> gcCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> setDashesOffsetCache, 5);
+ PutUINT(value, outputMessage + 8, bigEndian_);
+ unsigned char *nextDest = outputMessage + 12;
+ for (unsigned int i = 0; i < numDashes; i++)
+ {
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> setDashesDashCache_[i & 1], 5);
+ *nextDest++ = cValue;
+ }
+ }
+ break;
+ case X_SetSelectionOwner:
+ {
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> setSelectionOwnerCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> getSelectionOwnerSelectionCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> setSelectionOwnerTimestampCache, 9);
+ PutULONG(value, outputMessage + 12, bigEndian_);
+ }
+ break;
+ case X_TranslateCoords:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_TranslateCoords);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ break;
+ }
+
+ outputLength = 16;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> translateCoordsSrcCache, 9);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 29,
+ clientCache_ -> translateCoordsDstCache, 9);
+ PutULONG(value, outputMessage + 8, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> translateCoordsXCache, 8);
+ PutUINT(value, outputMessage + 12, bigEndian_);
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> translateCoordsYCache, 8);
+ PutUINT(value, outputMessage + 14, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_GetImage:
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_GetImage);
+
+ if (handleDecodeCached(decodeBuffer, clientCache_, messageStore,
+ outputMessage, outputLength))
+ {
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ break;
+ }
+
+ outputLength = 20;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+ // Format.
+ unsigned int format;
+ decodeBuffer.decodeValue(format, 2);
+ outputMessage[1] = (unsigned char) format;
+ // Drawable.
+ decodeBuffer.decodeXidValue(value, clientCache_ -> drawableCache);
+ PutULONG(value, outputMessage + 4, bigEndian_);
+ // X.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> putImageXCache, 8);
+ clientCache_ -> putImageLastX += value;
+ clientCache_ -> putImageLastX &= 0xffff;
+ PutUINT(clientCache_ -> putImageLastX, outputMessage + 8, bigEndian_);
+ // Y.
+ decodeBuffer.decodeCachedValue(value, 16,
+ clientCache_ -> putImageYCache, 8);
+ clientCache_ -> putImageLastY += value;
+ clientCache_ -> putImageLastY &= 0xffff;
+ PutUINT(clientCache_ -> putImageLastY, outputMessage + 10, bigEndian_);
+ // Width.
+ unsigned int width;
+ decodeBuffer.decodeCachedValue(width, 16,
+ clientCache_ -> putImageWidthCache, 8);
+ PutUINT(width, outputMessage + 12, bigEndian_);
+ // Height.
+ unsigned int height;
+ decodeBuffer.decodeCachedValue(height, 16,
+ clientCache_ -> putImageHeightCache, 8);
+ PutUINT(height, outputMessage + 14, bigEndian_);
+ // Plane mask.
+ decodeBuffer.decodeCachedValue(value, 32,
+ clientCache_ -> getImagePlaneMaskCache, 5);
+ PutULONG(value, outputMessage + 16, bigEndian_);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+
+ handleSave(messageStore, outputMessage, outputLength);
+ }
+ break;
+ case X_GetPointerMapping:
+ {
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ case X_GetKeyboardControl:
+ {
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode);
+ }
+ break;
+ default:
+ {
+ if (outputOpcode == opcodeStore_ -> renderExtension)
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalRenderExtension);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> shapeExtension)
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalShapeExtension);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> putPackedImage)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding packed image request for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXPutPackedImage);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+
+ if (outputOpcode == opcodeStore_ -> putPackedImage)
+ {
+ handleImage(outputOpcode, outputMessage, outputLength);
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> setUnpackColormap)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding set unpack colormap request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackColormap);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ //
+ // Message could have been split.
+ //
+
+ if (outputOpcode == opcodeStore_ -> setUnpackColormap)
+ {
+ handleColormap(outputOpcode, outputMessage, outputLength);
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> setUnpackAlpha)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding unpack alpha request for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackAlpha);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ //
+ // Message could have been split.
+ //
+
+ if (outputOpcode == opcodeStore_ -> setUnpackAlpha)
+ {
+ handleAlpha(outputOpcode, outputMessage, outputLength);
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> setUnpackGeometry)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding set unpack geometry request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXSetUnpackGeometry);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+
+ handleGeometry(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> startSplit)
+ {
+ handleStartSplitRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> endSplit)
+ {
+ handleEndSplitRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> commitSplit)
+ {
+ int result = handleCommitSplitRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+
+ //
+ // Check if message has been successfully
+ // extracted from the split store. In this
+ // case post-process it in the usual way.
+ //
+
+ if (result > 0)
+ {
+ if (outputOpcode == opcodeStore_ -> putPackedImage ||
+ outputOpcode == X_PutImage)
+ {
+ handleImage(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> setUnpackColormap)
+ {
+ handleColormap(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> setUnpackAlpha)
+ {
+ handleAlpha(outputOpcode, outputMessage, outputLength);
+ }
+ }
+ else if (result < 0)
+ {
+ return -1;
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> abortSplit)
+ {
+ handleAbortSplitRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> finishSplit)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding finish split request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> resourceCache);
+
+ handleNullRequest(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> freeSplit)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding free split request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> resourceCache);
+
+ handleNullRequest(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> freeUnpack)
+ {
+ #ifdef DEBUG
+ *logofs << "handleWrite: Decoding free unpack request "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache_ -> resourceCache);
+
+ #ifdef DEBUG
+ *logofs << "handleWrite: Freeing unpack state for resource "
+ << (unsigned int) cValue << ".\n" << logofs_flush;
+ #endif
+
+ handleUnpackStateRemove(cValue);
+
+ handleNullRequest(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> setExposeParameters)
+ {
+ //
+ // Send expose events according to agent's wish.
+ //
+
+ decodeBuffer.decodeBoolValue(enableExpose_);
+ decodeBuffer.decodeBoolValue(enableGraphicsExpose_);
+ decodeBuffer.decodeBoolValue(enableNoExpose_);
+
+ handleNullRequest(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> getUnpackParameters)
+ {
+ //
+ // Client proxy needs the list of supported
+ // unpack methods. We would need an encode
+ // buffer, but this is in proxy, not here in
+ // channel.
+ //
+
+ #ifdef TEST
+ *logofs << "handleWrite: Sending X_GetInputFocus request for FD#"
+ << fd_ << " due to OPCODE#" << (unsigned int) outputOpcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ outputOpcode = X_GetInputFocus;
+
+ outputLength = 4;
+ outputMessage = writeBuffer_.addMessage(outputLength);
+
+ sequenceQueue_.push(clientSequence_, outputOpcode,
+ opcodeStore_ -> getUnpackParameters);
+ }
+ else if (outputOpcode == opcodeStore_ -> getControlParameters ||
+ outputOpcode == opcodeStore_ -> getCleanupParameters ||
+ outputOpcode == opcodeStore_ -> getImageParameters)
+ {
+ handleNullRequest(outputOpcode, outputMessage, outputLength);
+ }
+ else if (outputOpcode == opcodeStore_ -> getShmemParameters)
+ {
+ if (handleShmemRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> setCacheParameters)
+ {
+ if (handleCacheRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else if (outputOpcode == opcodeStore_ -> getFontParameters)
+ {
+ if (handleFontRequest(decodeBuffer, outputOpcode,
+ outputMessage, outputLength) < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ MessageStore *messageStore = clientStore_ ->
+ getRequestStore(X_NXInternalGenericRequest);
+
+ hit = handleDecode(decodeBuffer, clientCache_, messageStore,
+ outputOpcode, outputMessage, outputLength);
+ }
+ }
+ } // End of switch on opcode.
+
+ //
+ // TODO: at the moment the variable hit was being set
+ // but not used, so to avoid the corresponding warning
+ // this logging block has been added.
+ // This code will probably be optimized away when none
+ // of the defines is set, but if there is no additional
+ // use for the hit variable in the future, then maybe
+ // it could be removed completely.
+ //
+
+ if (hit)
+ {
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Cached flag enabled in handled request.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // A packed image request can generate more than just
+ // a single X_PutImage. Write buffer is handled inside
+ // handleUnpack(). Cannot simply assume that the final
+ // opcode and size must be put at the buffer offset as
+ // as buffer could have been grown or could have been
+ // replaced by a scratch buffer. The same is true in
+ // the case of a shared memory image.
+ //
+
+ if (outputOpcode != 0)
+ {
+ //
+ // Commit opcode and size to the buffer.
+ //
+
+ *outputMessage = (unsigned char) outputOpcode;
+
+ PutUINT(outputLength >> 2, outputMessage + 2, bigEndian_);
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleWrite: Handled request OPCODE#"
+ << (unsigned int) outputOpcode << " ("
+ << DumpOpcode(outputOpcode) << ") for FD#"
+ << fd_ << " sequence " << clientSequence_
+ << ". " << outputLength << " bytes out.\n"
+ << logofs_flush;
+ #endif
+ }
+ #if defined(TEST) || defined(OPCODES)
+ else
+ {
+ //
+ // In case of shared memory images the log doesn't
+ // reflect the actual opcode of the request that is
+ // going to be written. It would be possible to find
+ // the opcode of the original request received from
+ // the remote proxy in member imageState_ -> opcode,
+ // but we have probably already deleted the struct.
+ //
+
+ *logofs << "handleWrite: Handled image request for FD#"
+ << fd_ << " new sequence " << clientSequence_
+ << ". " << outputLength << " bytes out.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ //
+ // Check if we produced enough data. We need to
+ // decode all the proxy messages or the decode
+ // buffer will be left in an inconsistent state,
+ // so we just update the finish flag in case of
+ // failure.
+ //
+
+ handleFlush(flush_if_needed);
+
+ } // End of while (decodeBuffer.decodeOpcodeValue(outputOpcode, 8, ...
+
+ } // End of the decoding block.
+
+ //
+ // Write any remaining data to the X connection.
+ //
+
+ if (handleFlush(flush_if_any) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Reset offset at which we read the
+ // last event looking for the shared
+ // memory completion.
+ //
+
+ if (shmemState_ != NULL)
+ {
+ shmemState_ -> checked = 0;
+ }
+
+ return 1;
+}
+
+//
+// End of handleWrite().
+//
+
+//
+// Other members.
+//
+
+int ServerChannel::handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store,
+ T_store_action action, int position, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ // Since ProtoStep7 (#issue 108)
+ splitState_.current = splitState_.resource;
+
+ handleSplitStoreAlloc(&splitResources_, splitState_.current);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Message OPCODE#"
+ << (unsigned int) store -> opcode() << " of size " << size
+ << " [split] with resource " << splitState_.current
+ << " position " << position << " and action ["
+ << DumpAction(action) << "] at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Get the MD5 of the message being
+ // split.
+ //
+
+ T_checksum checksum = NULL;
+
+ if (action != IS_HIT)
+ {
+ handleSplitChecksum(decodeBuffer, checksum);
+ }
+
+ //
+ // The method must abort the connection
+ // if it can't allocate the split.
+ //
+
+ Split *splitMessage = clientStore_ -> getSplitStore(splitState_.current) ->
+ add(store, splitState_.current, position,
+ action, checksum, buffer, size);
+
+ //
+ // If the encoding side didn't provide
+ // a checksum, then don't send the split
+ // report.
+ //
+
+ if (checksum == NULL)
+ {
+ if (action == IS_HIT)
+ {
+ splitMessage -> setState(split_loaded);
+ }
+ else
+ {
+ splitMessage -> setState(split_missed);
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ *logofs << "handleSplit: SPLIT! There are " << clientStore_ ->
+ getSplitTotalSize() << " messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+
+ clientStore_ -> dumpSplitStore(splitState_.current);
+
+ #endif
+
+ return 1;
+ }
+
+ delete [] checksum;
+
+ //
+ // Tell the split store if it must use
+ // the disk cache to retrieve and save
+ // the message.
+ //
+
+ splitMessage -> setPolicy(splitState_.load, splitState_.save);
+
+ //
+ // Try to locate the message on disk.
+ //
+
+ if (clientStore_ -> getSplitStore(splitState_.current) ->
+ load(splitMessage) == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Loaded the message "
+ << "from the image cache.\n" << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_loaded);
+ }
+ else
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: WARNING! SPLIT! Can't find the message "
+ << "in the image cache.\n" << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_missed);
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ T_timestamp startTs = getTimestamp();
+
+ *logofs << "handleSplit: SPLIT! Encoding abort "
+ << "split events for FD#" << fd_ << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Send the encoded data immediately. We
+ // want the abort split message to reach
+ // the remote proxy as soon as possible.
+ //
+
+ if (proxy -> handleAsyncFlush() < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ *logofs << "handleSplit: SPLIT! Spent "
+ << diffTimestamp(startTs, getTimestamp()) << " Ms "
+ << "handling abort split events for FD#" << fd_
+ << ".\n" << logofs_flush;
+
+ *logofs << "handleSplit: SPLIT! There are " << clientStore_ ->
+ getSplitTotalSize() << " messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+
+ clientStore_ -> dumpSplitStore(splitState_.current);
+
+ #endif
+
+ return 1;
+}
+
+int ServerChannel::handleSplit(DecodeBuffer &decodeBuffer)
+{
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Going to handle splits "
+ << "for FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ unsigned char resource;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ splitState_.current = resource;
+
+ handleSplitStoreAlloc(&splitResources_, splitState_.current);
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(splitState_.current);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Handling splits for "
+ << "resource [" << splitState_.current << "] with "
+ << splitStore -> getSize() << " elements "
+ << "in the split store.\n" << logofs_flush;
+ #endif
+
+ int result = splitStore -> receive(decodeBuffer);
+
+ if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleSplit: PANIC! Receive of split for FD#" << fd_
+ << " failed.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Receive of split for FD#" << fd_
+ << " failed.\n";
+
+ return -1;
+ }
+ else if (result == 0)
+ {
+ //
+ // The split is still incomplete. It's time
+ // to check if we need to start the house-
+ // keeping process to take care of the image
+ // cache.
+ //
+
+ if (proxy -> handleAsyncKeeperCallback() < 0)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ //
+ // Note that we don't need the resource id at the
+ // X server side and, thus, we don't provide it
+ // at the time we add split to the split store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Remote agent should "
+ << "now commit a new split for resource ["
+ << splitState_.current << "].\n"
+ << logofs_flush;
+
+ clientStore_ -> dumpCommitStore();
+
+ #endif
+
+ if (splitStore -> getSize() == 0)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! Removing split store "
+ << "for resource [" << splitState_.current
+ << "] at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ handleSplitStoreRemove(&splitResources_, splitState_.current);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: SPLIT! There are [" << clientStore_ ->
+ getSplitTotalSize() << "] messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ //
+ // If the next split is discarded, it can be
+ // that, since the beginning of the split, we
+ // have saved the message on the disk, due to
+ // a more recent split operation. This is also
+ // the case when we had to discard the message
+ // because it was locked but, since then, we
+ // completed the transferral of the split.
+ //
+
+ Split *splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage -> getAction() == is_discarded &&
+ splitMessage -> getState() == split_missed &&
+ splitStore -> load(splitMessage) == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplit: WARNING! SPLIT! Asynchronously "
+ << "loaded the message from the image cache.\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> setState(split_loaded);
+
+ #if defined(TEST) || defined(SPLIT)
+
+ T_timestamp startTs = getTimestamp();
+
+ *logofs << "handleSplit: WARNING! SPLIT! Asynchronously "
+ << "encoding abort split events for FD#" << fd_
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncSplit(fd_, splitMessage) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Send the encoded data immediately. We
+ // want the abort split message to reach
+ // the remote proxy as soon as possible.
+ //
+
+ if (proxy -> handleAsyncFlush() < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+
+ *logofs << "handleSplit: WARNING! SPLIT! Spent "
+ << diffTimestamp(startTs, getTimestamp()) << " Ms "
+ << "handling asynchronous abort split events for "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+
+ *logofs << "handleSplit: SPLIT! There are " << clientStore_ ->
+ getSplitTotalSize() << " messages and " << clientStore_ ->
+ getSplitTotalStorageSize() << " bytes to send in "
+ << "the split stores.\n" << logofs_flush;
+
+ clientStore_ -> dumpSplitStore(splitState_.current);
+
+ #endif
+ }
+ }
+ }
+
+ return 1;
+}
+
+int ServerChannel::handleSplitEvent(EncodeBuffer &encodeBuffer, Split *splitMessage)
+{
+ int resource = splitMessage -> getResource();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Going to send a "
+ << "split report for resource " << resource
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // This function is called only after the message
+ // has been searched in the disk cache. We need to
+ // inform the other side if the data transfer can
+ // start or it must be aborted to let the local
+ // side use the copy that was found on the disk.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ if (splitMessage -> getState() != split_loaded &&
+ splitMessage -> getState() != split_missed)
+ {
+ *logofs << "handleSplitEvent: PANIC! Can't find the split to be aborted.\n"
+ << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ //
+ // We need to send a boolean telling if the split
+ // was found or not, followed by the checksum of
+ // message we are referencing.
+ //
+
+ T_checksum checksum = splitMessage -> getChecksum();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Sending split report for "
+ << "checksum [" << DumpChecksum(checksum) << "].\n"
+ << logofs_flush;
+ #endif
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ encodeBuffer.encodeOpcodeValue(opcodeStore_ -> splitEvent,
+ serverCache_ -> opcodeCache);
+
+ //
+ // The encoding in older protocol versions
+ // is different but we will never try to
+ // send a split report if the remote does
+ // not support our version.
+ //
+
+ encodeBuffer.encodeCachedValue(resource, 8,
+ serverCache_ -> resourceCache);
+
+ if (splitMessage -> getState() == split_loaded)
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeOpcodeValue(splitMessage -> getStore() -> opcode(),
+ serverCache_ -> abortOpcodeCache);
+
+ encodeBuffer.encodeValue(splitMessage -> compressedSize(), 32, 14);
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ }
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ encodeBuffer.encodeValue((unsigned int) checksum[i], 8);
+ }
+
+ //
+ // Update statistics for this special opcode.
+ //
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES) || defined(INFO) || defined(SPLIT)
+ *logofs << "handleSplitEvent: SPLIT! Handled event OPCODE#"
+ << (unsigned int) opcodeStore_ -> splitEvent << " ("
+ << DumpOpcode(opcodeStore_ -> splitEvent) << ")" << " for FD#"
+ << fd_ << " sequence none. 0 bytes in, " << bits << " bits ("
+ << ((float) bits) / 8 << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ statistics -> addEventBits(opcodeStore_ -> splitEvent, 0, bits);
+
+ return 1;
+}
+
+int ServerChannel::handleAbortSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ unsigned char resource;
+
+ decodeBuffer.decodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Handling abort split "
+ << "request for FD#" << fd_ << " and resource "
+ << (unsigned) resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ int splits = 0;
+
+ SplitStore *splitStore = clientStore_ -> getSplitStore(resource);
+
+ if (splitStore != NULL)
+ {
+ //
+ // Discard from the memory cache the messages
+ // that are still incomplete and then get rid
+ // of the splits in the store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ clientStore_ -> dumpSplitStore(resource);
+
+ #endif
+
+ Split *splitMessage;
+
+ for (;;)
+ {
+ splitMessage = splitStore -> getFirstSplit();
+
+ if (splitMessage == NULL)
+ {
+ //
+ // Check if we had created the store
+ // but no message was added yet.
+ //
+
+ #ifdef WARNING
+
+ if (splits == 0)
+ {
+ *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The "
+ << "split store for resource [" << (unsigned int)
+ resource << "] is unexpectedly empty.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ break;
+ }
+
+ //
+ // Splits already aborted can't be in the
+ // split store.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+
+ if (splitMessage -> getState() == split_aborted)
+ {
+ *logofs << "handleAbortSplitRequest: PANIC! SPLIT! Found an "
+ << "aborted split in store [" << (unsigned int) resource
+ << "].\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ if (splitMessage -> getAction() == IS_HIT)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Removing the "
+ << "split from the memory cache.\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage -> getStore() -> remove(splitMessage -> getPosition(),
+ discard_checksum, use_data);
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Removing the "
+ << "split from the split store.\n"
+ << logofs_flush;
+ #endif
+
+ splitMessage = splitStore -> pop();
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleAbortSplitRequest: SPLIT! Freeing up the "
+ << "aborted split.\n" << logofs_flush;
+ #endif
+
+ delete splitMessage;
+
+ splits++;
+ }
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "handleAbortSplitRequest: WARNING! SPLIT! The "
+ << "split store for resource [" << (unsigned int)
+ resource << "] is already empty.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ handleNullRequest(opcode, buffer, size);
+
+ return (splits > 0);
+}
+
+int ServerChannel::handleCommitSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // Get request type and position of the image
+ // to commit.
+ //
+
+ unsigned char request;
+
+ decodeBuffer.decodeOpcodeValue(request, clientCache_ -> opcodeCache);
+
+ unsigned int diffCommit;
+
+ decodeBuffer.decodeValue(diffCommit, 32, 5);
+
+ splitState_.commit += diffCommit;
+
+ unsigned char resource = 0;
+ unsigned int commit = 1;
+
+ //
+ // Send the resource id and the commit flag.
+ // The resource id is ignored at the moment.
+ // The message will be handled based on the
+ // resource id that was sent together with
+ // the original message.
+ //
+
+ decodeBuffer.decodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ decodeBuffer.decodeBoolValue(commit);
+
+ Split *split = handleSplitCommitRemove(request, resource, splitState_.commit);
+
+ if (split == NULL)
+ {
+ return -1;
+ }
+
+ clientStore_ -> getCommitStore() -> update(split);
+
+ if (commit == 1)
+ {
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleCommitSplitRequest: SPLIT! Handling split commit "
+ << "for FD#" << fd_ << " with commit " << commit
+ << " request " << (unsigned) request << " resource "
+ << (unsigned) resource << " and position "
+ << splitState_.commit << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Allocate as many bytes in the write
+ // buffer as the final length of the
+ // message in uncompressed form.
+ //
+
+ size = split -> plainSize();
+
+ buffer = writeBuffer_.addMessage(size);
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleCommitSplitRequest: SPLIT! Prepared an "
+ << "outgoing buffer of " << size << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ if (clientStore_ -> getCommitStore() -> expand(split, buffer, size) < 0)
+ {
+ writeBuffer_.removeMessage(size);
+
+ commit = 0;
+ }
+ }
+
+ //
+ // Free the split.
+ //
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleCommitSplitRequest: SPLIT! Freeing up the "
+ << "committed split.\n" << logofs_flush;
+ #endif
+
+ delete split;
+
+ //
+ // Discard the operation and send a null
+ // message.
+ //
+
+ if (commit == 0)
+ {
+ handleNullRequest(opcode, buffer, size);
+ }
+ else
+ {
+ //
+ // Save the sequence number to be able
+ // to mask any error generated by the
+ // request.
+ //
+
+ updateCommitQueue(clientSequence_);
+
+ //
+ // Now in the write buffer there is
+ // a copy of this request.
+ //
+
+ opcode = request;
+ }
+
+ return commit;
+}
+
+int ServerChannel::handleGeometry(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // Replace the old geometry and taint
+ // the message into a X_NoOperation.
+ //
+
+ int resource = *(buffer + 1);
+
+ #ifdef TEST
+ *logofs << "handleGeometry: Setting new unpack geometry "
+ << "for resource " << resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ handleUnpackStateInit(resource);
+
+ handleUnpackAllocGeometry(resource);
+
+ unpackState_[resource] -> geometry -> depth1_bpp = *(buffer + 4);
+ unpackState_[resource] -> geometry -> depth4_bpp = *(buffer + 5);
+ unpackState_[resource] -> geometry -> depth8_bpp = *(buffer + 6);
+ unpackState_[resource] -> geometry -> depth16_bpp = *(buffer + 7);
+ unpackState_[resource] -> geometry -> depth24_bpp = *(buffer + 8);
+ unpackState_[resource] -> geometry -> depth32_bpp = *(buffer + 9);
+
+ unpackState_[resource] -> geometry -> red_mask = GetULONG(buffer + 12, bigEndian_);
+ unpackState_[resource] -> geometry -> green_mask = GetULONG(buffer + 16, bigEndian_);
+ unpackState_[resource] -> geometry -> blue_mask = GetULONG(buffer + 20, bigEndian_);
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleColormap(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ //
+ // Replace the old colormap and taint
+ // the message into a X_NoOperation.
+ //
+
+ int resource = *(buffer + 1);
+
+ #ifdef TEST
+ *logofs << "handleColormap: Setting new unpack colormap "
+ << "for resource " << resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ handleUnpackStateInit(resource);
+
+ handleUnpackAllocColormap(resource);
+
+ //
+ // New protocol versions send the alpha
+ // data in compressed form.
+ //
+
+ //
+ // Since ProtoStep7 (#issue 108)
+ //
+
+ { // An anonymous block is used here to limit the scope of local variables
+ unsigned int packed = GetULONG(buffer + 8, bigEndian_);
+ unsigned int unpacked = GetULONG(buffer + 12, bigEndian_);
+
+ validateSize("colormap", packed, unpacked, 16, size);
+
+ if (unpackState_[resource] -> colormap -> entries != unpacked >> 2 &&
+ unpackState_[resource] -> colormap -> data != NULL)
+ {
+ #ifdef TEST
+ *logofs << "handleColormap: Freeing previously allocated "
+ << "unpack colormap data.\n" << logofs_flush;
+ #endif
+
+ delete [] unpackState_[resource] -> colormap -> data;
+
+ unpackState_[resource] -> colormap -> data = NULL;
+ unpackState_[resource] -> colormap -> entries = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "handleColormap: Setting " << unpacked
+ << " bytes of unpack colormap data for resource "
+ << resource << ".\n" << logofs_flush;
+ #endif
+
+ if (unpackState_[resource] -> colormap -> data == NULL)
+ {
+ unpackState_[resource] -> colormap -> data =
+ (unsigned int *) new unsigned char[unpacked];
+
+ if (unpackState_[resource] -> colormap -> data == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleColormap: PANIC! Can't allocate "
+ << unpacked << " entries for unpack colormap data "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ goto handleColormapEnd;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleColormap: Size of new colormap data is "
+ << unpacked << ".\n" << logofs_flush;
+ #endif
+ }
+
+ unsigned int method = *(buffer + 4);
+
+ if (method == PACK_COLORMAP)
+ {
+ if (UnpackColormap(method, buffer + 16, packed,
+ (unsigned char *) unpackState_[resource] ->
+ colormap -> data, unpacked) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleColormap: PANIC! Can't unpack " << packed
+ << " bytes to " << unpacked << " entries for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ delete [] unpackState_[resource] -> colormap -> data;
+
+ unpackState_[resource] -> colormap -> data = NULL;
+ unpackState_[resource] -> colormap -> entries = 0;
+
+ goto handleColormapEnd;
+ }
+ }
+ else
+ {
+ memcpy((unsigned char *) unpackState_[resource] ->
+ colormap -> data, buffer + 16, unpacked);
+ }
+
+ unpackState_[resource] -> colormap -> entries = unpacked >> 2;
+
+ #if defined(DEBUG) && defined(DUMP)
+
+ *logofs << "handleColormap: Dumping colormap entries:\n"
+ << logofs_flush;
+
+ const unsigned char *p = (const unsigned char *) unpackState_[resource] -> colormap -> data;
+
+ for (unsigned int i = 0; i < unpackState_[resource] ->
+ colormap -> entries; i++)
+ {
+ *logofs << "handleColormap: [" << i << "] ["
+ << (void *) ((int) p[i]) << "].\n"
+ << logofs_flush;
+ }
+
+ #endif
+ } // end anonymous block
+
+handleColormapEnd:
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleAlpha(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ int resource = *(buffer + 1);
+
+ #ifdef TEST
+ *logofs << "handleAlpha: Setting new unpack alpha "
+ << "for resource " << resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ handleUnpackStateInit(resource);
+
+ handleUnpackAllocAlpha(resource);
+
+ //
+ // New protocol versions send the alpha
+ // data in compressed form.
+ //
+
+ //
+ // Since ProtoStep7 (#issue 108)
+ //
+
+ { // An anonymous block is used here to limit the scope of local variables
+ unsigned int packed = GetULONG(buffer + 8, bigEndian_);
+ unsigned int unpacked = GetULONG(buffer + 12, bigEndian_);
+
+ validateSize("alpha", packed, unpacked, 16, size);
+
+ if (unpackState_[resource] -> alpha -> entries != unpacked &&
+ unpackState_[resource] -> alpha -> data != NULL)
+ {
+ #ifdef TEST
+ *logofs << "handleAlpha: Freeing previously allocated "
+ << "unpack alpha data.\n" << logofs_flush;
+ #endif
+
+ delete [] unpackState_[resource] -> alpha -> data;
+
+ unpackState_[resource] -> alpha -> data = NULL;
+ unpackState_[resource] -> alpha -> entries = 0;
+ }
+
+ #ifdef TEST
+ *logofs << "handleAlpha: Setting " << unpacked
+ << " bytes of unpack alpha data for resource "
+ << resource << ".\n" << logofs_flush;
+ #endif
+
+ if (unpackState_[resource] -> alpha -> data == NULL)
+ {
+ unpackState_[resource] -> alpha -> data = new unsigned char[unpacked];
+
+ if (unpackState_[resource] -> alpha -> data == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleAlpha: PANIC! Can't allocate "
+ << unpacked << " entries for unpack alpha data "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ goto handleAlphaEnd;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleAlpha: Size of new alpha data is "
+ << unpacked << ".\n" << logofs_flush;
+ #endif
+ }
+
+ unsigned int method = *(buffer + 4);
+
+ if (method == PACK_ALPHA)
+ {
+ if (UnpackAlpha(method, buffer + 16, packed,
+ unpackState_[resource] -> alpha ->
+ data, unpacked) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleAlpha: PANIC! Can't unpack " << packed
+ << " bytes to " << unpacked << " entries for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ delete [] unpackState_[resource] -> alpha -> data;
+
+ unpackState_[resource] -> alpha -> data = NULL;
+ unpackState_[resource] -> alpha -> entries = 0;
+
+ goto handleAlphaEnd;
+ }
+ }
+ else
+ {
+ memcpy((unsigned char *) unpackState_[resource] ->
+ alpha -> data, buffer + 16, unpacked);
+ }
+
+ unpackState_[resource] -> alpha -> entries = unpacked;
+
+ #if defined(DEBUG) && defined(DUMP)
+
+ *logofs << "handleAlpha: Dumping alpha entries:\n"
+ << logofs_flush;
+
+ const unsigned char *p = unpackState_[resource] -> alpha -> data;
+
+ for (unsigned int i = 0; i < unpackState_[resource] ->
+ alpha -> entries; i++)
+ {
+ *logofs << "handleAlpha: [" << i << "] ["
+ << (void *) ((int) p[i]) << "].\n"
+ << logofs_flush;
+ }
+
+ #endif
+ } //end anonymous block
+
+handleAlphaEnd:
+
+ handleCleanAndNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleImage(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ int result = 1;
+
+ //
+ // Save the original opcode together with
+ // the image state so we can later find if
+ // this is a plain or a packed image when
+ // moving data to the shared memory area.
+ //
+
+ handleImageStateAlloc(opcode);
+
+ if (opcode == opcodeStore_ -> putPackedImage)
+ {
+ //
+ // Unpack the image and put a X_PutImage in a
+ // new buffer. Save the expected output size,
+ // so, in the case of a decoding error we can
+ // still update the statistics.
+ //
+
+ int length = GetULONG(buffer + 20, bigEndian_);
+
+ #ifdef TEST
+ *logofs << "handleImage: Sending image for FD#" << fd_
+ << " due to OPCODE#" << (unsigned int) opcode << " with "
+ << GetULONG(buffer + 16, bigEndian_) << " bytes packed "
+ << "and " << GetULONG(buffer + 20, bigEndian_)
+ << " bytes unpacked.\n" << logofs_flush;
+ #endif
+
+ statistics -> addPackedBytesIn(size);
+
+ result = handleUnpack(opcode, buffer, size);
+
+ if (result < 0)
+ {
+ //
+ // Recover from the error. Send a X_NoOperation
+ // to keep the sequence counter in sync with
+ // the remote peer.
+ //
+
+ size = 4;
+ buffer = writeBuffer_.addMessage(size);
+
+ *buffer = X_NoOperation;
+
+ PutUINT(size >> 2, buffer + 2, bigEndian_);
+
+ #ifdef PANIC
+ *logofs << "handleImage: PANIC! Sending X_NoOperation for FD#"
+ << fd_ << " to recover from failed unpack.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Set the output length to reflect the amount of
+ // data that would have been produced by unpacking
+ // the image. This is advisable to keep the count-
+ // ers in sync with those at remote proxy. Setting
+ // the size here doesn't have any effect on the
+ // size of data sent to the X server as the actual
+ // size will be taken from the content of the write
+ // buffer.
+ //
+
+ size = length;
+ }
+
+ statistics -> addPackedBytesOut(size);
+
+ //
+ // Refrain the write loop from putting
+ // opcode and size in the output buffer.
+ //
+
+ opcode = 0;
+ }
+
+ //
+ // Now image is unpacked as a X_PutImage
+ // in write buffer. Check if we can send
+ // the image using the MIT-SHM extension.
+ //
+
+ if (result > 0)
+ {
+ result = handleShmem(opcode, buffer, size);
+
+ //
+ // We already put opcode and size in
+ // the resulting buffer.
+ //
+
+ if (result > 0)
+ {
+ opcode = 0;
+ }
+ }
+
+ return 1;
+}
+
+int ServerChannel::handleMotion(EncodeBuffer &encodeBuffer)
+{
+ #if defined(TEST) || defined(INFO)
+
+ if (lastMotion_[0] == '\0')
+ {
+ *logofs << "handleMotion: PANIC! No motion events to send "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleMotion: Sending motion events for FD#"
+ << fd_ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Replicate code from read loop. When have
+ // time and wish, try to split everything
+ // in functions.
+ //
+
+ if (proxy -> handleAsyncSwitch(fd_) < 0)
+ {
+ return -1;
+ }
+
+ const unsigned char *buffer = lastMotion_;
+ unsigned char opcode = *lastMotion_;
+ unsigned int size = 32;
+
+ if (GetUINT(buffer + 2, bigEndian_) < serverSequence_)
+ {
+ PutUINT(serverSequence_, (unsigned char *) buffer + 2, bigEndian_);
+ }
+
+ encodeBuffer.encodeOpcodeValue(opcode, serverCache_ -> opcodeCache);
+
+ unsigned int sequenceNum = GetUINT(buffer + 2, bigEndian_);
+
+ unsigned int sequenceDiff = sequenceNum - serverSequence_;
+
+ serverSequence_ = sequenceNum;
+
+ #ifdef DEBUG
+ *logofs << "handleMotion: Last server sequence number for FD#"
+ << fd_ << " is " << serverSequence_ << " with "
+ << "difference " << sequenceDiff << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(sequenceDiff, 16,
+ serverCache_ -> eventSequenceCache, 7);
+
+ //
+ // If we fast encoded the message
+ // then skip the rest.
+ //
+
+ if (control -> LocalDeltaCompression == 0)
+ {
+ int result = handleFastReadEvent(encodeBuffer, opcode,
+ buffer, size);
+
+ #ifdef DEBUG
+ *logofs << "handleMotion: Sent saved motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ lastMotion_[0] = '\0';
+
+ #ifdef DEBUG
+ *logofs << "handleMotion: Reset last motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ if (result < 0)
+ {
+ return -1;
+ }
+ else if (result > 0)
+ {
+ return 1;
+ }
+ }
+
+ //
+ // This should be just the part specific
+ // for motion events but is currently a
+ // copy-paste of code from the read loop.
+ //
+
+ unsigned char detail = buffer[1];
+ if (*buffer == MotionNotify)
+ encodeBuffer.encodeBoolValue((unsigned int) detail);
+ else if ((*buffer == EnterNotify) || (*buffer == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) detail, 3);
+ else if (*buffer == KeyRelease)
+ {
+ if (detail == serverCache_ -> keyPressLastKey)
+ encodeBuffer.encodeBoolValue(1);
+ else
+ {
+ encodeBuffer.encodeBoolValue(0);
+ encodeBuffer.encodeValue((unsigned int) detail, 8);
+ }
+ }
+ else if ((*buffer == ButtonPress) || (*buffer == ButtonRelease))
+ encodeBuffer.encodeCachedValue(detail, 8,
+ serverCache_ -> buttonCache);
+ else
+ encodeBuffer.encodeValue((unsigned int) detail, 8);
+ unsigned int _timestamp = GetULONG(buffer + 4, bigEndian_);
+ unsigned int timestampDiff = _timestamp - serverCache_ -> lastTimestamp;
+ serverCache_ -> lastTimestamp = _timestamp;
+ encodeBuffer.encodeCachedValue(timestampDiff, 32,
+ serverCache_ -> motionNotifyTimestampCache, 9);
+ int skipRest = 0;
+ if (*buffer == KeyRelease)
+ {
+ skipRest = 1;
+ for (unsigned int i = 8; i < 31; i++)
+ {
+ if (buffer[i] != serverCache_ -> keyPressCache[i - 8])
+ {
+ skipRest = 0;
+ break;
+ }
+ }
+ encodeBuffer.encodeBoolValue(skipRest);
+ }
+ if (!skipRest)
+ {
+ const unsigned char *nextSrc = buffer + 8;
+ for (unsigned int i = 0; i < 3; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetULONG(nextSrc, bigEndian_), 29,
+ *serverCache_ -> motionNotifyWindowCache[i], 6);
+ nextSrc += 4;
+ }
+ unsigned int rootX = GetUINT(buffer + 20, bigEndian_);
+ unsigned int rootY = GetUINT(buffer + 22, bigEndian_);
+ unsigned int eventX = GetUINT(buffer + 24, bigEndian_);
+ unsigned int eventY = GetUINT(buffer + 26, bigEndian_);
+ eventX -= rootX;
+ eventY -= rootY;
+ encodeBuffer.encodeCachedValue(rootX -
+ serverCache_ -> motionNotifyLastRootX, 16,
+ serverCache_ -> motionNotifyRootXCache, 6);
+ serverCache_ -> motionNotifyLastRootX = rootX;
+ encodeBuffer.encodeCachedValue(rootY -
+ serverCache_ -> motionNotifyLastRootY, 16,
+ serverCache_ -> motionNotifyRootYCache, 6);
+ serverCache_ -> motionNotifyLastRootY = rootY;
+ encodeBuffer.encodeCachedValue(eventX, 16,
+ serverCache_ -> motionNotifyEventXCache, 6);
+ encodeBuffer.encodeCachedValue(eventY, 16,
+ serverCache_ -> motionNotifyEventYCache, 6);
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + 28, bigEndian_),
+ 16, serverCache_ -> motionNotifyStateCache);
+ if ((*buffer == EnterNotify) || (*buffer == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) buffer[30], 2);
+ else
+ encodeBuffer.encodeBoolValue((unsigned int) buffer[30]);
+ if ((*buffer == EnterNotify) || (*buffer == LeaveNotify))
+ encodeBuffer.encodeValue((unsigned int) buffer[31], 2);
+ else if (*buffer == KeyPress)
+ {
+ serverCache_ -> keyPressLastKey = detail;
+ for (unsigned int i = 8; i < 31; i++)
+ {
+ serverCache_ -> keyPressCache[i - 8] = buffer[i];
+ }
+ }
+ }
+
+ //
+ // Print info about achieved compression
+ // and update the statistics.
+ //
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleMotion: Handled event OPCODE#" << (unsigned int) buffer[0]
+ << " for FD#" << fd_ << " sequence " << sequenceNum << ". "
+ << size << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ statistics -> addEventBits(*buffer, size << 3, bits);
+
+ #ifdef DEBUG
+ *logofs << "handleMotion: Sent saved motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ lastMotion_[0] = '\0';
+
+ #ifdef DEBUG
+ *logofs << "handleMotion: Reset last motion event for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ServerChannel::handleConfiguration()
+{
+ #ifdef TEST
+ *logofs << "ServerChannel: Setting new buffer parameters "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ readBuffer_.setSize(control -> ServerInitialReadSize,
+ control -> ServerMaximumBufferSize);
+
+ writeBuffer_.setSize(control -> TransportXBufferSize,
+ control -> TransportXBufferThreshold,
+ control -> TransportMaximumBufferSize);
+
+ transport_ -> setSize(control -> TransportXBufferSize,
+ control -> TransportXBufferThreshold,
+ control -> TransportMaximumBufferSize);
+ return 1;
+}
+
+int ServerChannel::handleFinish()
+{
+ #ifdef TEST
+ *logofs << "ServerChannel: Finishing connection for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ congestion_ = 0;
+ priority_ = 0;
+
+ finish_ = 1;
+
+ //
+ // Reset the motion event.
+ //
+
+ lastMotion_[0] = '\0';
+
+ transport_ -> fullReset();
+
+ return 1;
+}
+
+int ServerChannel::handleAsyncEvents()
+{
+ //
+ // Encode more events while decoding the
+ // proxy messages.
+ //
+
+ if (transport_ -> readable() > 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleAsyncEvents: WARNING! Encoding events "
+ << "for FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ #if defined(TEST) || defined(INFO)
+
+ T_timestamp startTs = getTimestamp();
+
+ #endif
+
+ if (proxy -> handleAsyncRead(fd_) < 0)
+ {
+ return -1;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleAsyncEvents: Spent " << diffTimestamp(startTs,
+ getTimestamp()) << " Ms handling events for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int ServerChannel::handleUnpack(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ int resource = *(buffer + 1);
+
+ #ifdef DEBUG
+ *logofs << "handleUnpack: Unpacking image for resource " << resource
+ << " with method " << (unsigned int) *(buffer + 12)
+ << ".\n" << logofs_flush;
+ #endif
+
+ handleUnpackStateInit(resource);
+
+ T_geometry *geometryState = unpackState_[resource] -> geometry;
+ T_colormap *colormapState = unpackState_[resource] -> colormap;
+ T_alpha *alphaState = unpackState_[resource] -> alpha;
+
+ if (geometryState == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Missing geometry unpacking "
+ << "image for resource " << resource << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Missing geometry unpacking "
+ << "image for resource " << resource << ".\n";
+
+ return -1;
+ }
+
+ //
+ // Get the image data from the buffer.
+ //
+
+ imageState_ -> drawable = GetULONG(buffer + 4, bigEndian_);
+ imageState_ -> gcontext = GetULONG(buffer + 8, bigEndian_);
+
+ imageState_ -> method = *(buffer + 12);
+
+ imageState_ -> format = *(buffer + 13);
+ imageState_ -> srcDepth = *(buffer + 14);
+ imageState_ -> dstDepth = *(buffer + 15);
+
+ imageState_ -> srcLength = GetULONG(buffer + 16, bigEndian_);
+ imageState_ -> dstLength = GetULONG(buffer + 20, bigEndian_);
+
+ imageState_ -> srcX = GetUINT(buffer + 24, bigEndian_);
+ imageState_ -> srcY = GetUINT(buffer + 26, bigEndian_);
+ imageState_ -> srcWidth = GetUINT(buffer + 28, bigEndian_);
+ imageState_ -> srcHeight = GetUINT(buffer + 30, bigEndian_);
+
+ imageState_ -> dstX = GetUINT(buffer + 32, bigEndian_);
+ imageState_ -> dstY = GetUINT(buffer + 34, bigEndian_);
+ imageState_ -> dstWidth = GetUINT(buffer + 36, bigEndian_);
+ imageState_ -> dstHeight = GetUINT(buffer + 38, bigEndian_);
+
+ #ifdef TEST
+ *logofs << "handleUnpack: Source X is " << imageState_ -> srcX
+ << " Y is " << imageState_ -> srcY << " width is "
+ << imageState_ -> srcWidth << " height is "
+ << imageState_ -> srcHeight << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "handleUnpack: Destination X is " << imageState_ -> dstX
+ << " Y is " << imageState_ -> dstY << " width is "
+ << imageState_ -> dstWidth << " height is "
+ << imageState_ -> dstHeight << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (imageState_ -> srcX != 0 || imageState_ -> srcY != 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Unsupported source coordinates "
+ << "in unpack request.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ else if (imageState_ -> method == PACK_COLORMAP_256_COLORS &&
+ (colormapState == NULL || colormapState -> data == NULL))
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Cannot find any unpack colormap.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Field srcLength carries size of image data in
+ // packed format. Field dstLength is size of the
+ // image in the original X bitmap format.
+ //
+
+ unsigned int srcDataOffset = 40;
+
+ unsigned int srcSize = imageState_ -> srcLength;
+
+ unsigned int removeSize = size;
+
+ unsigned char *srcData = buffer + srcDataOffset;
+
+ //
+ // Get source and destination bits per pixel.
+ //
+
+ int srcBitsPerPixel = MethodBitsPerPixel(imageState_ -> method);
+
+ if (srcBitsPerPixel <= 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Can't identify source "
+ << "bits per pixel for method " << (unsigned int)
+ imageState_ -> method << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify source bits "
+ << "per pixel for method " << (unsigned int)
+ imageState_ -> method << ".\n";
+
+ writeBuffer_.removeMessage(removeSize);
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "handleUnpack: Source bits per pixel are "
+ << srcBitsPerPixel << " source data size is "
+ << srcSize << ".\n" << logofs_flush;
+ #endif
+
+ int dstBitsPerPixel = UnpackBitsPerPixel(geometryState, imageState_ -> dstDepth);
+
+ if (dstBitsPerPixel <= 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Can't identify "
+ << "destination bits per pixel for depth "
+ << (unsigned int) imageState_ -> dstDepth
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't identify "
+ << "destination bits per pixel for depth "
+ << (unsigned int) imageState_ -> dstDepth
+ << ".\n";
+
+ writeBuffer_.removeMessage(removeSize);
+
+ return -1;
+ }
+
+ //
+ // Destination is a PutImage request.
+ //
+
+ unsigned int dstDataOffset = 24;
+
+ //
+ // Output buffer size must match the number of input
+ // pixels multiplied by the number of bytes per pixel
+ // of current geometry.
+ //
+
+ size = (RoundUp4(imageState_ -> dstWidth * dstBitsPerPixel / 8) *
+ imageState_ -> dstHeight) + dstDataOffset;
+
+ #ifdef TEST
+ *logofs << "handleUnpack: Destination bits per pixel are "
+ << dstBitsPerPixel << " destination data size is "
+ << size - dstDataOffset << ".\n" << logofs_flush;
+ #endif
+
+ unsigned int dstSize = size - dstDataOffset;
+
+ imageState_ -> dstLines = imageState_ -> dstHeight;
+
+ unsigned char *dstData;
+
+ //
+ // Size of the final output buffer had to be stored
+ // in the offset field of XImage/NXPackedImage.
+ //
+
+ #ifdef WARNING
+
+ if (dstSize != imageState_ -> dstLength)
+ {
+ *logofs << "handleUnpack: WARNING! Destination size mismatch "
+ << "with reported " << imageState_ -> dstLength
+ << " and actual " << dstSize << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ //
+ // The decoding algorithm has put the packed image
+ // in the plain write buffer. Let's use the scratch
+ // buffer to uncompress the image.
+ //
+
+ buffer = writeBuffer_.addScratchMessage(size);
+
+ dstData = buffer + dstDataOffset;
+
+ //
+ // Unpack image into the buffer.
+ //
+
+ *buffer = (unsigned char) X_PutImage;
+
+ *(buffer + 1) = imageState_ -> format;
+
+ PutUINT(size >> 2, buffer + 2, bigEndian_);
+
+ PutULONG(imageState_ -> drawable, buffer + 4, bigEndian_);
+ PutULONG(imageState_ -> gcontext, buffer + 8, bigEndian_);
+
+ PutUINT(imageState_ -> dstWidth, buffer + 12, bigEndian_);
+ PutUINT(imageState_ -> dstLines, buffer + 14, bigEndian_);
+
+ PutUINT(imageState_ -> dstX, buffer + 16, bigEndian_);
+ PutUINT(imageState_ -> dstY, buffer + 18, bigEndian_);
+
+ *(buffer + 20) = 0;
+ *(buffer + 21) = imageState_ -> dstDepth;
+
+ #ifdef TEST
+ *logofs << "handleUnpack: Write buffer size is "
+ << writeBuffer_.getLength() << " scratch size is "
+ << writeBuffer_.getScratchLength() << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = 0;
+
+ switch (imageState_ -> method)
+ {
+ case PACK_JPEG_8_COLORS:
+ case PACK_JPEG_64_COLORS:
+ case PACK_JPEG_256_COLORS:
+ case PACK_JPEG_512_COLORS:
+ case PACK_JPEG_4K_COLORS:
+ case PACK_JPEG_32K_COLORS:
+ case PACK_JPEG_64K_COLORS:
+ case PACK_JPEG_256K_COLORS:
+ case PACK_JPEG_2M_COLORS:
+ case PACK_JPEG_16M_COLORS:
+ {
+ result = UnpackJpeg(geometryState, imageState_ -> method, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+ break;
+ }
+ case PACK_PNG_8_COLORS:
+ case PACK_PNG_64_COLORS:
+ case PACK_PNG_256_COLORS:
+ case PACK_PNG_512_COLORS:
+ case PACK_PNG_4K_COLORS:
+ case PACK_PNG_32K_COLORS:
+ case PACK_PNG_64K_COLORS:
+ case PACK_PNG_256K_COLORS:
+ case PACK_PNG_2M_COLORS:
+ case PACK_PNG_16M_COLORS:
+ {
+ result = UnpackPng(geometryState, imageState_ -> method, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+ break;
+ }
+ case PACK_RGB_16M_COLORS:
+ {
+ result = UnpackRgb(geometryState, imageState_ -> method, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+ break;
+ }
+ case PACK_RLE_16M_COLORS:
+ {
+ result = UnpackRle(geometryState, imageState_ -> method, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+ break;
+ }
+ case PACK_BITMAP_16M_COLORS:
+ {
+ result = UnpackBitmap(geometryState, imageState_ -> method, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+ break;
+ }
+ case PACK_COLORMAP_256_COLORS:
+ {
+ result = Unpack8(geometryState, colormapState, srcBitsPerPixel,
+ imageState_ -> srcWidth, imageState_ -> srcHeight, srcData,
+ srcSize, dstBitsPerPixel, imageState_ -> dstWidth,
+ imageState_ -> dstHeight, dstData, dstSize);
+
+ break;
+ }
+ default:
+ {
+ const T_colormask *colorMask = MethodColorMask(imageState_ -> method);
+
+ switch (imageState_ -> method)
+ {
+ case PACK_MASKED_8_COLORS:
+ case PACK_MASKED_64_COLORS:
+ case PACK_MASKED_256_COLORS:
+ {
+ result = Unpack8(geometryState, colorMask, imageState_ -> srcDepth,
+ imageState_ -> srcWidth, imageState_ -> srcHeight,
+ srcData, srcSize, imageState_ -> dstDepth,
+ imageState_ -> dstWidth, imageState_ -> dstHeight,
+ dstData, dstSize);
+ break;
+ }
+ case PACK_MASKED_512_COLORS:
+ case PACK_MASKED_4K_COLORS:
+ case PACK_MASKED_32K_COLORS:
+ case PACK_MASKED_64K_COLORS:
+ {
+ result = Unpack16(geometryState, colorMask, imageState_ -> srcDepth,
+ imageState_ -> srcWidth, imageState_ -> srcHeight,
+ srcData, srcSize, imageState_ -> dstDepth,
+ imageState_ -> dstWidth, imageState_ -> dstHeight,
+ dstData, dstSize);
+ break;
+ }
+ case PACK_MASKED_256K_COLORS:
+ case PACK_MASKED_2M_COLORS:
+ case PACK_MASKED_16M_COLORS:
+ {
+ result = Unpack24(geometryState, colorMask, imageState_ -> srcDepth,
+ imageState_ -> srcWidth, imageState_ -> srcHeight,
+ srcData, srcSize, imageState_ -> dstDepth,
+ imageState_ -> dstWidth, imageState_ -> dstHeight,
+ dstData, dstSize);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ writeBuffer_.removeMessage(removeSize);
+
+ if (result <= 0)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpack: PANIC! Failed to unpack image "
+ << "with method '" << (unsigned int) imageState_ -> method
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to unpack image "
+ << "with method '" << (unsigned int) imageState_ -> method
+ << "'.\n";
+
+ //
+ // TODO: We should mark the image somehow,
+ // and force the remote to remove it from
+ // the cache.
+ //
+
+ writeBuffer_.removeScratchMessage();
+
+ return -1;
+ }
+
+ //
+ // Alpha channel is used only on some 32 bits pixmaps
+ // and only if render extension is in use. Presently
+ // we don't have an efficient way to know in advance
+ // if mask must be applied or not to the image. If an
+ // alpha channel is set, the function will check if
+ // the size of the alpha data matches the size of the
+ // image. In the worst case we'll create an useless
+ // alpha plane for a pixmap that doesn't need it.
+ //
+
+ if (alphaState != NULL && alphaState -> data != NULL &&
+ imageState_ -> dstDepth == 32)
+ {
+ UnpackAlpha(alphaState, dstData, dstSize, imageByteOrder_);
+ }
+
+ return 1;
+}
+
+int ServerChannel::handleAuthorization(unsigned char *buffer)
+{
+ //
+ // At the present moment we don't support more than
+ // a single display for each proxy, so authorization
+ // data is shared among all the channels.
+ //
+ // Use the following code to simulate authentication
+ // failures on a LSB machine:
+ //
+ // memcpy(buffer + 12 + (((buffer[6] + 256 *
+ // buffer[7]) + 3) & ~3), "1234567890123456", 16);
+ //
+
+ if (auth == NULL)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleAuthorization: Forwarding the real cookie "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (auth -> checkCookie(buffer) == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleAuthorization: Matched the fake cookie "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+ else
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleAuthorization: WARNING! Failed to match "
+ << "the fake cookie for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+}
+
+int ServerChannel::handleAuthorization(const unsigned char *buffer, int size)
+{
+ //
+ // Check the X server's response and, in the case of
+ // an error, print the textual information reported
+ // by the server.
+ //
+
+ if (*buffer != 1)
+ {
+ const char *reason = NULL;
+
+ //
+ // At the moment we don't take into account the end-
+ // ianess of the reply. This should work in any case
+ // because we simply try to match a few well-known
+ // error strings.
+ //
+
+ if (size >= INVALID_COOKIE_SIZE + 8 &&
+ memcmp(buffer + 8, INVALID_COOKIE_DATA,
+ INVALID_COOKIE_SIZE) == 0)
+ {
+ reason = INVALID_COOKIE_DATA;
+ }
+ else if (size >= NO_AUTH_PROTO_SIZE + 8 &&
+ memcmp(buffer + 8, NO_AUTH_PROTO_DATA,
+ NO_AUTH_PROTO_SIZE) == 0)
+ {
+ reason = NO_AUTH_PROTO_DATA;
+ }
+ else
+ {
+ reason = "Unknown";
+ }
+
+ #ifdef WARNING
+ *logofs << "handleAuthorization: WARNING! X connection failed "
+ << "with error '" << reason << "' on FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": X connection failed "
+ << "with error '" << reason << "'.\n";
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "handleAuthorization: X connection successful "
+ << "on FD#" << fd_ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+//
+// Use a simple encoding. Need to handle the image
+// requests in the usual way and the X_ListExtensions
+// and X_QueryExtension to hide MIT-SHM and RENDER
+// in the reply.
+//
+
+int ServerChannel::handleFastWriteRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // All the NX requests are handled in the
+ // main message loop.
+ //
+
+ //
+ // Since ProtoStep7 (#issue 108)
+ //
+ // The X_PutImage can be handled here only
+ // if a split was not requested.
+ //
+
+ if ((opcode >= X_NXFirstOpcode && opcode <= X_NXLastOpcode) ||
+ (opcode == X_PutImage && splitState_.resource != nothing) ||
+ opcode == X_ListExtensions ||
+ opcode == X_QueryExtension)
+ {
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleFastWriteRequest: Decoding raw request OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ buffer = writeBuffer_.addMessage(4);
+
+ #ifndef __sun
+
+ unsigned int *next = (unsigned int *) decodeBuffer.decodeMemory(4);
+
+ *((unsigned int *) buffer) = *next;
+
+ #else /* #ifndef __sun */
+
+ memcpy(buffer, decodeBuffer.decodeMemory(4), 4);
+
+ #endif /* #ifndef __sun */
+
+ size = GetUINT(buffer + 2, bigEndian_) << 2;
+
+ if (size < 4)
+ {
+ #ifdef WARNING
+ *logofs << "handleFastWriteRequest: WARNING! Assuming size 4 "
+ << "for suspicious message of size " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ size = 4;
+ }
+
+ writeBuffer_.registerPointer(&buffer);
+
+ if (writeBuffer_.getAvailable() < size - 4 ||
+ (int) size >= control -> TransportFlushBufferSize)
+ {
+ #ifdef DEBUG
+ *logofs << "handleFastWriteRequest: Using scratch buffer for OPCODE#"
+ << (unsigned int) opcode << " with size " << size << " and "
+ << writeBuffer_.getLength() << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // The procedure moving data to shared memory
+ // assumes that the full message is stored in
+ // the scratch buffer. We can safely let the
+ // scratch buffer inherit the decode buffer
+ // at the next offset.
+ //
+
+ writeBuffer_.removeMessage(4);
+
+ buffer = writeBuffer_.addScratchMessage(((unsigned char *)
+ decodeBuffer.decodeMemory(size - 4)) - 4, size);
+ }
+ else
+ {
+ writeBuffer_.addMessage(size - 4);
+
+ #ifndef __sun
+
+ if (size <= 32)
+ {
+ next = (unsigned int *) decodeBuffer.decodeMemory(size - 4);
+
+ for (unsigned int i = 4; i < size; i += sizeof(unsigned int))
+ {
+ *((unsigned int *) (buffer + i)) = *next++;
+ }
+ }
+ else
+ {
+ memcpy(buffer + 4, decodeBuffer.decodeMemory(size - 4), size - 4);
+ }
+
+ #else /* #ifndef __sun */
+
+ memcpy(buffer + 4, decodeBuffer.decodeMemory(size - 4), size - 4);
+
+ #endif /* #ifndef __sun */
+ }
+
+ //
+ // Opcode could have been tainted by the client
+ // proxy. Replace the original opcode with the
+ // one sent in the decode buffer.
+ //
+
+ *buffer = opcode;
+
+ writeBuffer_.unregisterPointer();
+
+ if (opcode == X_PutImage)
+ {
+ handleImage(opcode, buffer, size);
+ }
+
+ #if defined(TEST) || defined(OPCODES)
+
+ if (opcode != 0)
+ {
+ *logofs << "handleFastWriteRequest: Handled request "
+ << "OPCODE#" << (unsigned int) opcode << " ("
+ << DumpOpcode(opcode) << ") for FD#" << fd_
+ << " sequence " << clientSequence_ << ". "
+ << size << " bytes out.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleFastWriteRequest: Handled image or "
+ << "other request for FD#" << fd_
+ << " sequence " << clientSequence_ << ". "
+ << size << " bytes out.\n" << logofs_flush;
+ }
+
+ #endif
+
+ handleFlush(flush_if_needed);
+
+ return 1;
+}
+
+//
+// Use the simplest encoding except for replies that
+// need to be managed some way.
+//
+
+int ServerChannel::handleFastReadReply(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size)
+{
+ //
+ // If we pushed a X_GetInputFocus in the sequence
+ // queue this means that the original message was
+ // a NX request for which we have to provide a NX
+ // reply.
+ //
+
+ if ((opcode >= X_NXFirstOpcode &&
+ opcode <= X_NXLastOpcode) ||
+ opcode == X_QueryExtension ||
+ opcode == X_ListExtensions ||
+ opcode == X_GetInputFocus)
+ {
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "handleFastReadReply: Encoding raw reply OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << " with size " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeMemory(buffer, size);
+
+ //
+ // Send back the reply as soon
+ // as possible.
+ //
+
+ priority_++;
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+ *logofs << "handleFastReadReply: Handled raw reply OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_ << " sequence "
+ << serverSequence_ << ". " << size << " bytes in, "
+ << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ #endif
+
+ statistics -> addReplyBits(opcode, size << 3, bits);
+
+ return 1;
+}
+
+int ServerChannel::handleFastReadEvent(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size)
+{
+ #ifdef DEBUG
+ *logofs << "handleFastReadEvent: Encoding raw "
+ << (opcode == X_Error ? "error" : "event") << " OPCODE#"
+ << (unsigned int) opcode << " for FD#" << fd_
+ << " with size " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeMemory(buffer, size);
+
+ switch (opcode)
+ {
+ case X_Error:
+ case ButtonPress:
+ case ButtonRelease:
+ case KeyPress:
+ case KeyRelease:
+ {
+ priority_++;
+ }
+ }
+
+ int bits = encodeBuffer.diffBits();
+
+ #if defined(TEST) || defined(OPCODES)
+
+ if (opcode == X_Error)
+ {
+ unsigned char code = *(buffer + 1);
+
+ *logofs << "handleFastReadEvent: Handled error ERR_CODE#"
+ << (unsigned int) code << " for FD#" << fd_;
+
+ *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_);
+
+ *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_);
+
+ *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10);
+
+ *logofs << " sequence " << serverSequence_ << ". " << size
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "handleFastReadEvent: Handled event OPCODE#"
+ << (unsigned int) *buffer << " for FD#" << fd_
+ << " sequence " << serverSequence_ << ". " << size
+ << " bytes in, " << bits << " bits (" << ((float) bits) / 8
+ << " bytes) out.\n" << logofs_flush;
+ }
+
+ #endif
+
+ statistics -> addEventBits(opcode, size << 3, bits);
+
+ return 1;
+}
+
+void ServerChannel::initCommitQueue()
+{
+ #ifdef TEST
+ *logofs << "initCommitQueue: Resetting the queue of split commits "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE; i++)
+ {
+ commitSequenceQueue_[i] = 0;
+ }
+}
+
+void ServerChannel::updateCommitQueue(unsigned short sequence)
+{
+ for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE - 1; i++)
+ {
+ commitSequenceQueue_[i + 1] = commitSequenceQueue_[i];
+ }
+
+ #ifdef TEST
+ *logofs << "updateCommitQueue: Saved " << sequence
+ << " as last sequence number of image to commit.\n"
+ << logofs_flush;
+ #endif
+
+ commitSequenceQueue_[0] = sequence;
+}
+
+int ServerChannel::checkCommitError(unsigned char error, unsigned short sequence,
+ const unsigned char *buffer)
+{
+ //
+ // Check if error is due to an image commit
+ // generated at the end of a split.
+ //
+ // TODO: It should zero the head of the list
+ // when an event comes with a sequence number
+ // greater than the value of the last element
+ // added.
+ //
+
+ for (int i = 0; i < MAX_COMMIT_SEQUENCE_QUEUE &&
+ commitSequenceQueue_[i] != 0; i++)
+ {
+ #ifdef TEST
+ *logofs << "checkCommitError: Checking committed image's "
+ << "sequence number " << commitSequenceQueue_[i]
+ << " with input sequence " << sequence << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (commitSequenceQueue_[i] == sequence)
+ {
+ #ifdef WARNING
+
+ *logofs << "checkCommitError: WARNING! Failed operation for "
+ << "FD#" << fd_ << " with ERR_CODE#"
+ << (unsigned int) *(buffer + 1);
+
+ *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_);
+
+ *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_);
+
+ *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10);
+
+ *logofs << " sequence " << sequence << ".\n";
+
+ *logofs << logofs_flush;
+
+ #endif
+
+ cerr << "Warning" << ": Failed commit operation "
+ << "with ERR_CODE#" << (unsigned int) error;
+
+ cerr << " RES_ID#" << GetULONG(buffer + 4, bigEndian_);
+
+ cerr << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_);
+
+ cerr << " MAJ_OP#" << (unsigned int) *(buffer + 10);
+
+ cerr << ".\n";
+
+ #ifdef WARNING
+ *logofs << "checkCommitError: WARNING! Suppressing error on "
+ << "OPCODE#" << (unsigned int) opcodeStore_ -> commitSplit
+ << " for FD#" << fd_ << " with sequence " << sequence
+ << " at position " << i << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+//
+// Check if the user pressed the CTRL+ALT+SHIFT+ESC
+// keystroke. At the present moment it uses different
+// keycodes based on the client OS. This should be
+// implemented in a way that is platform independent
+// (that's not an easy task, considered that we don't
+// have access to the higher level X libraries).
+//
+
+int ServerChannel::checkKeyboardEvent(unsigned char event, unsigned short sequence,
+ const unsigned char *buffer)
+{
+ #ifdef TEST
+ *logofs << "checkKeyboardEvent: Checking escape sequence with byte [1] "
+ << (void *) ((unsigned) *(buffer + 1)) << " and bytes [28-29] "
+ << (void *) ((unsigned) GetUINT(buffer + 28, bigEndian_))
+ << " for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef __APPLE__
+
+ int alert = (*(buffer + 1) == 0x3d &&
+ GetUINT(buffer + 28, bigEndian_) == 0x2005);
+
+ #else
+
+ int alert = (*(buffer + 1) == 0x09 &&
+ ((GetUINT(buffer + 28, bigEndian_) &
+ 0x0d) == 0x0d));
+
+ #endif
+
+ if (alert == 1)
+ {
+ #ifdef PANIC
+ *logofs << "checkKeyboardEvent: PANIC! Received sequence "
+ << "CTRL+ALT+SHIFT+ESC " << "for FD#"<< fd_
+ << ". Showing the abort dialog.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Received sequence CTRL+ALT+SHIFT+ESC. "
+ << "Showing the abort dialog.\n";
+
+ HandleAlert(CLOSE_UNRESPONSIVE_X_SERVER_ALERT, 1);
+ }
+
+ return alert;
+}
+
+//
+// Handle the MIT-SHM initialization
+// messages exchanged with the remote
+// proxy.
+//
+
+int ServerChannel::handleShmemReply(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned int stage, const unsigned char *buffer,
+ const unsigned int size)
+{
+ #ifdef TEST
+ *logofs << "handleShmemReply: Returning shmem reply for "
+ << "stage " << stage << ".\n" << logofs_flush;
+ #endif
+
+ if (opcode == X_QueryExtension)
+ {
+ encodeBuffer.encodeValue(stage, 2);
+
+#ifndef ANDROID
+ shmemState_ -> present = *(buffer + 8);
+#else
+ shmemState_ -> present = 0;
+ cerr << "Info: handleShmemReply: In android no shared memory. Setting present to 0 hardcoded\n";
+#endif
+ shmemState_ -> opcode = *(buffer + 9);
+ shmemState_ -> event = *(buffer + 10);
+ shmemState_ -> error = *(buffer + 11);
+
+ #ifdef TEST
+ *logofs << "handleShmemReply: Extension present is "
+ << shmemState_ -> present << " with base OPCODE#"
+ << (unsigned int) shmemState_ -> opcode << " base event "
+ << (unsigned int) shmemState_ -> event << " base error "
+ << (unsigned int) shmemState_ -> error << ".\n"
+ << logofs_flush;
+ #endif
+ }
+ else if (opcode == X_GetInputFocus)
+ {
+ encodeBuffer.encodeValue(stage, 2);
+
+ encodeBuffer.encodeBoolValue(0);
+
+ if (shmemState_ -> present == 1 &&
+ shmemState_ -> address != NULL &&
+ shmemState_ -> segment > 0 &&
+ shmemState_ -> id > 0)
+ {
+ cerr << "Info" << ": Using shared memory parameters 1/"
+ << (shmemState_ -> size / 1024) << "K.\n";
+
+#ifndef ANDROID
+ shmemState_ -> enabled = 1;
+#else
+ cerr << "Info: handleShmemReply: In android no shared memory. Setting enabled to -1. This should not be displayed\n";
+ shmemState_ -> enabled = -1;
+#endif
+
+ encodeBuffer.encodeBoolValue(1);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "handleShmemReply: WARNING! Not using shared memory "
+ << "support in X server for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Info" << ": Using shared memory parameters 0/0K.\n";
+
+ handleShmemStateRemove();
+
+ encodeBuffer.encodeBoolValue(0);
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "handleShmemReply: PANIC! Conversation error "
+ << "handling shared memory support for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Conversation error handling "
+ << "shared memory support.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ServerChannel::handleShmemRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // We need to query and initialize MIT-SHM on
+ // the real X server. To do this we'll need 3
+ // requests. At the end we'll have to encode
+ // the final reply for the X client side.
+ //
+
+ handleShmemStateAlloc();
+
+ unsigned int stage;
+
+ decodeBuffer.decodeValue(stage, 2);
+
+ unsigned int expected = shmemState_ -> stage + 1;
+
+ if (stage != expected || stage > 2)
+ {
+ #ifdef PANIC
+ *logofs << "handleShmemRequest: PANIC! Unexpected stage "
+ << stage << " in handling shared memory "
+ << "support for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unexpected stage "
+ << stage << " in handling shared memory "
+ << "support for FD#" << fd_ << ".\n";
+
+ return -1;
+ }
+
+ switch (stage)
+ {
+ case 0:
+ {
+ unsigned int enableClient;
+ unsigned int enableServer;
+
+ decodeBuffer.decodeBoolValue(enableClient);
+ decodeBuffer.decodeBoolValue(enableServer);
+
+ unsigned int clientSegment;
+ unsigned int serverSegment;
+
+ decodeBuffer.decodeValue(clientSegment, 29, 9);
+ decodeBuffer.decodeValue(serverSegment, 29, 9);
+
+ shmemState_ -> segment = serverSegment;
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Size of the shared memory "
+ << "segment will be " << control -> ShmemServerSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Sending X_QueryExtension request "
+ << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int)
+ opcodeStore_ -> getShmemParameters << " in stage "
+ << stage << ".\n" << logofs_flush;
+ #endif
+
+ opcode = X_QueryExtension;
+
+ size = 16;
+ buffer = writeBuffer_.addMessage(size);
+
+ PutUINT(7, buffer + 4, bigEndian_);
+
+ //
+ // Simply make the query fail if shared
+ // memory support is disabled by the
+ // user.
+ //
+#ifndef ANDROID
+ if (control -> ShmemServer == 1 &&
+ control -> ShmemServerSize > 0 &&
+ enableServer == 1)
+ {
+ memcpy(buffer + 8, "MIT-SHM", 7);
+ }
+ else
+ {
+ memcpy(buffer + 8, "NO-MIT-", 7);
+ }
+#else
+ cerr << "Info: handleShmemRequest: In android no shared memory. Returning NO-MIT- answer\n";
+
+ memcpy(buffer + 8, "NO-MIT-", 7);
+#endif
+ sequenceQueue_.push(clientSequence_, opcode,
+ opcodeStore_ -> getShmemParameters, stage);
+
+ //
+ // Save the sequence number so we can
+ // later identify any matching X error
+ // received from server.
+ //
+
+ shmemState_ -> sequence = clientSequence_;
+
+ break;
+ }
+ case 1:
+ {
+ if (shmemState_ -> present == 1)
+ {
+ //
+ // Make the segment read-write for everybody on
+ // Cygwin (to avoid any lack of support or any
+ // performance issue) and on MacOS/X (where the
+ // 0600 mask doesn't seem to work).
+ //
+
+ #if defined(__CYGWIN32__) || defined(__APPLE__)
+
+ int permissions = 0777;
+
+ #else
+
+ int permissions = 0600;
+
+ #endif
+
+ shmemState_ -> size = control -> ShmemServerSize;
+
+#ifndef ANDROID
+ shmemState_ -> id = shmget(IPC_PRIVATE, shmemState_ -> size,
+ IPC_CREAT | permissions);
+#else
+ cerr << "Info: handleShmemReqyest: In android no shared memory (shmget). This message should not be displayed present should never be 1 in android\n";
+ shmemState_ -> id = -1;
+#endif
+ if (shmemState_ -> id >= 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmemRequest: Allocated shared memory "
+ << "segment of " << shmemState_ -> size
+ << " bytes with id " << shmemState_ -> id
+ << ".\n" << logofs_flush;
+ #endif
+
+
+#ifndef ANDROID
+ shmemState_ -> address = shmat(shmemState_ -> id, 0, 0);
+#else
+ cerr << "Info: handleShmemReqyest: In android no shared memory (shmat). This message should not be displayed. present should never be 1 in android\n";
+ shmemState_ -> address = NULL;
+#endif
+ if (shmemState_ -> address != NULL)
+ {
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Sending X_ShmAttach request "
+ << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int)
+ opcodeStore_ -> getShmemParameters << " in stage "
+ << stage << ".\n" << logofs_flush;
+ #endif
+
+ opcode = shmemState_ -> opcode;
+
+ size = 16;
+ buffer = writeBuffer_.addMessage(size);
+
+ *(buffer + 1) = X_ShmAttach;
+
+ PutULONG(shmemState_ -> segment, buffer + 4, bigEndian_);
+ PutULONG(shmemState_ -> id, buffer + 8, bigEndian_);
+
+ *(buffer + 12) = 1;
+
+ shmemState_ -> sequence = clientSequence_;
+
+ break;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "handleShmemRequest: WARNING! Can't attach the shared "
+ << "memory segment. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't attach the shared memory "
+ << "segment. Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+ }
+ }
+ else
+ {
+ #ifndef __CYGWIN32__
+
+ #ifdef WARNING
+ *logofs << "handleShmemRequest: WARNING! Can't create the shared "
+ << "memory segment. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't create the shared memory "
+ << "segment. Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ #else
+
+ #ifdef TEST
+ *logofs << "handleShmemRequest: WARNING! Can't create the shared "
+ << "memory segment. Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ #endif
+ }
+ }
+
+ if (shmemState_ -> present != 0)
+ {
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Resetting shared memory "
+ << "presence flag for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ shmemState_ -> present = 0;
+ }
+
+ handleNullRequest(opcode, buffer, size);
+
+ break;
+ }
+ default:
+ {
+ #ifdef TEST
+ *logofs << "handleShmemRequest: Sending X_GetInputFocus request "
+ << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int)
+ opcodeStore_ -> getShmemParameters << " in stage "
+ << stage << ".\n" << logofs_flush;
+ #endif
+
+ opcode = X_GetInputFocus;
+
+ size = 4;
+ buffer = writeBuffer_.addMessage(size);
+
+ sequenceQueue_.push(clientSequence_, opcode,
+ opcodeStore_ -> getShmemParameters, stage);
+ break;
+ }
+ }
+
+ shmemState_ -> stage += 1;
+
+ return 1;
+}
+
+//
+// Handling of MIT-SHM extension has been plugged late in
+// the design, so we have to make some assumptions. Image
+// is a X_PutImage request contained either in the scratch
+// buffer or in the normal write buffer. We need to move
+// the image data to the shared memory segment and replace
+// the X_PutImage request with a X_ShmPutImage.
+//
+
+int ServerChannel::handleShmem(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size)
+{
+ if (shmemState_ == NULL || shmemState_ -> enabled != 1)
+ {
+ #ifdef TEST
+
+ if (shmemState_ != NULL)
+ {
+ *logofs << "handleShmem: PANIC! Shared memory "
+ << "state found but support is not enabled "
+ << "for FD#" << fd_ << " in stage "
+ << shmemState_ -> stage << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ return 0;
+ }
+#ifdef ANDROID
+ cerr << "Info: handleShmem: In android no shared memory. enabled should never be 1. This should not be displayed\n";
+ return 0;
+#endif
+
+ //
+ // Ignore null requests and requests that will not result
+ // in a single X_PutImage. To conform with the other func-
+ // tions, we get the opcode passed as a parameter. It can
+ // be zero if we don't want the write loop to put opcode
+ // and length in the resulting buffer. Anyway we are only
+ // interested in the original opcode of the request, that
+ // is stored in the image state.
+ //
+
+ unsigned char *dstData = buffer + 24;
+ unsigned int dstDataSize = size - 24;
+
+ if (dstDataSize == 0 || dstDataSize >
+ (unsigned int) control -> MaximumRequestSize)
+ {
+ #ifdef TEST
+ *logofs << "handleShmem: Ignoring image with opcode "
+ << (unsigned int) imageState_ -> opcode
+ << " and size " << size << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #ifdef TEST
+ *logofs << "handleShmem: Handling image with opcode "
+ << (unsigned int) imageState_ -> opcode
+ << " and size " << size << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Get image data from buffer.
+ //
+
+ if (imageState_ -> opcode == X_PutImage)
+ {
+ //
+ // We still need to get the image's data.
+ //
+
+ imageState_ -> format = *(buffer + 1);
+
+ imageState_ -> drawable = GetULONG(buffer + 4, bigEndian_);
+ imageState_ -> gcontext = GetULONG(buffer + 8, bigEndian_);
+
+ imageState_ -> dstWidth = GetUINT(buffer + 12, bigEndian_);
+ imageState_ -> dstHeight = GetUINT(buffer + 14, bigEndian_);
+
+ imageState_ -> srcX = 0;
+ imageState_ -> srcY = 0;
+
+ imageState_ -> srcWidth = imageState_ -> dstWidth;
+ imageState_ -> srcHeight = imageState_ -> dstHeight;
+
+ imageState_ -> dstX = GetUINT(buffer + 16, bigEndian_);
+ imageState_ -> dstY = GetUINT(buffer + 18, bigEndian_);
+
+ imageState_ -> leftPad = *(buffer + 20);
+ imageState_ -> dstDepth = *(buffer + 21);
+
+ imageState_ -> dstLines = imageState_ -> dstHeight;
+
+ imageState_ -> dstLength = size - 24;
+ }
+
+ //
+ // Skip the MIT-SHM operation if the image
+ // is 1 bits-per-plane.
+ //
+
+ if (imageState_ -> dstDepth == 1)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmem: Ignoring image with opcode "
+ << (unsigned int) imageState_ -> opcode << " depth "
+ << (unsigned int) imageState_ -> dstDepth << " and "
+ << "size " << size << " for FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // If the image can't fit in the available
+ // space, check if the completion event is
+ // arrived.
+ //
+
+ #if defined(TEST) || defined(INFO)
+
+ if (isTimestamp(shmemState_ -> last) == 0 &&
+ shmemState_ -> offset != 0)
+ {
+ *logofs << "handleShmem: PANIC! No timestamp for sequence "
+ << shmemState_ -> sequence << " with offset "
+ << shmemState_ -> offset << ".\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ if (shmemState_ -> offset + imageState_ -> dstLength >
+ shmemState_ -> size)
+ {
+ if (imageState_ -> dstLength > shmemState_ -> size)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmem: WARNING! Can't fit the image "
+ << "in the available memory segment for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (handleShmemEvent() <= 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmem: WARNING! Missing completion "
+ << "after " << diffTimestamp(shmemState_ -> last,
+ getTimestamp()) << " Ms for shared memory "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+
+ //
+ // Let image start at current offset
+ // in the shared segment.
+ //
+
+ #ifdef TEST
+ *logofs << "handleShmem: Copying " << dstDataSize
+ << " bytes to shared memory at offset "
+ << shmemState_ -> offset << " for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) shmemState_ -> address +
+ shmemState_ -> offset, dstData, dstDataSize);
+
+ //
+ // Get rid of the original X_PutImage
+ // request.
+ //
+
+ if (writeBuffer_.getScratchData() != NULL)
+ {
+ writeBuffer_.removeScratchMessage();
+ }
+ else
+ {
+ writeBuffer_.removeMessage(size);
+ }
+
+ //
+ // Add a X_ShmPutImage request to the
+ // write buffer.
+ //
+
+ buffer = writeBuffer_.addMessage(40);
+
+ *buffer = shmemState_ -> opcode;
+
+ *(buffer + 1) = X_ShmPutImage;
+
+ PutUINT(40 >> 2, buffer + 2, bigEndian_);
+
+ PutULONG(imageState_ -> drawable, buffer + 4, bigEndian_);
+ PutULONG(imageState_ -> gcontext, buffer + 8, bigEndian_);
+
+ PutUINT(imageState_ -> dstWidth, buffer + 12, bigEndian_);
+ PutUINT(imageState_ -> dstLines, buffer + 14, bigEndian_);
+
+ PutUINT(imageState_ -> srcX, buffer + 16, bigEndian_);
+ PutUINT(imageState_ -> srcY, buffer + 18, bigEndian_);
+
+ PutUINT(imageState_ -> dstWidth, buffer + 20, bigEndian_);
+ PutUINT(imageState_ -> dstLines, buffer + 22, bigEndian_);
+
+ PutUINT(imageState_ -> dstX, buffer + 24, bigEndian_);
+ PutUINT(imageState_ -> dstY, buffer + 26, bigEndian_);
+
+ *(buffer + 28) = imageState_ -> dstDepth;
+ *(buffer + 29) = imageState_ -> format;
+ *(buffer + 30) = 1;
+
+ PutULONG(shmemState_ -> segment, buffer + 32, bigEndian_);
+ PutULONG(shmemState_ -> offset, buffer + 36, bigEndian_);
+
+ shmemState_ -> offset += dstDataSize;
+
+ shmemState_ -> sequence = clientSequence_;
+ shmemState_ -> last = getTimestamp();
+
+ #ifdef TEST
+ *logofs << "handleShmem: Saved shared memory sequence "
+ << shmemState_ -> sequence << " for FD#" << fd_
+ << " with offset " << shmemState_ -> offset
+ << " at " << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Make the X server read immediately
+ // from the shared memory buffer and
+ // produce the completion event.
+ //
+
+ handleFlush(flush_if_any);
+
+ return 1;
+}
+
+//
+// Try to read more events from the socket in the
+// attempt to get the completion event required
+// to reset the MIT-SHM segment.
+//
+
+int ServerChannel::handleShmemEvent()
+{
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmemEvent: Waiting for shared memory "
+ << "sequence " << shmemState_ -> sequence
+ << " for X server FD#" << fd_ << ".\n"
+ << logofs_flush;
+
+ T_timestamp startTs = getTimestamp();
+
+ #endif
+
+ while (isTimestamp(shmemState_ -> last) != 0)
+ {
+ if (handleWait(control -> ShmemTimeout) <= 0)
+ {
+ break;
+ }
+ #if defined(TEST) || defined(INFO)
+ else
+ {
+ *logofs << "handleShmemEvent: WARNING! Encoded events "
+ << "for FD#" << fd_ << " at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ }
+ #endif
+ }
+
+ if (isTimestamp(shmemState_ -> last) == 0)
+ {
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmemEvent: Spent "
+ << diffTimestamp(startTs, getTimestamp()) << " Ms "
+ << "waiting for shared memory sequence for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ #if defined(TEST) || defined(INFO)
+ *logofs << "handleShmemEvent: WARNING! Can't reset shared "
+ << "memory sequence for FD#" << fd_ << " after "
+ << diffTimestamp(shmemState_ -> last, getTimestamp())
+ << " Ms.\n" << logofs_flush;
+ #endif
+
+ return 0;
+}
+
+int ServerChannel::checkShmemEvent(unsigned char event, unsigned short sequence,
+ const unsigned char *buffer)
+{
+ if (isTimestamp(shmemState_ -> last) == 1 &&
+ sequence == shmemState_ -> sequence)
+ {
+ #ifdef TEST
+ *logofs << "checkShmemEvent: Reset shared memory sequence "
+ << shmemState_ -> sequence << " for FD#" << fd_
+ << " after " << diffTimestamp(shmemState_ -> last,
+ getTimestamp()) << " Ms.\n" << logofs_flush;
+ #endif
+
+ shmemState_ -> sequence = 0;
+ shmemState_ -> offset = 0;
+ shmemState_ -> last = nullTimestamp();
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "checkShmemEvent: Skipping past shared memory "
+ << "image sequence " << sequence << " for FD#"
+ << fd_ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int ServerChannel::checkShmemError(unsigned char error, unsigned short sequence,
+ const unsigned char *buffer)
+{
+ #ifdef TEST
+
+ *logofs << "checkShmemError: WARNING! Failed operation for "
+ << "FD#" << fd_ << " in stage " << shmemState_ -> stage
+ << " with ERR_CODE#" << (unsigned int) *(buffer + 1);
+
+ *logofs << " RES_ID#" << GetULONG(buffer + 4, bigEndian_);
+
+ *logofs << " MIN_OP#" << GetUINT(buffer + 8, bigEndian_);
+
+ *logofs << " MAJ_OP#" << (unsigned int) *(buffer + 10);
+
+ *logofs << " sequence " << sequence << ".\n";
+
+ *logofs << logofs_flush;
+
+ #endif
+
+ //
+ // If enabled flag is <= 0 we are still
+ // in the inizialization phase. In this
+ // case force presence to false.
+ //
+
+ if (shmemState_ -> enabled != 1)
+ {
+ if (shmemState_ -> present != 0)
+ {
+ #ifdef TEST
+ *logofs << "checkShmemError: Resetting shared memory "
+ << "presence flag for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ shmemState_ -> present = 0;
+ }
+
+ return 0;
+ }
+
+ if (shmemState_ -> sequence == sequence)
+ {
+ //
+ // Reset the sequence and timestamp.
+ //
+
+ shmemState_ -> sequence = 0;
+ shmemState_ -> offset = 0;
+ shmemState_ -> last = nullTimestamp();
+ }
+
+ return 1;
+}
+
+int ServerChannel::handleFontRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // Send a synchronization request and use
+ // the reply to return the requested font
+ // path.
+ //
+
+ #ifdef TEST
+ *logofs << "handleFontRequest: Sending X_GetInputFocus request "
+ << "for FD#" << fd_ << " due to OPCODE#" << (unsigned int)
+ opcodeStore_ -> getFontParameters << ".\n"
+ << logofs_flush;
+ #endif
+
+ opcode = X_GetInputFocus;
+
+ size = 4;
+ buffer = writeBuffer_.addMessage(size);
+
+ sequenceQueue_.push(clientSequence_, X_GetInputFocus,
+ opcodeStore_ -> getFontParameters);
+
+ return 1;
+}
+
+int ServerChannel::handleFontReply(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+{
+ #ifdef TEST
+ *logofs << "handleFontReply: Encoding font operation "
+ << "reply with size " << size << ".\n"
+ << logofs_flush;
+ #endif
+
+ char data[256];
+
+ if (fontPort_ != -1)
+ {
+ sprintf(data + 1, "tcp/localhost:%d", fontPort_);
+ }
+ else
+ {
+ *(data + 1) = '\0';
+ }
+
+ *data = strlen(data + 1);
+
+ unsigned char *next = (unsigned char *) data;
+
+ unsigned int length = (unsigned int) (*next++);
+
+ encodeBuffer.encodeValue(length, 8);
+
+ encodeBuffer.encodeTextData(next, length);
+
+ return 1;
+}
+
+int ServerChannel::handleCacheRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ unsigned int mask;
+
+ decodeBuffer.decodeCachedValue(mask, 32, clientCache_ ->
+ setCacheParametersCache);
+
+ splitState_.save = (mask >> 8) & 0xff;
+ splitState_.load = mask & 0xff;
+
+ #ifdef TEST
+ *logofs << "handleCacheRequest: Set cache parameters to "
+ << "save " << splitState_.save << " load "
+ << splitState_.load << ".\n" << logofs_flush;
+ #endif
+
+ handleNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleStartSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // Prepare for the split for the selected
+ // resource. Old proxy versions only used
+ // the split store at position 0.
+ //
+
+ // Since ProtoStep7 (#issue 108)
+ unsigned char resource;
+
+ decodeBuffer.decodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ splitState_.resource = resource;
+
+ splitState_.current = splitState_.resource;
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleStartSplitRequest: SPLIT! Registered id "
+ << splitState_.resource << " as resource "
+ << "waiting for a split.\n" << logofs_flush;
+ #endif
+
+ handleNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleEndSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size)
+{
+ //
+ // Verify that the agent resource matches.
+ //
+
+ // Since ProtoStep7 (#issue 108)
+ unsigned char resource;
+
+ decodeBuffer.decodeCachedValue(resource, 8,
+ clientCache_ -> resourceCache);
+
+ #ifdef TEST
+
+ if (splitState_.resource == nothing)
+ {
+ #ifdef PANIC
+ *logofs << "handleEndSplitRequest: PANIC! SPLIT! Received an end of "
+ << "split for resource id " << (unsigned int) *(buffer + 1)
+ << " without a previous start.\n"
+ << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+ else if (resource != splitState_.resource)
+ {
+ #ifdef PANIC
+ *logofs << "handleEndSplitRequest: PANIC! SPLIT! Invalid resource id "
+ << resource << " received while waiting for resource id "
+ << splitState_.resource << ".\n" << logofs_flush;
+ #endif
+
+ HandleCleanup();
+ }
+
+ #endif
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleEndSplitRequest: SPLIT! Reset id "
+ << splitState_.resource << " as resource "
+ << "selected for splits.\n" << logofs_flush;
+ #endif
+
+ splitState_.resource = nothing;
+
+ handleNullRequest(opcode, buffer, size);
+
+ return 1;
+}
+
+int ServerChannel::handleSplitChecksum(DecodeBuffer &decodeBuffer, T_checksum &checksum)
+{
+ unsigned int receive;
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeBoolValue(receive);
+
+ if (receive == 1)
+ {
+ checksum = new md5_byte_t[MD5_LENGTH];
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ decodeBuffer.decodeValue(receive, 8);
+
+ if (checksum != NULL)
+ {
+ checksum[i] = (unsigned char) receive;
+ }
+ }
+
+ #if defined(TEST) || defined(SPLIT)
+ *logofs << "handleSplitChecksum: SPLIT! Received checksum "
+ << "[" << DumpChecksum(checksum) << "].\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ return 0;
+}
+
+void ServerChannel::handleShmemStateAlloc()
+{
+ if (shmemState_ == NULL)
+ {
+ shmemState_ = new T_shmem_state();
+
+ shmemState_ -> stage = -1;
+ shmemState_ -> present = -1;
+ shmemState_ -> enabled = -1;
+
+ shmemState_ -> segment = -1;
+ shmemState_ -> id = -1;
+ shmemState_ -> address = NULL;
+ shmemState_ -> size = 0;
+
+ shmemState_ -> opcode = 0xff;
+ shmemState_ -> event = 0xff;
+ shmemState_ -> error = 0xff;
+
+ shmemState_ -> sequence = 0;
+ shmemState_ -> offset = 0;
+ shmemState_ -> last = nullTimestamp();
+
+ shmemState_ -> checked = 0;
+ }
+}
+
+void ServerChannel::handleShmemStateRemove()
+{
+ if (shmemState_ != NULL)
+ {
+ if (shmemState_ -> address != NULL)
+ {
+#ifndef ANDROID
+ shmdt((char *) shmemState_ -> address);
+#else
+ cerr << "Info: handleShmemStateRemove: In android no shared memory. This should not be displayed. address should always be NULL\n";
+#endif
+ }
+
+ if (shmemState_ -> id > 0)
+ {
+#ifndef ANDROID
+ shmctl(shmemState_ -> id, IPC_RMID, 0);
+#else
+ cerr << "Info: handleShmemStateRemove: In android no shared memory. This should not be displayed. id should always be 0\n";
+#endif
+ }
+
+ delete shmemState_;
+
+ shmemState_ = NULL;
+ }
+}
+
+void ServerChannel::handleUnpackStateInit(int resource)
+{
+ if (unpackState_[resource] == NULL)
+ {
+ unpackState_[resource] = new T_unpack_state();
+
+ if (unpackState_[resource] == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpackStateInit: PANIC! Can't allocate "
+ << "memory for unpack state in context [A].\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "unpack state in context [A].\n";
+
+ HandleAbort();
+ }
+
+ unpackState_[resource] -> geometry = NULL;
+ unpackState_[resource] -> colormap = NULL;
+ unpackState_[resource] -> alpha = NULL;
+ }
+}
+
+void ServerChannel::handleUnpackAllocGeometry(int resource)
+{
+ if (unpackState_[resource] -> geometry == NULL)
+ {
+ unpackState_[resource] -> geometry = new T_geometry();
+
+ if (unpackState_[resource] -> geometry == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpackAllocGeometry: PANIC! Can't allocate "
+ << "memory for unpack state in context [B].\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "unpack state in context [B].\n";
+
+ HandleAbort();
+ }
+
+ unpackState_[resource] -> geometry -> depth1_bpp = 4;
+ unpackState_[resource] -> geometry -> depth4_bpp = 4;
+ unpackState_[resource] -> geometry -> depth8_bpp = 8;
+ unpackState_[resource] -> geometry -> depth16_bpp = 16;
+ unpackState_[resource] -> geometry -> depth24_bpp = 32;
+ unpackState_[resource] -> geometry -> depth32_bpp = 32;
+
+ unpackState_[resource] -> geometry -> red_mask = 0xff0000;
+ unpackState_[resource] -> geometry -> green_mask = 0x00ff00;
+ unpackState_[resource] -> geometry -> blue_mask = 0x0000ff;
+
+ unpackState_[resource] -> geometry -> image_byte_order = imageByteOrder_;
+ unpackState_[resource] -> geometry -> bitmap_bit_order = bitmapBitOrder_;
+ unpackState_[resource] -> geometry -> scanline_unit = scanlineUnit_;
+ unpackState_[resource] -> geometry -> scanline_pad = scanlinePad_;
+ }
+}
+
+void ServerChannel::handleUnpackAllocColormap(int resource)
+{
+ if (unpackState_[resource] -> colormap == NULL)
+ {
+ unpackState_[resource] -> colormap = new T_colormap();
+
+ if (unpackState_[resource] -> colormap == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpackAllocColormap: PANIC! Can't allocate "
+ << "memory for unpack state in context [C].\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "unpack state in context [C].\n";
+
+ HandleAbort();
+ }
+
+ unpackState_[resource] -> colormap -> entries = 0;
+ unpackState_[resource] -> colormap -> data = NULL;
+ }
+}
+
+void ServerChannel::handleUnpackAllocAlpha(int resource)
+{
+ if (unpackState_[resource] -> alpha == NULL)
+ {
+ unpackState_[resource] -> alpha = new T_alpha();
+
+ if (unpackState_[resource] -> alpha == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "handleUnpackAllocAlpha: PANIC! Can't allocate "
+ << "memory for unpack state in context [D].\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "unpack state in context [D].\n";
+
+ HandleAbort();
+ }
+
+ unpackState_[resource] -> alpha -> entries = 0;
+ unpackState_[resource] -> alpha -> data = NULL;
+ }
+}
+
+void ServerChannel::handleUnpackStateRemove(int resource)
+{
+ if (unpackState_[resource] != NULL)
+ {
+ delete unpackState_[resource] -> geometry;
+
+ if (unpackState_[resource] -> colormap != NULL)
+ {
+ delete [] unpackState_[resource] -> colormap -> data;
+ }
+
+ delete unpackState_[resource] -> colormap;
+
+ if (unpackState_[resource] -> alpha != NULL)
+ {
+ delete [] unpackState_[resource] -> alpha -> data;
+ }
+
+ delete unpackState_[resource] -> alpha;
+
+ delete unpackState_[resource];
+
+ unpackState_[resource] = NULL;
+ }
+}
+
+void ServerChannel::handleEncodeCharInfo(const unsigned char *nextSrc, EncodeBuffer &encodeBuffer)
+{
+ unsigned int value = GetUINT(nextSrc, bigEndian_) |
+ (GetUINT(nextSrc + 10, bigEndian_) << 16);
+
+ encodeBuffer.encodeCachedValue(value, 32,
+ *serverCache_ -> queryFontCharInfoCache[0], 6);
+
+ nextSrc += 2;
+
+ for (unsigned int i = 1; i < 5; i++)
+ {
+ unsigned int _value = GetUINT(nextSrc, bigEndian_);
+
+ nextSrc += 2;
+
+ encodeBuffer.encodeCachedValue(_value, 16,
+ *serverCache_ -> queryFontCharInfoCache[i], 6);
+ }
+}
+
+int ServerChannel::setBigEndian(int flag)
+{
+ bigEndian_ = flag;
+
+ readBuffer_.setBigEndian(flag);
+
+ return 1;
+}
+
+int ServerChannel::setReferences()
+{
+ #ifdef TEST
+ *logofs << "ServerChannel: Initializing the static "
+ << "members for the server channels.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef REFERENCES
+
+ references_ = 0;
+
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/ServerChannel.h b/nxcomp/src/ServerChannel.h
new file mode 100644
index 000000000..374e52896
--- /dev/null
+++ b/nxcomp/src/ServerChannel.h
@@ -0,0 +1,529 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ServerChannel_H
+#define ServerChannel_H
+
+#include "List.h"
+#include "Channel.h"
+
+#include "SequenceQueue.h"
+
+#include "ServerReadBuffer.h"
+
+#include "Unpack.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// How many sequence numbers of split commit
+// requests we are going to save in order to
+// mask errors.
+//
+
+#define MAX_COMMIT_SEQUENCE_QUEUE 16
+
+//
+// Define this to know when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// This class implements the X server
+// side compression of X protocol.
+//
+
+class ServerChannel : public Channel
+{
+ public:
+
+ ServerChannel(Transport *transport, StaticCompressor *compressor);
+
+ virtual ~ServerChannel();
+
+ virtual int handleRead(EncodeBuffer &encodeBuffer, const unsigned char *message,
+ unsigned int length);
+
+ virtual int handleWrite(const unsigned char *message, unsigned int length);
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer, MessageStore *store,
+ T_store_action action, int position, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size)
+ {
+ return 0;
+ }
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer, MessageStore *store,
+ T_store_action action, int position, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ virtual int handleSplit(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleSplit(DecodeBuffer &decodeBuffer);
+
+ virtual int handleSplitEvent(EncodeBuffer &encodeBuffer, Split *split);
+
+ virtual int handleSplitEvent(DecodeBuffer &decodeBuffer)
+ {
+ return 0;
+ }
+
+ //
+ // Send the last motion notify event
+ // received from the X server to the
+ // remote proxy.
+ //
+
+ virtual int handleMotion(EncodeBuffer &encodeBuffer);
+
+ virtual int handleCompletion(EncodeBuffer &encodeBuffer)
+ {
+ return 0;
+ }
+
+ virtual int handleConfiguration();
+
+ virtual int handleFinish();
+
+ virtual int handleAsyncEvents();
+
+ virtual int needSplit() const
+ {
+ return 0;
+ }
+
+ virtual int needMotion() const
+ {
+ return (lastMotion_[0] != '\0');
+ }
+
+ virtual T_channel_type getType() const
+ {
+ return channel_x11;
+ }
+
+ int setBigEndian(int flag);
+
+ //
+ // Initialize the static members.
+ //
+
+ static int setReferences();
+
+ private:
+
+ int handleFastReadReply(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size);
+
+ int handleFastReadEvent(EncodeBuffer &encodeBuffer, const unsigned char &opcode,
+ const unsigned char *&buffer, const unsigned int &size);
+
+ int handleFastWriteRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Handle the fake authorization cookie
+ // and the X server's reply.
+ //
+
+ int handleAuthorization(unsigned char *buffer);
+ int handleAuthorization(const unsigned char *buffer, int size);
+
+ //
+ // Set the unpack colormap and the alpha
+ // blending data to be used to unpack
+ // images.
+ //
+
+ int handleGeometry(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleColormap(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ int handleAlpha(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ //
+ // Manage the decoded buffer to unpack
+ // the image and move the data to the
+ // shared memory segment.
+ //
+
+ int handleImage(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ //
+ // Uncompress a packed image in one
+ // or more graphic X requests.
+ //
+
+ int handleUnpack(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ //
+ // Move the image to the shared
+ // memory buffer.
+ //
+
+ int handleShmem(unsigned char &opcode, unsigned char *&buffer,
+ unsigned int &size);
+
+ //
+ // Handle suppression of error on
+ // commit of image splits.
+ //
+
+ void initCommitQueue();
+
+ void updateCommitQueue(unsigned short sequence);
+
+ int checkCommitError(unsigned char error, unsigned short sequence,
+ const unsigned char *buffer);
+
+ void clearCommitQueue()
+ {
+ if (commitSequenceQueue_[0] != 0)
+ {
+ initCommitQueue();
+ }
+ }
+
+ //
+ // Check if the user pressed the
+ // CTRL+ALT+SHIFT+ESC keystroke.
+ //
+
+ int checkKeyboardEvent(unsigned char event, unsigned short sequence,
+ const unsigned char *buffer);
+
+ //
+ // Other utilities.
+ //
+
+ void handleEncodeCharInfo(const unsigned char *nextSrc, EncodeBuffer &encodeBuffer);
+
+ //
+ // Handle the MIT-SHM initialization
+ // messages exchanged with the remote
+ // proxy.
+ //
+
+ int handleShmemRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+
+ int handleShmemReply(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned int stage, const unsigned char *buffer,
+ const unsigned int size);
+
+ //
+ // Try to read more events in the attempt to
+ // get the MIT-SHM image completion event
+ // from the X server.
+ //
+
+ int handleShmemEvent();
+
+ //
+ // Handle the MIT-SHM events as they are read
+ // from the socket.
+ //
+
+ int checkShmemEvent(unsigned char event, unsigned short sequence,
+ const unsigned char *buffer);
+
+ int checkShmemError(unsigned char error, unsigned short sequence,
+ const unsigned char *buffer);
+
+ //
+ // Query the port used to tunnel
+ // the font server connections.
+ //
+
+ int handleFontRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ int handleFontReply(EncodeBuffer &encodeBuffer, const unsigned char opcode,
+ const unsigned char *buffer, const unsigned int size);
+
+ //
+ // Set the cache policy for image
+ // requests.
+ //
+
+ int handleCacheRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Decode the start and end split
+ // requests.
+ //
+
+ int handleStartSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ int handleEndSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Remove the split store and the
+ // incomplete messages from the
+ // memory cache.
+ //
+
+ int handleAbortSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ //
+ // Send the split requests to the
+ // X server once they have been
+ // recomposed.
+ //
+
+ int handleCommitSplitRequest(DecodeBuffer &decodeBuffer, unsigned char &opcode,
+ unsigned char *&buffer, unsigned int &size);
+
+ int handleSplitChecksum(DecodeBuffer &decodeBuffer, T_checksum &checksum);
+
+ //
+ // Allocate and free the shared memory
+ // support resources.
+ //
+
+ void handleShmemStateAlloc();
+ void handleShmemStateRemove();
+
+ //
+ // Temporary storage for the image info.
+ //
+
+ void handleImageStateAlloc(unsigned char opcode)
+ {
+ if (imageState_ == NULL)
+ {
+ imageState_ = new T_image_state();
+ }
+
+ imageState_ -> opcode = opcode;
+ }
+
+ void handleImageStateRemove()
+ {
+ if (imageState_ != NULL)
+ {
+ delete imageState_;
+
+ imageState_ = NULL;
+ }
+ }
+
+ //
+ // Store the information needed to unpack
+ // images per each known agent's client.
+ //
+
+ void handleUnpackStateInit(int resource);
+
+ void handleUnpackAllocGeometry(int resource);
+ void handleUnpackAllocColormap(int resource);
+ void handleUnpackAllocAlpha(int resource);
+
+ void handleUnpackStateRemove(int resource);
+
+ typedef struct
+ {
+ T_geometry *geometry;
+ T_colormap *colormap;
+ T_alpha *alpha;
+
+ } T_unpack_state;
+
+ T_unpack_state *unpackState_[256];
+
+ //
+ // Own read buffer. It is able to identify
+ // full messages read from X descriptor.
+ //
+
+ ServerReadBuffer readBuffer_;
+
+ //
+ // Sequence number of last request coming
+ // from X client or X server.
+ //
+
+ unsigned int clientSequence_;
+ unsigned int serverSequence_;
+
+ //
+ // Used to identify replies based on sequence
+ // number of original request.
+ //
+
+ SequenceQueue sequenceQueue_;
+
+ //
+ // Last motion notify read from the X server.
+ //
+
+ unsigned char lastMotion_[32];
+
+ //
+ // Sequence numbers of last auto-generated
+ // put image requests. Needed to intercept
+ // and suppress errors generated by such
+ // requests.
+ //
+
+ unsigned int commitSequenceQueue_[MAX_COMMIT_SEQUENCE_QUEUE];
+
+ //
+ // Let agent select which expose
+ // events is going to receive.
+ //
+
+ unsigned int enableExpose_;
+ unsigned int enableGraphicsExpose_;
+ unsigned int enableNoExpose_;
+
+ //
+ // Used in initialization and handling
+ // of MIT-SHM shared memory put images.
+ //
+
+ typedef struct
+ {
+ int stage;
+ int present;
+ int enabled;
+ int segment;
+ int id;
+ void *address;
+ unsigned int size;
+
+ unsigned char opcode;
+ unsigned char event;
+ unsigned char error;
+
+ unsigned int sequence;
+ unsigned int offset;
+ T_timestamp last;
+
+ unsigned int checked;
+
+ } T_shmem_state;
+
+ T_shmem_state *shmemState_;
+
+ //
+ // Used to pass current image data between
+ // the different decompression stages.
+ //
+
+ typedef struct
+ {
+ unsigned char opcode;
+
+ unsigned int drawable;
+ unsigned int gcontext;
+
+ unsigned char method;
+
+ unsigned char format;
+ unsigned char srcDepth;
+ unsigned char dstDepth;
+
+ unsigned int srcLength;
+ unsigned int dstLength;
+ unsigned int dstLines;
+
+ short int srcX;
+ short int srcY;
+ unsigned short srcWidth;
+ unsigned short srcHeight;
+
+ short int dstX;
+ short int dstY;
+ unsigned short dstWidth;
+ unsigned short dstHeight;
+
+ unsigned char leftPad;
+
+ } T_image_state;
+
+ T_image_state *imageState_;
+
+ //
+ // The flags is set according to the
+ // split load and save policy set by
+ // the encoding side.
+ //
+
+ typedef struct
+ {
+ int resource;
+ int current;
+ int load;
+ int save;
+ int commit;
+
+ } T_split_state;
+
+ T_split_state splitState_;
+
+ //
+ // List of agent resources.
+ //
+
+ List splitResources_;
+
+ //
+ // Keep track of object creation and
+ // deletion.
+ //
+
+ private:
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+#endif /* ServerChannel_H */
diff --git a/nxcomp/src/ServerProxy.cpp b/nxcomp/src/ServerProxy.cpp
new file mode 100644
index 000000000..28f94842a
--- /dev/null
+++ b/nxcomp/src/ServerProxy.cpp
@@ -0,0 +1,621 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+
+#include "NXalert.h"
+
+#include "Socket.h"
+
+#include "ServerProxy.h"
+
+#include "ServerChannel.h"
+#include "GenericChannel.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Log the operations related to sending
+// and receiving the control tokens.
+//
+
+#undef TOKEN
+
+ServerProxy::ServerProxy(int proxyFd) : Proxy(proxyFd)
+
+{
+ xServerAddrFamily_ = -1;
+ xServerAddrLength_ = 0;
+
+ xServerAddr_ = NULL;
+ xServerDisplay_ = NULL;
+
+ cupsServerPort_ = NULL;
+ smbServerPort_ = NULL;
+ mediaServerPort_ = NULL;
+ httpServerPort_ = NULL;
+
+ fontServerPort_ = NULL;
+
+ #ifdef DEBUG
+ *logofs << "ServerProxy: Created new object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+ServerProxy::~ServerProxy()
+{
+ delete xServerAddr_;
+
+ delete [] xServerDisplay_;
+
+ delete [] fontServerPort_;
+
+ #ifdef DEBUG
+ *logofs << "ServerProxy: Deleted object at " << this
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+void ServerProxy::handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily,
+ sockaddr *xServerAddr, unsigned int xServerAddrLength)
+{
+ delete xServerAddr_;
+
+ xServerAddr_ = xServerAddr;
+
+ xServerAddrFamily_ = xServerAddrFamily;
+ xServerAddrLength_ = xServerAddrLength;
+
+ delete [] xServerDisplay_;
+
+ xServerDisplay_ = new char[strlen(xServerDisplay) + 1];
+
+ strcpy(xServerDisplay_, xServerDisplay);
+
+ #ifdef DEBUG
+ *logofs << "ServerProxy: Set display configuration to display '"
+ << xServerDisplay_ << "'.\n"
+ << logofs_flush;
+ #endif
+}
+
+void ServerProxy::handlePortConfiguration(ChannelEndPoint &cupsServerPort,
+ ChannelEndPoint &smbServerPort,
+ ChannelEndPoint &mediaServerPort,
+ ChannelEndPoint &httpServerPort,
+ const char *fontServerPort)
+{
+ cupsServerPort_ = cupsServerPort;
+ smbServerPort_ = smbServerPort;
+ mediaServerPort_ = mediaServerPort;
+ httpServerPort_ = httpServerPort;
+
+ delete [] fontServerPort_;
+
+ fontServerPort_ = new char[strlen(fontServerPort) + 1];
+
+ strcpy(fontServerPort_, fontServerPort);
+
+ #ifdef DEBUG
+ *logofs << "ServerProxy: Set port configuration to CUPS "
+ << cupsServerPort_ << ", SMB " << smbServerPort_
+ << ", media " << mediaServerPort_ << ", HTTP "
+ << httpServerPort_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+int ServerProxy::handleNewConnection(T_channel_type type, int clientFd)
+{
+ switch (type)
+ {
+ case channel_font:
+ {
+ return handleNewGenericConnection(clientFd, channel_font, "font");
+ }
+ case channel_slave:
+ {
+ return handleNewSlaveConnection(clientFd);
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Unsupported channel with type '"
+ << getTypeName(type) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unsupported channel with type '"
+ << getTypeName(type) << "'.\n";
+
+ return -1;
+ }
+ }
+}
+
+int ServerProxy::handleNewConnectionFromProxy(T_channel_type type, int channelId)
+{
+ switch (type)
+ {
+ case channel_x11:
+ {
+ return handleNewXConnectionFromProxy(channelId);
+ }
+ case channel_cups:
+ {
+ return handleNewGenericConnectionFromProxy(channelId, channel_cups,
+ cupsServerPort_, "CUPS");
+ }
+ case channel_smb:
+ {
+ smbServerPort_.setDefaultTCPInterface(1);
+ return handleNewGenericConnectionFromProxy(channelId, channel_smb,
+ smbServerPort_, "SMB");
+ }
+ case channel_media:
+ {
+ return handleNewGenericConnectionFromProxy(channelId, channel_media,
+ mediaServerPort_, "media");
+ }
+ case channel_http:
+ {
+ return handleNewGenericConnectionFromProxy(channelId, channel_http,
+ httpServerPort_, "HTTP");
+ }
+ case channel_slave:
+ {
+ return handleNewSlaveConnectionFromProxy(channelId);
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Unsupported channel with type '"
+ << getTypeName(type) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Unsupported channel with type '"
+ << getTypeName(type) << "'.\n";
+
+ return -1;
+ }
+ }
+}
+
+int ServerProxy::handleNewAgentConnection(Agent *agent)
+{
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Can't create an agent "
+ << "connection at this side.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create an agent "
+ << "connection at this side.\n";
+
+ return -1;
+}
+
+int ServerProxy::handleNewXConnection(int clientFd)
+{
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Can't create a new X channel "
+ << "with FD#" << clientFd << " at this side.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't create a new X channel "
+ << "with FD#" << clientFd << " at this side.\n";
+
+ return -1;
+}
+
+int ServerProxy::handleNewXConnectionFromProxy(int channelId)
+{
+ //
+ // Connect to the real X server.
+ //
+
+ int retryConnect = control -> OptionServerRetryConnect;
+
+ int xServerFd;
+
+ for (;;)
+ {
+ xServerFd = socket(xServerAddrFamily_, SOCK_STREAM, PF_UNSPEC);
+
+ if (xServerFd < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Call to socket failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "ServerProxy: Trying to connect to X server '"
+ << xServerDisplay_ << "'.\n" << logofs_flush;
+ #endif
+
+ int result = connect(xServerFd, xServerAddr_, xServerAddrLength_);
+
+ getNewTimestamp();
+
+ if (result < 0)
+ {
+ #ifdef WARNING
+ *logofs << "ServerProxy: WARNING! Connection to '"
+ << xServerDisplay_ << "' failed with error '"
+ << ESTR() << "'. Retrying.\n" << logofs_flush;
+ #endif
+
+ close(xServerFd);
+
+ if (--retryConnect == 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Connection to '"
+ << xServerDisplay_ << "' for channel ID#"
+ << channelId << " failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Connection to '"
+ << xServerDisplay_ << "' failed. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ close(xServerFd);
+
+ return -1;
+ }
+
+ if (activeChannels_.getSize() == 0)
+ {
+ sleep(2);
+ }
+ else
+ {
+ sleep(1);
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ assignChannelMap(channelId, xServerFd);
+
+ #ifdef TEST
+ *logofs << "ServerProxy: X server descriptor FD#" << xServerFd
+ << " mapped to channel ID#" << channelId << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Turn queuing off for path proxy-to-X-server.
+ //
+
+ if (control -> OptionServerNoDelay == 1)
+ {
+ SetNoDelay(xServerFd, control -> OptionServerNoDelay);
+ }
+
+ //
+ // If requested, set the size of the TCP send
+ // and receive buffers.
+ //
+
+ if (control -> OptionServerSendBuffer != -1)
+ {
+ SetSendBuffer(xServerFd, control -> OptionServerSendBuffer);
+ }
+
+ if (control -> OptionServerReceiveBuffer != -1)
+ {
+ SetReceiveBuffer(xServerFd, control -> OptionServerReceiveBuffer);
+ }
+
+ if (allocateTransport(xServerFd, channelId) < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Starting from protocol level 3 client and server
+ // caches are created in proxy and shared between all
+ // channels. If remote proxy has older protocol level
+ // pointers are NULL and channels must create their
+ // own instances.
+ //
+
+ channels_[channelId] = new ServerChannel(transports_[channelId], compressor_);
+
+ if (channels_[channelId] == NULL)
+ {
+ deallocateTransport(channelId);
+
+ return -1;
+ }
+
+ increaseChannels(channelId);
+
+ //
+ // Propagate channel stores and caches to the new
+ // channel.
+ //
+
+ channels_[channelId] -> setOpcodes(opcodeStore_);
+
+ channels_[channelId] -> setStores(clientStore_, serverStore_);
+
+ channels_[channelId] -> setCaches(clientCache_, serverCache_);
+
+ int port = atoi(fontServerPort_);
+
+ if (port > 0)
+ {
+ channels_[channelId] -> setPorts(port);
+ }
+
+ //
+ // Let channel configure itself according
+ // to control parameters.
+ //
+
+ channels_[channelId] -> handleConfiguration();
+
+ //
+ // Check if we have successfully loaded the
+ // selected cache and, if not, remove it
+ // from disk.
+ //
+
+ handleCheckLoad();
+
+ return 1;
+}
+
+//
+// Check if we still need to drop a channel. We need
+// to check this explicitly at the time we receive a
+// request to load or save the cache because we could
+// receive the control message before having entered
+// the function handling the channel events.
+//
+
+int ServerProxy::handleCheckDrop()
+{
+ T_list channelList = activeChannels_.copyList();
+
+ for (T_list::iterator j = channelList.begin();
+ j != channelList.end(); j++)
+ {
+ int channelId = *j;
+
+ if (channels_[channelId] != NULL &&
+ (channels_[channelId] -> getDrop() == 1 ||
+ channels_[channelId] -> getClosing() == 1))
+ {
+ #ifdef TEST
+ *logofs << "ServerProxy: Dropping the descriptor FD#"
+ << getFd(channelId) << " channel ID#"
+ << channelId << ".\n" << logofs_flush;
+ #endif
+
+ handleDrop(channelId);
+ }
+ }
+
+ return 1;
+}
+
+int ServerProxy::handleCheckLoad()
+{
+ //
+ // Check if we just created the first X channel
+ // but the client side didn't tell us to load
+ // the cache selected at the session negotiation.
+ // This is very likely because the load operation
+ // failed at the remote side, for example because
+ // the cache was invalid or corrupted.
+ //
+
+ int channelCount = getChannels(channel_x11);
+
+ if (channelCount != 1)
+ {
+ return 0;
+ }
+
+ if (control -> PersistentCacheEnableLoad == 1 &&
+ control -> PersistentCachePath != NULL &&
+ control -> PersistentCacheName != NULL &&
+ isTimestamp(timeouts_.loadTs) == 0)
+ {
+ #ifdef WARNING
+ *logofs << "ServerProxy: WARNING! Cache file '" << control -> PersistentCachePath
+ << "/" << control -> PersistentCacheName << "' not loaded.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Remove the cache file.
+ //
+
+ #ifdef WARNING
+ *logofs << "ServerProxy: WARNING! Removing supposedly "
+ << "incompatible cache '" << control -> PersistentCachePath
+ << "/" << control -> PersistentCacheName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ handleResetPersistentCache();
+ }
+
+ return 1;
+}
+
+int ServerProxy::handleLoadFromProxy()
+{
+ //
+ // Be sure we drop any confirmed channel.
+ //
+
+ handleCheckDrop();
+
+ //
+ // Check that either no X channel is
+ // remaining or we are inside a reset.
+ //
+
+ int channelCount = getChannels(channel_x11);
+
+ if (channelCount > 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Protocol violation "
+ << "in command load with " << channelCount
+ << " channels.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Protocol violation "
+ << "in command load from proxy.\n";
+
+ return -1;
+ }
+ else if (handleLoadStores() < 0)
+ {
+ #ifdef WARNING
+ *logofs << "ServerProxy: WARNING! Failed to load content "
+ << "of persistent cache.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ServerProxy::handleSaveFromProxy()
+{
+ //
+ // Be sure we drop any confirmed channel.
+ //
+
+ handleCheckDrop();
+
+ //
+ // Now verify that all channels are gone.
+ //
+
+ int channelCount = getChannels(channel_x11);
+
+ if (channelCount > 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Protocol violation "
+ << "in command save with " << channelCount
+ << " channels.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Protocol violation "
+ << "in command save from proxy.\n";
+
+ return -1;
+ }
+ else if (handleSaveStores() < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerProxy: PANIC! Failed to save stores "
+ << "to persistent cache.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int ServerProxy::handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient) const
+{
+ if (clientStore_ -> saveRequestStores(cachefs, md5StateStream, md5StateClient,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> saveReplyStores(cachefs, md5StateStream, md5StateClient,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> saveEventStores(cachefs, md5StateStream, md5StateClient,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
+
+int ServerProxy::handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const
+{
+ if (clientStore_ -> loadRequestStores(cachefs, md5StateStream,
+ discard_checksum, use_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> loadReplyStores(cachefs, md5StateStream,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+ else if (serverStore_ -> loadEventStores(cachefs, md5StateStream,
+ use_checksum, discard_data) < 0)
+ {
+ return -1;
+ }
+
+ return 1;
+}
diff --git a/nxcomp/src/ServerProxy.h b/nxcomp/src/ServerProxy.h
new file mode 100644
index 000000000..e169c4aec
--- /dev/null
+++ b/nxcomp/src/ServerProxy.h
@@ -0,0 +1,154 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ServerProxy_H
+#define ServerProxy_H
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "Proxy.h"
+
+#include "Misc.h"
+#include "ChannelEndPoint.h"
+
+//
+// Set the verbosity level.
+//
+
+#undef TEST
+#undef DEBUG
+
+class ServerProxy : public Proxy
+{
+ public:
+
+ ServerProxy(int proxyFd);
+
+ virtual ~ServerProxy();
+
+ virtual void handleDisplayConfiguration(const char *xServerDisplay, int xServerAddrFamily,
+ sockaddr *xServerAddr, unsigned int xServerAddrLength);
+
+ virtual void handlePortConfiguration(ChannelEndPoint &cupsServerPort,
+ ChannelEndPoint &smbServerPort,
+ ChannelEndPoint &mediaServerPort,
+ ChannelEndPoint &httpServerPort,
+ const char *fontServerPort);
+
+ protected:
+
+ //
+ // Create a new channel.
+ //
+
+ virtual int handleNewConnection(T_channel_type type, int clientFd);
+
+ virtual int handleNewConnectionFromProxy(T_channel_type type, int channelId);
+
+ virtual int handleNewAgentConnection(Agent *agent);
+
+ virtual int handleNewXConnection(int clientFd);
+
+ virtual int handleNewXConnectionFromProxy(int channelId);
+
+ //
+ // Implement persistence according
+ // to our proxy mode.
+ //
+
+ virtual int handleLoad(T_load_type type)
+ {
+ return 0;
+ }
+
+ virtual int handleSave()
+ {
+ return 0;
+ }
+
+ virtual int handleAsyncEvents()
+ {
+ return 0;
+ }
+
+ virtual int handleLoadFromProxy();
+ virtual int handleSaveFromProxy();
+
+ virtual int handleSaveAllStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient) const;
+
+ virtual int handleLoadAllStores(istream *cachefs, md5_state_t *md5StateStream) const;
+
+ int handleCheckDrop();
+ int handleCheckLoad();
+
+ //
+ // Utility function used to realize
+ // a new connection.
+ //
+
+ protected:
+
+ virtual int checkLocalChannelMap(int channelId)
+ {
+ // Since ProtoStep7 (#issue 108)
+ return ((channelId & control -> ChannelMask) == 0);
+ }
+
+ private:
+
+ // FIXME: Use a ChannelEndPoint object also for the X server!
+ int xServerAddrFamily_;
+ sockaddr *xServerAddr_;
+ unsigned int xServerAddrLength_;
+
+ //
+ // This is the name of the X display where
+ // we are going to forward connections.
+ //
+
+ char *xServerDisplay_;
+
+ //
+ // Ports where to forward extended services'
+ // TCP connections.
+ //
+
+ ChannelEndPoint cupsServerPort_;
+ ChannelEndPoint smbServerPort_;
+ ChannelEndPoint mediaServerPort_;
+ ChannelEndPoint httpServerPort_;
+
+ //
+ // It will have to be passed to the channel
+ // so that it can set the port where the
+ // font server connections are tunneled.
+ //
+
+ char *fontServerPort_;
+};
+
+#endif /* ServerProxy_H */
diff --git a/nxcomp/src/ServerReadBuffer.cpp b/nxcomp/src/ServerReadBuffer.cpp
new file mode 100644
index 000000000..277b85216
--- /dev/null
+++ b/nxcomp/src/ServerReadBuffer.cpp
@@ -0,0 +1,247 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ServerReadBuffer.h"
+#include "ServerChannel.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+unsigned int ServerReadBuffer::suggestedLength(unsigned int pendingLength)
+{
+ //
+ // Always read all the data that
+ // is available.
+ //
+
+ int readable = transport_ -> readable();
+
+ unsigned int readLength = (readable == -1 ? 0 : (unsigned int) readable);
+
+ if (readLength < pendingLength)
+ {
+ readLength = pendingLength;
+ }
+
+ //
+ // Even if the readable data is not
+ // enough to make a complete message,
+ // resize the buffer to accommodate
+ // it all.
+ //
+
+ if (pendingLength < remaining_)
+ {
+ readLength = remaining_;
+ }
+
+ return readLength;
+}
+
+int ServerReadBuffer::locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength)
+{
+ unsigned int size = end - start;
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Locating message for FD#"
+ << transport_ -> fd() << " with " << size
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ if (firstMessage_)
+ {
+ if (size < 8)
+ {
+ remaining_ = 8 - size;
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ dataLength = 8 + (GetUINT(start + 6, bigEndian_) << 2);
+ }
+ else
+ {
+ if (size < 32)
+ {
+ remaining_ = 32 - size;
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ if (*start == 1)
+ {
+ dataLength = 32 + (GetULONG(start + 4, bigEndian_) << 2);
+ }
+ else
+ {
+ dataLength = 32;
+ }
+
+ if (dataLength < 32)
+ {
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: WARNING! Assuming length 32 "
+ << "for suspicious message of length " << dataLength
+ << ".\n" << logofs_flush;
+ #endif
+
+ dataLength = 32;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Length of the next message is "
+ << dataLength << ".\n" << logofs_flush;
+ #endif
+
+ if (size < dataLength)
+ {
+ remaining_ = dataLength - size;
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: No message was located "
+ << "with remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ firstMessage_ = 0;
+
+ controlLength = 0;
+ trailerLength = 0;
+
+ remaining_ = 0;
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Located message with "
+ << "remaining " << remaining_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+//
+// Check if the data already read contains a
+// message matching the opcode and sequence,
+// starting at the given offset.
+//
+
+unsigned char *ServerReadBuffer::peekMessage(unsigned int &offset, unsigned char opcode,
+ unsigned short sequence)
+{
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Peeking message "
+ << "for FD#" << transport_ -> fd() << " with size "
+ << length_ << " offset " << offset << " opcode "
+ << (unsigned int) opcode << " and sequence "
+ << sequence << ".\n" << logofs_flush;
+ #endif
+
+ if (firstMessage_)
+ {
+ return NULL;
+ }
+
+ unsigned char *next = buffer_ + start_ + offset;
+ unsigned char *end = buffer_ + start_ + length_;
+
+ int found = 0;
+
+ while (end - next >= 32)
+ {
+ #ifdef DEBUG
+ *logofs << "ServerReadBuffer: Checking opcode "
+ << (unsigned int) *next << " sequence "
+ << GetUINT(next + 2, bigEndian_)
+ << " at " << next - buffer_ + start_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (*next == opcode && GetUINT(next + 2, bigEndian_) == sequence)
+ {
+ found = 1;
+
+ break;
+ }
+ else if (*next == 1)
+ {
+ next += (32 + (GetULONG(next + 4, bigEndian_) << 2));
+ }
+ else
+ {
+ next += 32;
+ }
+ }
+
+ offset = next - buffer_ + start_;
+
+ if (found == 1)
+ {
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Found message at "
+ << "offset " << next - buffer_ + start_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return next;
+ }
+
+ #ifdef TEST
+ *logofs << "ServerReadBuffer: Quitting loop at "
+ << "offset " << next - buffer_ + start_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return NULL;
+}
diff --git a/nxcomp/src/ServerReadBuffer.h b/nxcomp/src/ServerReadBuffer.h
new file mode 100644
index 000000000..d6c207ead
--- /dev/null
+++ b/nxcomp/src/ServerReadBuffer.h
@@ -0,0 +1,73 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ServerReadBuffer_H
+#define ServerReadBuffer_H
+
+#include "ReadBuffer.h"
+#include "Control.h"
+
+class ServerChannel;
+
+class ServerReadBuffer : public ReadBuffer
+{
+ public:
+
+ ServerReadBuffer(Transport *transport, ServerChannel *channel)
+
+ : ReadBuffer(transport), firstMessage_(1), channel_(channel)
+ {
+ }
+
+ virtual ~ServerReadBuffer()
+ {
+ }
+
+ void setBigEndian(int flag)
+ {
+ bigEndian_ = flag;
+ }
+
+ unsigned char *peekMessage(unsigned int &offset, unsigned char opcode,
+ unsigned short sequence);
+
+ protected:
+
+ virtual unsigned int suggestedLength(unsigned int pendingLength);
+
+ virtual int locateMessage(const unsigned char *start,
+ const unsigned char *end,
+ unsigned int &controlLength,
+ unsigned int &dataLength,
+ unsigned int &trailerLength);
+
+ int bigEndian_;
+
+ int firstMessage_;
+
+ ServerChannel *channel_;
+};
+
+#endif /* ServerReadBuffer_H */
diff --git a/nxcomp/src/ServerStore.cpp b/nxcomp/src/ServerStore.cpp
new file mode 100644
index 000000000..8123b6de0
--- /dev/null
+++ b/nxcomp/src/ServerStore.cpp
@@ -0,0 +1,183 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ServerStore.h"
+
+//
+// Cached reply classes.
+//
+
+#include "GetImageReply.h"
+#include "ListFontsReply.h"
+#include "QueryFontReply.h"
+#include "GetPropertyReply.h"
+#include "GenericReply.h"
+
+//
+// Set the verbosity level.
+//
+
+#define WARNING
+#define PANIC
+#undef TEST
+
+ServerStore::ServerStore(StaticCompressor *compressor)
+{
+ if (logofs == NULL)
+ {
+ logofs = &cout;
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ replies_[i] = NULL;
+ events_[i] = NULL;
+ }
+
+ replies_[X_ListFonts] = new ListFontsReplyStore(compressor);
+ replies_[X_QueryFont] = new QueryFontReplyStore(compressor);
+ replies_[X_GetImage] = new GetImageReplyStore(compressor);
+ replies_[X_GetProperty] = new GetPropertyReplyStore(compressor);
+
+ replies_[X_NXInternalGenericReply] = new GenericReplyStore(compressor);
+}
+
+ServerStore::~ServerStore()
+{
+ if (logofs == NULL)
+ {
+ logofs = &cout;
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ delete replies_[i];
+ delete events_[i];
+ }
+}
+
+int ServerStore::saveReplyStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (replies_[i] != NULL &&
+ replies_[i] -> saveStore(cachefs, md5StateStream, md5StateClient,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerStore: PANIC! Error saving reply store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error saving reply store "
+ << "for opcode '" << (unsigned int) i << "'.\n";
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int ServerStore::saveEventStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (events_[i] != NULL &&
+ events_[i] -> saveStore(cachefs, md5StateStream, md5StateClient,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerStore: PANIC! Error saving event store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Error saving event store "
+ << "for opcode '" << (unsigned int) i << "'.\n";
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int ServerStore::loadReplyStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (replies_[i] != NULL &&
+ replies_[i] -> loadStore(cachefs, md5StateStream,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerStore: PANIC! Error loading reply store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+int ServerStore::loadEventStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const
+{
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (events_[i] != NULL &&
+ events_[i] -> loadStore(cachefs, md5StateStream,
+ checksumAction, dataAction,
+ storeBigEndian()) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "ServerStore: PANIC! Error loading event store "
+ << "for OPCODE#" << (unsigned int) i << ".\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ return 1;
+}
diff --git a/nxcomp/src/ServerStore.h b/nxcomp/src/ServerStore.h
new file mode 100644
index 000000000..dbbb968e5
--- /dev/null
+++ b/nxcomp/src/ServerStore.h
@@ -0,0 +1,83 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ServerStore_H
+#define ServerStore_H
+
+#include "Message.h"
+
+#include "ChannelStore.h"
+
+class StaticCompressor;
+
+class ServerStore : public ChannelStore
+{
+ public:
+
+ ServerStore(StaticCompressor *compressor);
+
+ virtual ~ServerStore();
+
+ MessageStore *getReplyStore(unsigned char opcode) const
+ {
+ return replies_[opcode];
+ }
+
+ MessageStore *getEventStore(unsigned char opcode) const
+ {
+ return events_[opcode];
+ }
+
+ //
+ // Actually save the message store
+ // to disk according to proxy mode.
+ //
+
+ int saveReplyStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const;
+
+ int saveEventStores(ostream *cachefs, md5_state_t *md5StateStream,
+ md5_state_t *md5StateClient, T_checksum_action checksumAction,
+ T_data_action dataAction) const;
+
+
+ int loadReplyStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const;
+
+ int loadEventStores(istream *cachefs, md5_state_t *md5StateStream,
+ T_checksum_action checksumAction, T_data_action dataAction) const;
+
+ private:
+
+ //
+ // A server store contains replies and events.
+ //
+
+ MessageStore *replies_[CHANNEL_STORE_OPCODE_LIMIT];
+ MessageStore *events_[CHANNEL_STORE_OPCODE_LIMIT];
+};
+
+#endif /* ServerStore_H */
diff --git a/nxcomp/src/SetClipRectangles.cpp b/nxcomp/src/SetClipRectangles.cpp
new file mode 100644
index 000000000..b43cea938
--- /dev/null
+++ b/nxcomp/src/SetClipRectangles.cpp
@@ -0,0 +1,154 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SetClipRectangles.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SetClipRectanglesStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ setClipRectangles -> ordering = *(buffer + 1);
+
+ setClipRectangles -> gcontext = GetULONG(buffer + 4, bigEndian);
+
+ setClipRectangles -> x_origin = GetUINT(buffer + 8, bigEndian);
+ setClipRectangles -> y_origin = GetUINT(buffer + 10, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetClipRectanglesStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ *(buffer + 1) = setClipRectangles -> ordering;
+
+ PutULONG(setClipRectangles -> gcontext, buffer + 4, bigEndian);
+
+ PutUINT(setClipRectangles -> x_origin, buffer + 8, bigEndian);
+ PutUINT(setClipRectangles -> y_origin, buffer + 10, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SetClipRectanglesStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message;
+
+ *logofs << name() << ": Identity ordering " << (unsigned int) setClipRectangles -> ordering
+ << ", gcontext " << setClipRectangles -> gcontext << ", x_origin "
+ << setClipRectangles -> x_origin << ", y_origin "
+ << setClipRectangles -> y_origin << ", size "
+ << setClipRectangles -> size_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void SetClipRectanglesStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 1, 1);
+ md5_append(md5_state_, buffer + 8, 4);
+}
+
+void SetClipRectanglesStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message;
+ SetClipRectanglesMessage *cachedSetClipRectangles = (SetClipRectanglesMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << setClipRectangles -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeXidValue(setClipRectangles -> gcontext, clientCache -> gcCache);
+
+ cachedSetClipRectangles -> gcontext = setClipRectangles -> gcontext;
+}
+
+void SetClipRectanglesStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SetClipRectanglesMessage *setClipRectangles = (SetClipRectanglesMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ decodeBuffer.decodeXidValue(value, clientCache -> gcCache);
+
+ setClipRectangles -> gcontext = value;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value " << setClipRectangles -> gcontext
+ << " as gcontext field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/SetClipRectangles.h b/nxcomp/src/SetClipRectangles.h
new file mode 100644
index 000000000..a2245360c
--- /dev/null
+++ b/nxcomp/src/SetClipRectangles.h
@@ -0,0 +1,187 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef SetClipRectangles_H
+#define SetClipRectangles_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SETCLIPRECTANGLES_ENABLE_CACHE 1
+#define SETCLIPRECTANGLES_ENABLE_DATA 0
+#define SETCLIPRECTANGLES_ENABLE_SPLIT 0
+#define SETCLIPRECTANGLES_ENABLE_COMPRESS 0
+
+#define SETCLIPRECTANGLES_DATA_LIMIT 2048
+#define SETCLIPRECTANGLES_DATA_OFFSET 12
+
+#define SETCLIPRECTANGLES_CACHE_SLOTS 3000
+#define SETCLIPRECTANGLES_CACHE_THRESHOLD 3
+#define SETCLIPRECTANGLES_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class SetClipRectanglesMessage : public Message
+{
+ friend class SetClipRectanglesStore;
+
+ public:
+
+ SetClipRectanglesMessage()
+ {
+ }
+
+ ~SetClipRectanglesMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char ordering;
+ unsigned int gcontext;
+ unsigned short x_origin;
+ unsigned short y_origin;
+};
+
+class SetClipRectanglesStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ SetClipRectanglesStore() : MessageStore()
+ {
+ enableCache = SETCLIPRECTANGLES_ENABLE_CACHE;
+ enableData = SETCLIPRECTANGLES_ENABLE_DATA;
+ enableSplit = SETCLIPRECTANGLES_ENABLE_SPLIT;
+ enableCompress = SETCLIPRECTANGLES_ENABLE_COMPRESS;
+
+ dataLimit = SETCLIPRECTANGLES_DATA_LIMIT;
+ dataOffset = SETCLIPRECTANGLES_DATA_OFFSET;
+
+ cacheSlots = SETCLIPRECTANGLES_CACHE_SLOTS;
+ cacheThreshold = SETCLIPRECTANGLES_CACHE_THRESHOLD;
+ cacheLowerThreshold = SETCLIPRECTANGLES_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~SetClipRectanglesStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "SetClipRectangles";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_SetClipRectangles;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SetClipRectanglesMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new SetClipRectanglesMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SetClipRectanglesMessage((const SetClipRectanglesMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SetClipRectanglesMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SetClipRectangles_H */
diff --git a/nxcomp/src/SetUnpackAlpha.cpp b/nxcomp/src/SetUnpackAlpha.cpp
new file mode 100644
index 000000000..5a352d26a
--- /dev/null
+++ b/nxcomp/src/SetUnpackAlpha.cpp
@@ -0,0 +1,266 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SetUnpackAlpha.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+SetUnpackAlphaStore::SetUnpackAlphaStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = SETUNPACKALPHA_ENABLE_CACHE;
+ enableData = SETUNPACKALPHA_ENABLE_DATA;
+ enableCompress = SETUNPACKALPHA_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = SETUNPACKALPHA_DATA_LIMIT;
+ dataOffset = SETUNPACKALPHA_DATA_OFFSET_IF_PROTO_STEP_7;
+
+ cacheSlots = SETUNPACKALPHA_CACHE_SLOTS;
+ cacheThreshold = SETUNPACKALPHA_CACHE_THRESHOLD;
+ cacheLowerThreshold = SETUNPACKALPHA_CACHE_LOWER_THRESHOLD;
+
+ // Since ProtoStep8 (#issue 108)
+ enableSplit = SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_8;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+SetUnpackAlphaStore::~SetUnpackAlphaStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SetUnpackAlphaStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ //
+ // Encode the source length first because
+ // we need it to determine the size of
+ // the output buffer.
+ //
+
+ // SrcLength.
+ encodeBuffer.encodeValue(GetULONG(buffer + 8, bigEndian), 32, 9);
+
+ // Client.
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> resourceCache);
+ // Method.
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> methodCache);
+ // DstLength.
+ encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackAlphaStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+ unsigned char cValue;
+
+ // SrcLength.
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ size = RoundUp4(value) + 16;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ // Client.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> resourceCache);
+
+ *(buffer + 1) = cValue;
+
+ // Method.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> methodCache);
+
+ *(buffer + 4) = cValue;
+
+ // DstLength.
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackAlphaStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message;
+
+ setUnpackAlpha -> client = *(buffer + 1);
+ setUnpackAlpha -> method = *(buffer + 4);
+
+ setUnpackAlpha -> src_length = GetULONG(buffer + 8, bigEndian);
+ setUnpackAlpha -> dst_length = GetULONG(buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackAlphaStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message;
+
+ *(buffer + 1) = setUnpackAlpha -> client;
+ *(buffer + 4) = setUnpackAlpha -> method;
+
+ PutULONG(setUnpackAlpha -> src_length, buffer + 8, bigEndian);
+ PutULONG(setUnpackAlpha -> dst_length, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SetUnpackAlphaStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message;
+
+ *logofs << name() << ": Identity client "
+ << (unsigned int) setUnpackAlpha -> client << " method "
+ << (unsigned int) setUnpackAlpha -> method << " source length "
+ << setUnpackAlpha -> src_length << " destination length "
+ << setUnpackAlpha -> dst_length << " size "
+ << setUnpackAlpha -> size_ << ".\n";
+
+ #endif
+}
+
+void SetUnpackAlphaStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Include the pack method and the source
+ // and destination length.
+ //
+
+ md5_append(md5_state_, buffer + 4, 1);
+ md5_append(md5_state_, buffer + 8, 8);
+}
+
+void SetUnpackAlphaStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message;
+ SetUnpackAlphaMessage *cachedSetUnpackAlpha = (SetUnpackAlphaMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(setUnpackAlpha -> client, 8,
+ clientCache -> resourceCache);
+
+ cachedSetUnpackAlpha -> client = setUnpackAlpha -> client;
+}
+
+void SetUnpackAlphaStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SetUnpackAlphaMessage *setUnpackAlpha = (SetUnpackAlphaMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(setUnpackAlpha -> client, 8,
+ clientCache -> resourceCache);
+}
diff --git a/nxcomp/src/SetUnpackAlpha.h b/nxcomp/src/SetUnpackAlpha.h
new file mode 100644
index 000000000..54714efaa
--- /dev/null
+++ b/nxcomp/src/SetUnpackAlpha.h
@@ -0,0 +1,162 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef SetUnpackAlpha_H
+#define SetUnpackAlpha_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SETUNPACKALPHA_ENABLE_CACHE 1
+#define SETUNPACKALPHA_ENABLE_DATA 1
+
+#define SETUNPACKALPHA_DATA_LIMIT 16384
+
+#define SETUNPACKALPHA_CACHE_SLOTS 2000
+#define SETUNPACKALPHA_CACHE_THRESHOLD 10
+#define SETUNPACKALPHA_CACHE_LOWER_THRESHOLD 5
+
+#define SETUNPACKALPHA_DATA_OFFSET_IF_PROTO_STEP_7 16
+#define SETUNPACKALPHA_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+#define SETUNPACKALPHA_ENABLE_SPLIT_IF_PROTO_STEP_8 0
+
+//
+// The message class.
+//
+
+class SetUnpackAlphaMessage : public Message
+{
+ friend class SetUnpackAlphaStore;
+
+ public:
+
+ SetUnpackAlphaMessage()
+ {
+ }
+
+ ~SetUnpackAlphaMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char client;
+ unsigned char method;
+
+ unsigned int src_length;
+ unsigned int dst_length;
+};
+
+class SetUnpackAlphaStore : public MessageStore
+{
+ public:
+
+ SetUnpackAlphaStore(StaticCompressor *compressor);
+
+ virtual ~SetUnpackAlphaStore();
+
+ virtual const char *name() const
+ {
+ return "SetUnpackAlpha";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXSetUnpackAlpha;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SetUnpackAlphaMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new SetUnpackAlphaMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SetUnpackAlphaMessage((const SetUnpackAlphaMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SetUnpackAlphaMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SetUnpackAlpha_H */
diff --git a/nxcomp/src/SetUnpackColormap.cpp b/nxcomp/src/SetUnpackColormap.cpp
new file mode 100644
index 000000000..2c9bba1bd
--- /dev/null
+++ b/nxcomp/src/SetUnpackColormap.cpp
@@ -0,0 +1,266 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SetUnpackColormap.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+SetUnpackColormapStore::SetUnpackColormapStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = SETUNPACKCOLORMAP_ENABLE_CACHE;
+ enableData = SETUNPACKCOLORMAP_ENABLE_DATA;
+ enableCompress = SETUNPACKCOLORMAP_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = SETUNPACKCOLORMAP_DATA_LIMIT;
+ dataOffset = SETUNPACKCOLORMAP_DATA_OFFSET_IF_PROTO_STEP_7;
+
+ cacheSlots = SETUNPACKCOLORMAP_CACHE_SLOTS;
+ cacheThreshold = SETUNPACKCOLORMAP_CACHE_THRESHOLD;
+ cacheLowerThreshold = SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD;
+
+ // Since ProtoStep8 (#issue 108)
+ enableSplit = SETUNPACKCOLORMAP_ENABLE_SPLIT_IF_PROTO_STEP_8;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+SetUnpackColormapStore::~SetUnpackColormapStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SetUnpackColormapStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ //
+ // Encode the source length first because
+ // we need it to determine the size of
+ // the output buffer.
+ //
+
+ // SrcLength.
+ encodeBuffer.encodeValue(GetULONG(buffer + 8, bigEndian), 32, 9);
+
+ // Client.
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> resourceCache);
+ // Method.
+ encodeBuffer.encodeCachedValue(*(buffer + 4), 8,
+ clientCache -> methodCache);
+ // DstLength.
+ encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32, 9);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackColormapStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ unsigned int value;
+ unsigned char cValue;
+
+ // SrcLength.
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ size = RoundUp4(value) + 16;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ PutULONG(value, buffer + 8, bigEndian);
+
+ // Client.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> resourceCache);
+
+ *(buffer + 1) = cValue;
+
+ // Method.
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> methodCache);
+
+ *(buffer + 4) = cValue;
+
+ // DstLength.
+ decodeBuffer.decodeValue(value, 32, 9);
+
+ PutULONG(value, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackColormapStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message;
+
+ setUnpackColormap -> client = *(buffer + 1);
+ setUnpackColormap -> method = *(buffer + 4);
+
+ setUnpackColormap -> src_length = GetULONG(buffer + 8, bigEndian);
+ setUnpackColormap -> dst_length = GetULONG(buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackColormapStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message;
+
+ *(buffer + 1) = setUnpackColormap -> client;
+ *(buffer + 4) = setUnpackColormap -> method;
+
+ PutULONG(setUnpackColormap -> src_length, buffer + 8, bigEndian);
+ PutULONG(setUnpackColormap -> dst_length, buffer + 12, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SetUnpackColormapStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message;
+
+ *logofs << name() << ": Identity client "
+ << (unsigned int) setUnpackColormap -> client << " method "
+ << (unsigned int) setUnpackColormap -> method << " source length "
+ << setUnpackColormap -> src_length << " destination length "
+ << setUnpackColormap -> dst_length << " size "
+ << setUnpackColormap -> size_ << ".\n";
+
+ #endif
+}
+
+void SetUnpackColormapStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Include the pack method and the source
+ // and destination length.
+ //
+
+ md5_append(md5_state_, buffer + 4, 1);
+ md5_append(md5_state_, buffer + 8, 8);
+}
+
+void SetUnpackColormapStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message;
+ SetUnpackColormapMessage *cachedSetUnpackColormap = (SetUnpackColormapMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ encodeBuffer.encodeCachedValue(setUnpackColormap -> client, 8,
+ clientCache -> resourceCache);
+
+ cachedSetUnpackColormap -> client = setUnpackColormap -> client;
+}
+
+void SetUnpackColormapStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SetUnpackColormapMessage *setUnpackColormap = (SetUnpackColormapMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(setUnpackColormap -> client, 8,
+ clientCache -> resourceCache);
+}
diff --git a/nxcomp/src/SetUnpackColormap.h b/nxcomp/src/SetUnpackColormap.h
new file mode 100644
index 000000000..779366531
--- /dev/null
+++ b/nxcomp/src/SetUnpackColormap.h
@@ -0,0 +1,162 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef SetUnpackColormap_H
+#define SetUnpackColormap_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SETUNPACKCOLORMAP_ENABLE_CACHE 1
+#define SETUNPACKCOLORMAP_ENABLE_DATA 1
+
+#define SETUNPACKCOLORMAP_DATA_LIMIT 4096
+
+#define SETUNPACKCOLORMAP_CACHE_SLOTS 2000
+#define SETUNPACKCOLORMAP_CACHE_THRESHOLD 5
+#define SETUNPACKCOLORMAP_CACHE_LOWER_THRESHOLD 0
+
+#define SETUNPACKCOLORMAP_DATA_OFFSET_IF_PROTO_STEP_7 16
+#define SETUNPACKCOLORMAP_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+#define SETUNPACKCOLORMAP_ENABLE_SPLIT_IF_PROTO_STEP_8 0
+
+//
+// The message class.
+//
+
+class SetUnpackColormapMessage : public Message
+{
+ friend class SetUnpackColormapStore;
+
+ public:
+
+ SetUnpackColormapMessage()
+ {
+ }
+
+ ~SetUnpackColormapMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char client;
+ unsigned char method;
+
+ unsigned int src_length;
+ unsigned int dst_length;
+};
+
+class SetUnpackColormapStore : public MessageStore
+{
+ public:
+
+ SetUnpackColormapStore(StaticCompressor *compressor);
+
+ virtual ~SetUnpackColormapStore();
+
+ virtual const char *name() const
+ {
+ return "SetUnpackColormap";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXSetUnpackColormap;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SetUnpackColormapMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new SetUnpackColormapMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SetUnpackColormapMessage((const SetUnpackColormapMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SetUnpackColormapMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SetUnpackColormap_H */
diff --git a/nxcomp/src/SetUnpackGeometry.cpp b/nxcomp/src/SetUnpackGeometry.cpp
new file mode 100644
index 000000000..edff6a544
--- /dev/null
+++ b/nxcomp/src/SetUnpackGeometry.cpp
@@ -0,0 +1,305 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "SetUnpackGeometry.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+SetUnpackGeometryStore::SetUnpackGeometryStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = SETUNPACKGEOMETRY_ENABLE_CACHE;
+ enableData = SETUNPACKGEOMETRY_ENABLE_DATA;
+ enableSplit = SETUNPACKGEOMETRY_ENABLE_SPLIT;
+ enableCompress = SETUNPACKGEOMETRY_ENABLE_COMPRESS;
+
+ dataLimit = SETUNPACKGEOMETRY_DATA_LIMIT;
+ dataOffset = SETUNPACKGEOMETRY_DATA_OFFSET;
+
+ cacheSlots = SETUNPACKGEOMETRY_CACHE_SLOTS;
+ cacheThreshold = SETUNPACKGEOMETRY_CACHE_THRESHOLD;
+ cacheLowerThreshold = SETUNPACKGEOMETRY_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+SetUnpackGeometryStore::~SetUnpackGeometryStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int SetUnpackGeometryStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> resourceCache);
+
+ const unsigned char *nextChar = buffer + 4;
+
+ for (int i = 0; i < 6; i++)
+ {
+ encodeBuffer.encodeCachedValue(*nextChar++, 8,
+ clientCache -> depthCache);
+ }
+
+ encodeBuffer.encodeValue(GetULONG(buffer + 12, bigEndian), 32);
+ encodeBuffer.encodeValue(GetULONG(buffer + 16, bigEndian), 32);
+ encodeBuffer.encodeValue(GetULONG(buffer + 20, bigEndian), 32);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackGeometryStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ size = 24;
+ buffer = writeBuffer -> addMessage(size);
+
+ unsigned char cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> resourceCache);
+ *(buffer + 1) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 4) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 5) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 6) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 7) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 8) = cValue;
+
+ decodeBuffer.decodeCachedValue(cValue, 8,
+ clientCache -> depthCache);
+ *(buffer + 9) = cValue;
+
+ unsigned int value;
+
+ decodeBuffer.decodeValue(value, 32);
+ PutULONG(value, buffer + 12, bigEndian);
+
+ decodeBuffer.decodeValue(value, 32);
+ PutULONG(value, buffer + 16, bigEndian);
+
+ decodeBuffer.decodeValue(value, 32);
+ PutULONG(value, buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackGeometryStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message;
+
+ setUnpackGeometry -> client = *(buffer + 1);
+
+ setUnpackGeometry -> depth_1_bpp = *(buffer + 4);
+ setUnpackGeometry -> depth_4_bpp = *(buffer + 5);
+ setUnpackGeometry -> depth_8_bpp = *(buffer + 6);
+ setUnpackGeometry -> depth_16_bpp = *(buffer + 7);
+ setUnpackGeometry -> depth_24_bpp = *(buffer + 8);
+ setUnpackGeometry -> depth_32_bpp = *(buffer + 9);
+
+ setUnpackGeometry -> red_mask = GetULONG(buffer + 12, bigEndian);
+ setUnpackGeometry -> green_mask = GetULONG(buffer + 16, bigEndian);
+ setUnpackGeometry -> blue_mask = GetULONG(buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SetUnpackGeometryStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message;
+
+ *(buffer + 1) = setUnpackGeometry -> client;
+
+ *(buffer + 4) = setUnpackGeometry -> depth_1_bpp;
+ *(buffer + 5) = setUnpackGeometry -> depth_4_bpp;
+ *(buffer + 6) = setUnpackGeometry -> depth_8_bpp;
+ *(buffer + 7) = setUnpackGeometry -> depth_16_bpp;
+ *(buffer + 8) = setUnpackGeometry -> depth_24_bpp;
+ *(buffer + 9) = setUnpackGeometry -> depth_32_bpp;
+
+ PutULONG(setUnpackGeometry -> red_mask, buffer + 12, bigEndian);
+ PutULONG(setUnpackGeometry -> green_mask, buffer + 16, bigEndian);
+ PutULONG(setUnpackGeometry -> blue_mask, buffer + 20, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << message << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void SetUnpackGeometryStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message;
+
+ *logofs << name() << ": Identity client "
+ << (unsigned) setUnpackGeometry -> client << " depth_1_bpp "
+ << (unsigned) setUnpackGeometry -> depth_1_bpp << " depth_4_bpp "
+ << (unsigned int) setUnpackGeometry -> depth_4_bpp << " depth_8_bpp "
+ << (unsigned int) setUnpackGeometry -> depth_8_bpp << " depth_16_bpp "
+ << (unsigned int) setUnpackGeometry -> depth_16_bpp << " depth_24_bpp "
+ << (unsigned int) setUnpackGeometry -> depth_24_bpp << " depth_32_bpp "
+ << (unsigned int) setUnpackGeometry -> depth_32_bpp
+
+ << " red_mask " << setUnpackGeometry -> red_mask
+ << " green_mask " << setUnpackGeometry -> green_mask
+ << " blue_mask " << setUnpackGeometry -> blue_mask
+
+ << " size " << setUnpackGeometry -> size_ << ".\n";
+
+ #endif
+}
+
+void SetUnpackGeometryStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 4, 6);
+ md5_append(md5_state_, buffer + 12, 12);
+}
+
+void SetUnpackGeometryStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message;
+ SetUnpackGeometryMessage *cachedSetUnpackGeometry = (SetUnpackGeometryMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef TEST
+ *logofs << name() << ": Encoding value "
+ << (unsigned int) setUnpackGeometry -> client
+ << " as client field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue(setUnpackGeometry -> client, 8,
+ clientCache -> resourceCache);
+
+ cachedSetUnpackGeometry -> client = setUnpackGeometry -> client;
+}
+
+void SetUnpackGeometryStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ SetUnpackGeometryMessage *setUnpackGeometry = (SetUnpackGeometryMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ decodeBuffer.decodeCachedValue(setUnpackGeometry -> client, 8,
+ clientCache -> resourceCache);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded value "
+ << (unsigned int) setUnpackGeometry -> client
+ << " as client field.\n" << logofs_flush;
+ #endif
+}
diff --git a/nxcomp/src/SetUnpackGeometry.h b/nxcomp/src/SetUnpackGeometry.h
new file mode 100644
index 000000000..96104f57f
--- /dev/null
+++ b/nxcomp/src/SetUnpackGeometry.h
@@ -0,0 +1,167 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef SetUnpackGeometry_H
+#define SetUnpackGeometry_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SETUNPACKGEOMETRY_ENABLE_CACHE 1
+#define SETUNPACKGEOMETRY_ENABLE_DATA 0
+#define SETUNPACKGEOMETRY_ENABLE_SPLIT 0
+#define SETUNPACKGEOMETRY_ENABLE_COMPRESS 0
+
+#define SETUNPACKGEOMETRY_DATA_LIMIT 24
+#define SETUNPACKGEOMETRY_DATA_OFFSET 24
+
+#define SETUNPACKGEOMETRY_CACHE_SLOTS 20
+#define SETUNPACKGEOMETRY_CACHE_THRESHOLD 1
+#define SETUNPACKGEOMETRY_CACHE_LOWER_THRESHOLD 0
+
+//
+// The message class.
+//
+
+class SetUnpackGeometryMessage : public Message
+{
+ friend class SetUnpackGeometryStore;
+
+ public:
+
+ SetUnpackGeometryMessage()
+ {
+ }
+
+ ~SetUnpackGeometryMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned char client;
+
+ unsigned char depth_1_bpp;
+ unsigned char depth_4_bpp;
+ unsigned char depth_8_bpp;
+ unsigned char depth_16_bpp;
+ unsigned char depth_24_bpp;
+ unsigned char depth_32_bpp;
+
+ unsigned int red_mask;
+ unsigned int green_mask;
+ unsigned int blue_mask;
+};
+
+class SetUnpackGeometryStore : public MessageStore
+{
+ public:
+
+ SetUnpackGeometryStore(StaticCompressor *compressor);
+
+ virtual ~SetUnpackGeometryStore();
+
+ virtual const char *name() const
+ {
+ return "SetUnpackGeometry";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_NXSetUnpackGeometry;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(SetUnpackGeometryMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ protected:
+
+ virtual Message *create() const
+ {
+ return new SetUnpackGeometryMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new SetUnpackGeometryMessage((const SetUnpackGeometryMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (SetUnpackGeometryMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* SetUnpackGeometry_H */
diff --git a/nxcomp/src/ShapeExtension.cpp b/nxcomp/src/ShapeExtension.cpp
new file mode 100644
index 000000000..2ee2b67db
--- /dev/null
+++ b/nxcomp/src/ShapeExtension.cpp
@@ -0,0 +1,305 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ShapeExtension.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "WriteBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Constructors and destructors.
+//
+
+ShapeExtensionStore::ShapeExtensionStore(StaticCompressor *compressor)
+
+ : MessageStore(compressor)
+{
+ enableCache = SHAPEEXTENSION_ENABLE_CACHE;
+ enableData = SHAPEEXTENSION_ENABLE_DATA;
+ enableSplit = SHAPEEXTENSION_ENABLE_SPLIT;
+
+ // Since ProtoStep7 (#issue 108)
+ enableCompress = SHAPEEXTENSION_ENABLE_COMPRESS_IF_PROTO_STEP_7;
+
+ dataLimit = SHAPEEXTENSION_DATA_LIMIT;
+ dataOffset = SHAPEEXTENSION_DATA_OFFSET;
+
+ cacheSlots = SHAPEEXTENSION_CACHE_SLOTS;
+ cacheThreshold = SHAPEEXTENSION_CACHE_THRESHOLD;
+ cacheLowerThreshold = SHAPEEXTENSION_CACHE_LOWER_THRESHOLD;
+
+ opcode_ = X_NXInternalShapeExtension;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+}
+
+ShapeExtensionStore::~ShapeExtensionStore()
+{
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+}
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int ShapeExtensionStore::encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const
+{
+ //
+ // Handle this extension in a way similar to shape.
+ //
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoding full message identity.\n" << logofs_flush;
+ #endif
+
+ //
+ // We handle all possible requests of this extension
+ // using the same opcode. We give to message a data
+ // offset of 4 (or 16 if proto is >= 3) and handle
+ // the first 16 bytes through an array of caches.
+ //
+
+ encodeBuffer.encodeValue(size >> 2, 16, 10);
+
+ encodeBuffer.encodeCachedValue(*(buffer + 1), 8,
+ clientCache -> shapeOpcodeCache);
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ encodeBuffer.encodeCachedValue(GetUINT(buffer + (i * 2) + 4, bigEndian), 16,
+ *clientCache -> shapeDataCache[i]);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Encoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ShapeExtensionStore::decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const
+{
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoding full message identity.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeValue(size, 16, 10);
+
+ size <<= 2;
+
+ buffer = writeBuffer -> addMessage(size);
+
+ decodeBuffer.decodeCachedValue(*(buffer + 1), 8,
+ clientCache -> shapeOpcodeCache);
+
+ unsigned int value;
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> shapeDataCache[i]);
+
+ PutUINT(value, buffer + 4 + (i * 2), bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Decoded full message identity.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ShapeExtensionStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message;
+
+ shapeExtension -> opcode = *(buffer + 1);
+
+ for (unsigned int i = 0; i < 8; i++)
+ {
+ if ((i * 2 + 4) < size)
+ {
+ shapeExtension -> data[i] = GetUINT(buffer + i * 2 + 4, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed data[" << i << "].\n"
+ << logofs_flush;
+ #endif
+ }
+ else
+ {
+ shapeExtension -> data[i] = 0;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int ShapeExtensionStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message;
+
+ *(buffer + 1) = shapeExtension -> opcode;
+
+ for (unsigned int i = 0; i < 8 && (i * 2 + 4) < size; i++)
+ {
+ PutUINT(shapeExtension -> data[i], buffer + i * 2 + 4, bigEndian);
+ }
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at "
+ << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void ShapeExtensionStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message;
+
+ *logofs << name() << ": Identity opcode " << (unsigned) shapeExtension -> opcode;
+
+ for (int i = 0; i < 8; i++)
+ {
+ *logofs << ", data[" << i << "] " << shapeExtension -> data[i];
+ }
+
+ *logofs << ", size " << shapeExtension -> size_ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void ShapeExtensionStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ //
+ // Include minor opcode in the checksum. As data
+ // offset can be beyond the real end of message,
+ // we need to include size or we will match any
+ // message of size less or equal to data offset.
+ //
+
+ md5_append(md5_state_, buffer + 1, 3);
+}
+
+void ShapeExtensionStore::updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const
+{
+ //
+ // Encode the variant part.
+ //
+
+ ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message;
+ ShapeExtensionMessage *cachedShapeExtension = (ShapeExtensionMessage *) cachedMessage;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ for (int i = 0; i < 8 && (i * 2 + 4) < shapeExtension -> size_; i++)
+ {
+ #ifdef TEST
+ *logofs << name() << ": Encoding value " << shapeExtension -> data[i]
+ << " as data[" << i << "] field.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeCachedValue((unsigned int) shapeExtension -> data[i], 16,
+ *clientCache -> shapeDataCache[i]);
+
+ cachedShapeExtension -> data[i] = shapeExtension -> data[i];
+ }
+}
+
+void ShapeExtensionStore::updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const
+{
+ ShapeExtensionMessage *shapeExtension = (ShapeExtensionMessage *) message;
+
+ ClientCache *clientCache = (ClientCache *) channelCache;
+
+ unsigned int value;
+
+ for (int i = 0; i < 8 && (i * 2 + 4) < shapeExtension -> size_; i++)
+ {
+ decodeBuffer.decodeCachedValue(value, 16,
+ *clientCache -> shapeDataCache[i]);
+
+ shapeExtension -> data[i] = (unsigned short) value;
+
+ #ifdef TEST
+ *logofs << name() << ": Decoded value " << shapeExtension -> data[i]
+ << " as data[" << i << "] field.\n" << logofs_flush;
+ #endif
+ }
+}
diff --git a/nxcomp/src/ShapeExtension.h b/nxcomp/src/ShapeExtension.h
new file mode 100644
index 000000000..4dd636847
--- /dev/null
+++ b/nxcomp/src/ShapeExtension.h
@@ -0,0 +1,164 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef ShapeExtension_H
+#define ShapeExtension_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define SHAPEEXTENSION_ENABLE_CACHE 1
+#define SHAPEEXTENSION_ENABLE_DATA 1
+#define SHAPEEXTENSION_ENABLE_SPLIT 0
+
+#define SHAPEEXTENSION_DATA_LIMIT 3200
+#define SHAPEEXTENSION_DATA_OFFSET 20
+
+#define SHAPEEXTENSION_CACHE_SLOTS 3000
+#define SHAPEEXTENSION_CACHE_THRESHOLD 10
+#define SHAPEEXTENSION_CACHE_LOWER_THRESHOLD 5
+
+#define SHAPEEXTENSION_ENABLE_COMPRESS_IF_PROTO_STEP_7 0
+
+//
+// The message class.
+//
+
+class ShapeExtensionMessage : public Message
+{
+ friend class ShapeExtensionStore;
+
+ public:
+
+ ShapeExtensionMessage()
+ {
+ }
+
+ ~ShapeExtensionMessage()
+ {
+ }
+
+ //
+ // Note for encoding in protocol level 1: we consider
+ // for this message a data offset of 4. Bytes from 5
+ // to 20, if present, are taken as part of identity
+ // and encoded through an array of int caches.
+ //
+
+ private:
+
+ unsigned char opcode;
+ unsigned short data[8];
+};
+
+class ShapeExtensionStore : public MessageStore
+{
+ public:
+
+ ShapeExtensionStore(StaticCompressor *compressor);
+
+ virtual ~ShapeExtensionStore();
+
+ virtual const char *name() const
+ {
+ return "ShapeExtension";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return opcode_;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(ShapeExtensionMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new ShapeExtensionMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new ShapeExtensionMessage((const ShapeExtensionMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (ShapeExtensionMessage *) message;
+ }
+
+ virtual int encodeIdentity(EncodeBuffer &encodeBuffer, const unsigned char *buffer,
+ const unsigned int size, int bigEndian,
+ ChannelCache *channelCache) const;
+
+ virtual int decodeIdentity(DecodeBuffer &decodeBuffer, unsigned char *&buffer,
+ unsigned int &size, int bigEndian, WriteBuffer *writeBuffer,
+ ChannelCache *channelCache) const;
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void updateIdentity(EncodeBuffer &encodeBuffer, const Message *message,
+ const Message *cachedMessage,
+ ChannelCache *channelCache) const;
+
+ virtual void updateIdentity(DecodeBuffer &decodeBuffer, const Message *message,
+ ChannelCache *channelCache) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+
+ private:
+
+ unsigned char opcode_;
+};
+
+#endif /* ShapeExtension_H */
diff --git a/nxcomp/src/Socket.cpp b/nxcomp/src/Socket.cpp
new file mode 100644
index 000000000..8be04d76d
--- /dev/null
+++ b/nxcomp/src/Socket.cpp
@@ -0,0 +1,757 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#if defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun)
+#include <netinet/in_systm.h>
+#endif
+
+#ifdef __sun
+#include <unistd.h>
+#include <sys/termios.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <fcntl.h>
+
+//
+// System specific defines.
+//
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__sun)
+#define SOL_IP IPPROTO_IP
+#endif
+
+#ifdef __sun
+#define INADDR_NONE ((unsigned int) -1)
+#endif
+
+//
+// The TIOCOUTQ ioctl is not implemented on Cygwin.
+// Note also that TIOCOUTQ and IPTOS_LOWDELAY are
+// disabled when running on MacOS/X.
+//
+
+#ifdef __CYGWIN32__
+#define TIOCOUTQ ((unsigned int) -1)
+#endif
+
+//
+// NX includes.
+//
+
+#include "Misc.h"
+#include "Socket.h"
+
+//
+// Set verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Set this only once by querying OS details.
+//
+
+static int _kernelStep = -1;
+
+int GetKernelStep()
+{
+ if (_kernelStep < 0)
+ {
+ //
+ // At the moment only NX clients run on Win32
+ // and MacOS/X so we are not really interested
+ // in the relevant OS dependent functions.
+ //
+
+ #if defined(__CYGWIN32__) || defined(__APPLE__)
+
+ _kernelStep = 0;
+
+ #else
+
+ struct utsname buffer;
+
+ if (uname(&buffer) < 0)
+ {
+ #ifdef WARNING
+ *logofs << "Socket: WARNING! Failed to get system info. Error is "
+ << EGET() << " '" << ESTR() << "'.\n" << logofs_flush;
+
+ *logofs << "Socket: WARNING! Assuming lowest system support.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to get system info. Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ cerr << "Warning" << ": Assuming lowest system support.\n";
+
+ _kernelStep = 0;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Socket: System is '" << buffer.sysname
+ << "' nodename '" << buffer.nodename << "' release '"
+ << buffer.release << "'.\n" << logofs_flush;
+
+ *logofs << "Socket: Version is '" << buffer.version << "' machine '"
+ << buffer.machine << "'.\n" << logofs_flush;
+ #endif
+
+ //
+ // Should test support on other operating systems.
+ //
+
+ if (strcmp(buffer.sysname, "Linux") == 0)
+ {
+ if (strncmp(buffer.release, "2.0.", 4) == 0 ||
+ strncmp(buffer.release, "2.2.", 4) == 0)
+ {
+ #ifdef TEST
+ *logofs << "Socket: Assuming level 2 system support.\n"
+ << logofs_flush;
+ #endif
+
+ _kernelStep = 2;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Socket: Assuming level 3 system support.\n"
+ << logofs_flush;
+ #endif
+
+ _kernelStep = 3;
+ }
+ }
+ else if (strcmp(buffer.sysname, "SunOS") == 0)
+ {
+ #ifdef TEST
+ *logofs << "Socket: Assuming level 1 system support.\n"
+ << logofs_flush;
+ #endif
+
+ _kernelStep = 1;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Socket: Assuming level 0 system support.\n"
+ << logofs_flush;
+ #endif
+
+ _kernelStep = 0;
+ }
+ }
+
+ #endif /* #if defined(__CYGWIN32__) || defined(__APPLE__) */
+ }
+
+ return _kernelStep;
+}
+
+int SetReuseAddress(int fd)
+{
+ int flag = 1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &flag, sizeof(flag)) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set SO_REUSEADDR flag on FD#"
+ << fd << ". Error is " << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set SO_REUSEADDR flag on FD#"
+ << fd << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set SO_REUSEADDR flag on FD#"
+ << fd << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetNonBlocking(int fd, int value)
+{
+ int flags = fcntl(fd, F_GETFL);
+
+ if (flags >= 0)
+ {
+ if (value == 0)
+ {
+ flags &= ~O_NONBLOCK;
+ }
+ else
+ {
+ flags |= O_NONBLOCK;
+ }
+ }
+
+ if (flags < 0 || fcntl(fd, F_SETFL, flags) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set O_NONBLOCK flag on FD#"
+ << fd << " to " << value << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set O_NONBLOCK flag on FD#"
+ << fd << " to " << value << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set O_NONBLOCK flag on FD#"
+ << fd << " to " << value << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetLingerTimeout(int fd, int timeout)
+{
+ struct linger linger_value;
+
+ if (timeout > 0)
+ {
+ linger_value.l_onoff = 1;
+ linger_value.l_linger = timeout;
+ }
+ else
+ {
+ linger_value.l_onoff = 0;
+ linger_value.l_linger = 0;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_value, sizeof(linger_value)) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set SO_LINGER values to "
+ << linger_value.l_onoff << " and " << linger_value.l_linger
+ << " on FD#" << fd << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set SO_LINGER values to "
+ << linger_value.l_onoff << " and " << linger_value.l_linger
+ << " on FD#" << fd << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set SO_LINGER values to "
+ << linger_value.l_onoff << " and " << linger_value.l_linger
+ << " on FD#" << fd << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetSendBuffer(int fd, int size)
+{
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set SO_SNDBUF size to "
+ << size << " on FD#" << fd << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set SO_SNDBUF size to "
+ << size << " on FD#" << fd << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set SO_SNDBUF on FD#" << fd
+ << " to " << size << " bytes.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetReceiveBuffer(int fd, int size)
+{
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set SO_RCVBUF size to "
+ << size << " on FD#" << fd << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set SO_RCVBUF size to "
+ << size << " on FD#" << fd << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set SO_RCVBUF on FD#" << fd
+ << " to " << size << " bytes.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetNoDelay(int fd, int value)
+{
+ int result = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));
+
+ if (result == 0)
+ {
+ result = 1;
+ }
+ else if (result < 0)
+ {
+ //
+ // Is it become a different error on
+ // Mac OSX 10.4?
+ //
+
+ #if defined(__APPLE__)
+
+ result = 0;
+
+ #endif
+
+ #if defined(__sun)
+
+ if (EGET() == ENOPROTOOPT)
+ {
+ result = 0;
+ }
+
+ #endif
+
+ #if !defined(__APPLE__) && !defined(__sun)
+
+ if (EGET() == EOPNOTSUPP)
+ {
+ result = 0;
+ }
+
+ #endif
+ }
+
+ if (result < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set TCP_NODELAY flag on "
+ << "FD#" << fd << " to " << value << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set TCP_NODELAY flag on "
+ << "FD#" << fd << " to " << value << ". Error is "
+ << EGET() << " '" << ESTR() << "'.\n";
+ }
+ #ifdef TEST
+ else if (result == 0)
+ {
+ #ifdef TEST
+ *logofs << "Socket: Option TCP_NODELAY not supported "
+ << "on FD#" << fd << ".\n" << logofs_flush;
+ #endif
+ }
+ else
+ {
+ *logofs << "Socket: Set TCP_NODELAY flag on FD#"
+ << fd << " to " << value << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ return result;
+}
+
+int SetKeepAlive(int fd)
+{
+ int flag = 1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to set SO_KEEPALIVE flag on "
+ << "FD#" << fd << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to set SO_KEEPALIVE flag on "
+ << "FD#" << fd << ". Error is " << EGET() << " '"
+ << ESTR() << "'.\n";
+
+ return -1;
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set SO_KEEPALIVE flag on FD#"
+ << fd << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+}
+
+int SetLowDelay(int fd)
+{
+ if (_kernelStep < 0)
+ {
+ GetKernelStep();
+ }
+
+ switch (_kernelStep)
+ {
+ case 3:
+ case 2:
+ case 1:
+ {
+ int flag = IPTOS_LOWDELAY;
+
+ if (setsockopt(fd, SOL_IP, IP_TOS, &flag, sizeof(flag)) < 0)
+ {
+ if (EGET() == EOPNOTSUPP)
+ {
+ #ifdef TEST
+ *logofs << "Socket: Option IPTOS_LOWDELAY not supported "
+ << "on FD#" << fd << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "Socket: WARNING! Failed to set IPTOS_LOWDELAY flag on "
+ << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to set IPTOS_LOWDELAY flag on "
+ << "FD#" << fd << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Socket: Set IPTOS_LOWDELAY flag on FD#"
+ << fd << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 1;
+ }
+ default:
+ {
+ #ifdef TEST
+ *logofs << "Socket: Option IPTOS_LOWDELAY not "
+ << "supported on FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ }
+}
+
+int SetCloseOnExec(int fd)
+{
+ if (fcntl(fd, F_SETFD, 1) != 0)
+ {
+ #ifdef TEST
+ *logofs << "NXClient: PANIC! Cannot set close-on-exec "
+ << "on FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot set close-on-exec on FD#"
+ << fd << ". Error is " << EGET() << " '" << ESTR()
+ << "'.\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int GetBytesReadable(int fd)
+{
+ long readable = 0;
+
+ //
+ // It may fail, for example at session
+ // shutdown.
+ //
+
+ if (ioctl(fd, FIONREAD, &readable) < 0)
+ {
+ #ifdef TEST
+ *logofs << "Socket: PANIC! Failed to get bytes readable "
+ << "from FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Socket: Returning " << (int) readable
+ << " bytes readable on FD#" << fd << ".\n"
+ << logofs_flush;
+ #endif
+
+ return (int) readable;
+}
+
+int GetBytesWritable(int fd)
+{
+ if (_kernelStep < 0)
+ {
+ GetKernelStep();
+ }
+
+ long writable;
+
+ switch (_kernelStep)
+ {
+ case 3:
+ {
+ //
+ // TODO: Should query the real size
+ // of the TCP write buffer.
+ //
+
+ writable = 16384 - GetBytesQueued(fd);
+
+ if (writable < 0)
+ {
+ writable = 0;
+ }
+
+ break;
+ }
+ case 2:
+ {
+ if (ioctl(fd, TIOCOUTQ, (void *) &writable) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to get bytes writable "
+ << "on FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to get bytes writable "
+ << "on FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ return -1;
+ }
+
+ break;
+ }
+ default:
+ {
+ #ifdef TEST
+ *logofs << "Socket: Option TIOCOUTQ not supported "
+ << "on FD#" << fd << ",\n" << logofs_flush;
+ #endif
+
+ //
+ // TODO: Should query the real size
+ // of the TCP write buffer.
+ //
+
+ writable = 16384;
+
+ break;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Socket: Returning " << writable
+ << " bytes writable on FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (int) writable;
+}
+
+int GetBytesQueued(int fd)
+{
+ //
+ // The TIOCOUTQ ioctl is not implemented on Cygwin
+ // and returns the space available on Linux Kernels
+ // 2.0 and 2.2 (like current MIPS for PS/2).
+ //
+
+ if (_kernelStep < 0)
+ {
+ GetKernelStep();
+ }
+
+ long queued;
+
+ switch (_kernelStep)
+ {
+ case 3:
+ {
+ if (ioctl(fd, TIOCOUTQ, (void *) &queued) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to get bytes queued "
+ << "on FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to get bytes queued "
+ << "on FD#" << fd << ". Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ return -1;
+ }
+
+ break;
+ }
+ case 2:
+ {
+ //
+ // TODO: Should query the real size
+ // of the TCP write buffer.
+ //
+
+ queued = 16384 - GetBytesWritable(fd);
+
+ if (queued < 0)
+ {
+ queued = 0;
+ }
+
+ break;
+ }
+ default:
+ {
+ #ifdef TEST
+ *logofs << "Socket: Option TIOCOUTQ not supported "
+ << "on FD#" << fd << ",\n" << logofs_flush;
+ #endif
+
+ queued = 0;
+
+ break;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "Socket: Returning " << queued
+ << " bytes queued on FD#" << fd
+ << ".\n" << logofs_flush;
+ #endif
+
+ return (int) queued;
+}
+
+int GetHostAddress(const char *name)
+{
+ hostent *host = gethostbyname(name);
+
+ if (host == NULL)
+ {
+ //
+ // On some Unices gethostbyname() doesn't
+ // accept IP addresses, so try inet_addr.
+ //
+
+ IN_ADDR_T address = inet_addr(name);
+
+ if (address == INADDR_NONE)
+ {
+ #ifdef PANIC
+ *logofs << "Socket: PANIC! Failed to resolve address of '"
+ << name << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed to resolve address of '"
+ << name << "'.\n";
+
+ return 0;
+ }
+
+ return (int) address;
+ }
+ else
+ {
+ return (*((int *) host -> h_addr_list[0]));
+ }
+}
diff --git a/nxcomp/src/Socket.h b/nxcomp/src/Socket.h
new file mode 100644
index 000000000..77837a115
--- /dev/null
+++ b/nxcomp/src/Socket.h
@@ -0,0 +1,106 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Socket_H
+#define Socket_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#ifdef __sun
+#include <stropts.h>
+#include <sys/filio.h>
+#endif
+
+#ifdef HAVE_IN_ADDR_T
+#define IN_ADDR_T in_addr_t
+#else
+#define IN_ADDR_T unsigned
+#endif
+
+//
+// Set socket options.
+//
+
+int SetReuseAddress(int fd);
+int SetNonBlocking(int fd, int value);
+int SetLingerTimeout(int fd, int timeout);
+int SetSendBuffer(int fd, int size);
+int SetReceiveBuffer(int fd, int size);
+int SetNoDelay(int fd, int value);
+int SetKeepAlive(int fd);
+int SetLowDelay(int fd);
+int SetCloseOnExec(int fd);
+
+//
+// Get kernel support level.
+//
+
+int GetKernelStep();
+
+//
+// Get socket info.
+//
+
+int GetBytesReadable(int fd);
+int GetBytesWritable(int fd);
+int GetBytesQueued(int fd);
+
+//
+// Inline version, providing direct access
+// to the interface.
+//
+
+#include "Misc.h"
+
+inline int GetBytesReadable(int fd, int *readable)
+{
+ long t;
+
+ int result = ioctl(fd, FIONREAD, &t);
+
+ #ifdef DEBUG
+ *logofs << "Socket: Bytes readable from FD#"
+ << fd << " are " << t << " with result "
+ << result << ".\n" << logofs_flush;
+ #endif
+
+ *readable = (int) t;
+
+ return result;
+}
+
+//
+// Query Internet address.
+//
+
+int GetHostAddress(const char *name);
+
+#endif /* Socket_H */
diff --git a/nxcomp/src/Split.cpp b/nxcomp/src/Split.cpp
new file mode 100644
index 000000000..e2fea97cc
--- /dev/null
+++ b/nxcomp/src/Split.cpp
@@ -0,0 +1,1839 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unistd.h>
+#include <cstring>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <utime.h>
+
+#include "Misc.h"
+
+#include "Split.h"
+
+#include "Control.h"
+#include "Statistics.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "StaticCompressor.h"
+
+#include "Unpack.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Define this to trace elements
+// allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// Counters used for store control.
+//
+
+int SplitStore::totalSplitSize_;
+int SplitStore::totalSplitStorageSize_;
+
+//
+// This is used for reference count.
+//
+
+#ifdef REFERENCES
+
+int Split::references_ = 0;
+
+#endif
+
+Split::Split()
+{
+ resource_ = nothing;
+ position_ = nothing;
+
+ store_ = NULL;
+
+ d_size_ = 0;
+ i_size_ = 0;
+ c_size_ = 0;
+ r_size_ = 0;
+
+ next_ = 0;
+ load_ = 0;
+ save_ = 0;
+
+ checksum_ = NULL;
+ state_ = split_undefined;
+ mode_ = split_none;
+ action_ = is_discarded;
+
+ #ifdef REFERENCES
+
+ references_++;
+
+ *logofs << "Split: Created new Split at "
+ << this << " out of " << references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+Split::~Split()
+{
+ delete [] checksum_;
+
+ #ifdef REFERENCES
+
+ references_--;
+
+ *logofs << "Split: Deleted Split at "
+ << this << " out of " << references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+SplitStore::SplitStore(StaticCompressor *compressor, CommitStore *commits, int resource)
+
+ : compressor_(compressor), commits_(commits), resource_(resource)
+{
+ splits_ = new T_splits();
+
+ current_ = splits_ -> end();
+
+ splitStorageSize_ = 0;
+
+ #ifdef TEST
+ *logofs << "SplitStore: Created new store [";
+
+ if (resource_ != nothing)
+ {
+ *logofs << resource_;
+ }
+ else
+ {
+ *logofs << "commit";
+ }
+
+ *logofs << "].\n" << logofs_flush;
+
+ *logofs << "SplitStore: Total messages in stores are "
+ << totalSplitSize_ << " with total storage size "
+ << totalSplitStorageSize_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+SplitStore::~SplitStore()
+{
+ totalSplitSize_ -= splits_ -> size();
+
+ totalSplitStorageSize_ -= splitStorageSize_;
+
+ for (T_splits::iterator i = splits_ -> begin();
+ i != splits_ -> end(); i++)
+ {
+ delete *i;
+ }
+
+ delete splits_;
+
+ #ifdef TEST
+ *logofs << "SplitStore: Deleted store [";
+
+ if (resource_ != nothing)
+ {
+ *logofs << resource_;
+ }
+ else
+ {
+ *logofs << "commit";
+ }
+
+ *logofs << "] with storage size " << splitStorageSize_
+ << ".\n" << logofs_flush;
+
+ *logofs << "SplitStore: Total messages in stores are "
+ << totalSplitSize_ << " with total storage size "
+ << totalSplitStorageSize_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+//
+// This is called at the encoding side.
+//
+
+Split *SplitStore::add(MessageStore *store, int resource, T_split_mode mode,
+ int position, T_store_action action, T_checksum checksum,
+ const unsigned char *buffer, const int size)
+{
+ #ifdef TEST
+ *logofs << "SplitStore: Adding message [" << (unsigned int) store ->
+ opcode() << "] resource " << resource << " mode " << mode
+ << " position " << position << " action [" << DumpAction(action)
+ << "] and checksum [" << DumpChecksum(checksum) << "]"
+ << ".\n" << logofs_flush;
+ #endif
+
+ Split *split = new Split();
+
+ if (split == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Can't allocate "
+ << "memory for the split.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the split.\n";
+
+ HandleAbort();
+ }
+
+ split -> store_ = store;
+ split -> resource_ = resource;
+ split -> mode_ = mode;
+ split -> position_ = position;
+ split -> action_ = action;
+
+ split -> store_ -> validateSize(size);
+
+ //
+ // The checksum is not provided if the
+ // message is cached.
+ //
+
+ if (checksum != NULL)
+ {
+ split -> checksum_ = new md5_byte_t[MD5_LENGTH];
+
+ memcpy(split -> checksum_, checksum, MD5_LENGTH);
+ }
+
+ //
+ // We don't need the identity data at the
+ // encoding side. This qualifies the split
+ // as a split generated at the encoding
+ // side.
+ //
+
+ split -> i_size_ = store -> identitySize(buffer, size);
+
+ split -> d_size_ = size - split -> i_size_;
+
+ if (action == IS_ADDED || action == is_discarded)
+ {
+ //
+ // If the message was added to message
+ // store or discarded we need to save
+ // the real data so we can transfer it
+ // at later time.
+ //
+
+ split -> data_.resize(split -> d_size_);
+
+ memcpy(split -> data_.begin(), buffer + split -> i_size_, split -> d_size_);
+
+ //
+ // If the message was added, lock it so
+ // it will not be used by the encoding
+ // side until it is recomposed.
+ //
+
+ if (action == IS_ADDED)
+ {
+ split -> store_ -> lock(split -> position_);
+
+ #ifdef TEST
+
+ commits_ -> validate(split);
+
+ #endif
+ }
+ }
+ #ifdef WARNING
+ else
+ {
+ *logofs << "SplitStore: WARNING! Not copying data for the cached message.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ push(split);
+
+ return split;
+}
+
+//
+// This is called at decoding side. If checksum
+// is provided, the message can be searched on
+// disk, then, if message is found, an event is
+// sent to abort the data transfer.
+//
+
+Split *SplitStore::add(MessageStore *store, int resource, int position,
+ T_store_action action, T_checksum checksum,
+ unsigned char *buffer, const int size)
+{
+ #ifdef TEST
+ *logofs << "SplitStore: Adding message ["
+ << (unsigned int) store -> opcode() << "] resource "
+ << resource << " position " << position << " action ["
+ << DumpAction(action) << "] and checksum ["
+ << DumpChecksum(checksum) << "].\n" << logofs_flush;
+ #endif
+
+ Split *split = new Split();
+
+ if (split == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Can't allocate "
+ << "memory for the split.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory "
+ << "for the split.\n";
+
+ HandleAbort();
+ }
+
+ split -> store_ = store;
+ split -> resource_ = resource;
+ split -> position_ = position;
+ split -> action_ = action;
+
+ split -> store_ -> validateSize(size);
+
+ //
+ // Check if the checksum was provided
+ // by the remote.
+ //
+
+ if (checksum != NULL)
+ {
+ split -> checksum_ = new md5_byte_t[MD5_LENGTH];
+
+ memcpy(split -> checksum_, checksum, MD5_LENGTH);
+ }
+
+ split -> i_size_ = store -> identitySize(buffer, size);
+
+ //
+ // Copy the identity so we can expand the
+ // message when it is committed.
+ //
+
+ split -> identity_.resize(split -> i_size_);
+
+ memcpy(split -> identity_.begin(), buffer, split -> i_size_);
+
+ split -> d_size_ = size - split -> i_size_;
+
+ if (action == IS_ADDED || action == is_discarded)
+ {
+ //
+ // The unpack procedure will check if the
+ // first 2 bytes of the buffer contain the
+ // pattern and will not try to expand the
+ // image.
+ //
+
+ split -> data_.resize(2);
+
+ unsigned char *data = split -> data_.begin();
+
+ data[0] = SPLIT_PATTERN;
+ data[1] = SPLIT_PATTERN;
+
+ //
+ // If the message was added to the store,
+ // we don't have the data part, yet, so
+ // we need to lock the message until it
+ // is recomposed.
+ //
+
+ if (action == IS_ADDED)
+ {
+ split -> store_ -> lock(split -> position_);
+
+ #ifdef TEST
+
+ commits_ -> validate(split);
+
+ #endif
+ }
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "SplitStore: WARNING! Copying data for the cached message.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // We may optionally take the data from the
+ // message store in compressed form, but,
+ // as the data has been decompressed in the
+ // buffer, we save a further decompression.
+ //
+
+ split -> data_.resize(split -> d_size_);
+
+ memcpy(split -> data_.begin(), buffer + split -> i_size_, split -> d_size_);
+ }
+
+ push(split);
+
+ return split;
+}
+
+void SplitStore::push(Split *split)
+{
+ splits_ -> push_back(split);
+
+ splitStorageSize_ += getNodeSize(split);
+
+ totalSplitSize_++;
+
+ totalSplitStorageSize_ += getNodeSize(split);
+
+ statistics -> addSplit();
+
+ #ifdef TEST
+ *logofs << "SplitStore: There are " << splits_ -> size()
+ << " messages in store [" << resource_ << "] with "
+ << "storage size " << splitStorageSize_ << ".\n"
+ << logofs_flush;
+
+ *logofs << "SplitStore: Total messages in stores are "
+ << totalSplitSize_ << " with total storage size "
+ << totalSplitStorageSize_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ split -> state_ = split_added;
+}
+
+void SplitStore::dump()
+{
+ #ifdef DUMP
+
+ int n;
+
+ Split *split;
+
+ *logofs << "SplitStore: DUMP! Dumping content of ";
+
+ if (commits_ == NULL)
+ {
+ *logofs << "[commits]";
+ }
+ else
+ {
+ *logofs << "[splits] for store [" << resource_ << "]";
+ }
+
+ *logofs << " with [" << getSize() << "] elements "
+ << "in the store.\n" << logofs_flush;
+
+ n = 0;
+
+ for (T_splits::iterator i = splits_ -> begin(); i != splits_ -> end(); i++, n++)
+ {
+ split = *i;
+
+ *logofs << "SplitStore: DUMP! Split [" << n << "] has action ["
+ << DumpAction(split -> action_) << "] state ["
+ << DumpState(split -> state_) << "] ";
+
+ if (split -> resource_ >= 0)
+ {
+ *logofs << "resource " << split -> resource_;
+ }
+
+ *logofs << " request " << (unsigned) split -> store_ -> opcode()
+ << " position " << split -> position_ << " size is "
+ << split -> data_.size() << " (" << split -> d_size_
+ << "/" << split -> c_size_ << "/" << split -> r_size_
+ << ") with " << split -> data_.size() - split -> next_
+ << "] bytes to go.\n" << logofs_flush;
+ }
+
+ #endif
+}
+
+int SplitStore::send(EncodeBuffer &encodeBuffer, int packetSize)
+{
+ if (splits_ -> size() == 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Function send called with no splits available.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Function send called with no splits available.\n";
+
+ HandleAbort();
+ }
+
+ //
+ // A start operation must always be executed on
+ // the split, even in the case the split will be
+ // later aborted.
+ //
+
+ if (current_ == splits_ -> end())
+ {
+ start(encodeBuffer);
+ }
+
+ //
+ // If we have matched the checksum received from
+ // the remote side then we must abort the current
+ // split, else we can send another block of data
+ // to the remote peer.
+ //
+
+ Split *split = *current_;
+
+ unsigned int abort = 0;
+
+ if (split -> state_ == split_loaded)
+ {
+ abort = 1;
+ }
+
+ encodeBuffer.encodeBoolValue(abort);
+
+ if (abort == 1)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Aborting split for checksum ["
+ << DumpChecksum(split -> checksum_) << "] position "
+ << split -> position_ << " with " << (split ->
+ data_.size() - split -> next_) << " bytes to go "
+ << "out of " << split -> data_.size()
+ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addSplitAborted();
+
+ statistics -> addSplitAbortedBytesOut(split -> data_.size() - split -> next_);
+
+ split -> next_ = split -> data_.size();
+
+ split -> state_ = split_aborted;
+ }
+ else
+ {
+ int count = (packetSize <= 0 || split -> next_ +
+ packetSize > (int) split -> data_.size() ?
+ split -> data_.size() - split -> next_ : packetSize);
+
+ #ifdef TEST
+ *logofs << "SplitStore: Sending split for checksum ["
+ << DumpChecksum(split -> checksum_) << "] count "
+ << count << " position " << split -> position_
+ << ". Data size is " << split -> data_.size() << " ("
+ << split -> d_size_ << "/" << split -> c_size_ << "), "
+ << split -> data_.size() - (split -> next_ + count)
+ << " to go.\n" << logofs_flush;
+ #endif
+
+ encodeBuffer.encodeValue(count, 32, 10);
+
+ encodeBuffer.encodeMemory(split -> data_.begin() + split -> next_, count);
+
+ split -> next_ += count;
+ }
+
+ //
+ // Was data completely transferred? We are the
+ // sending side. We must update the message in
+ // store, even if split was aborted.
+ //
+
+ if (split -> next_ != ((int) split -> data_.size()))
+ {
+ return 0;
+ }
+
+ //
+ // Move the split at the head of the
+ // list to the commits.
+ //
+
+ remove(split);
+
+ //
+ // Reset current position to the
+ // end of repository.
+ //
+
+ current_ = splits_ -> end();
+
+ #ifdef TEST
+ *logofs << "SplitStore: Removed split at head of the list. "
+ << "Resource is " << split -> resource_ << " request "
+ << (unsigned) split -> store_ -> opcode() << " position "
+ << split -> position_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int SplitStore::start(EncodeBuffer &encodeBuffer)
+{
+ //
+ // Get the element at the top of the
+ // list.
+ //
+
+ current_ = splits_ -> begin();
+
+ Split *split = *current_;
+
+ #ifdef TEST
+ *logofs << "SplitStore: Starting split for checksum ["
+ << DumpChecksum(split -> checksum_) << "] position "
+ << split -> position_ << " with " << (split ->
+ data_.size() - split -> next_) << " bytes to go "
+ << "out of " << split -> data_.size()
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // See if compression of the data part is
+ // enabled.
+ //
+
+ if (split -> store_ -> enableCompress)
+ {
+ //
+ // If the split is going to be aborted don't
+ // compress the data and go straight to the
+ // send. The new data size will be assumed
+ // from the disk cache.
+ //
+
+ if (split -> state_ != split_loaded)
+ {
+ unsigned int compressedSize = 0;
+ unsigned char *compressedData = NULL;
+
+ if (control -> LocalDataCompression &&
+ (compressor_ -> compressBuffer(split -> data_.begin(), split -> d_size_,
+ compressedData, compressedSize)))
+ {
+ //
+ // Replace the data with the one in
+ // compressed form.
+ //
+
+ #ifdef TEST
+ *logofs << "SplitStore: Split data of size " << split -> d_size_
+ << " has been compressed to " << compressedSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ split -> data_.clear();
+
+ split -> data_.resize(compressedSize);
+
+ memcpy(split -> data_.begin(), compressedData, compressedSize);
+
+ split -> c_size_ = compressedSize;
+
+ //
+ // Inform our peer that the data is
+ // compressed and send the new size.
+ //
+
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeValue(compressedSize, 32, 14);
+
+ #ifdef TEST
+ *logofs << "SplitStore: Signaled " << split -> c_size_
+ << " bytes of compressed data for this message.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "SplitStore: Not trying to compress the "
+ << "loaded message.\n" << logofs_flush;
+ }
+ #endif
+
+ //
+ // Tell to the remote that data will
+ // follow uncompressed.
+ //
+
+ encodeBuffer.encodeBoolValue(0);
+ }
+
+ return 1;
+}
+
+int SplitStore::start(DecodeBuffer &decodeBuffer)
+{
+ #ifdef TEST
+ *logofs << "SplitStore: Going to receive a new split from the remote side.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Get the element at the head
+ // of the list.
+ //
+
+ current_ = splits_ -> begin();
+
+ Split *split = *current_;
+
+ unsigned int compressedSize = 0;
+
+ //
+ // Save the data size known by the remote.
+ // This information will be needed if the
+ // remote will not have a chance to abort
+ // the split.
+ //
+
+ split -> r_size_ = split -> d_size_;
+
+ //
+ // Find out if data was compressed by the
+ // remote.
+ //
+
+ if (split -> store_ -> enableCompress)
+ {
+ decodeBuffer.decodeBoolValue(compressedSize);
+
+ if (compressedSize == 1)
+ {
+ //
+ // Get the compressed size.
+ //
+
+ // Since ProtoStep7 (#issue 108)
+ decodeBuffer.decodeValue(compressedSize, 32, 14);
+
+ split -> store_ -> validateSize(split -> d_size_, compressedSize);
+
+ split -> r_size_ = compressedSize;
+ }
+ }
+
+ //
+ // Update the size if the split
+ // was not already loaded.
+ //
+
+ if (split -> state_ != split_loaded)
+ {
+ split -> data_.clear();
+
+ if (compressedSize > 0)
+ {
+ split -> c_size_ = compressedSize;
+
+ #ifdef TEST
+ *logofs << "SplitStore: Split data of size "
+ << split -> d_size_ << " was compressed to "
+ << split -> c_size_ << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ split -> data_.resize(split -> c_size_);
+ }
+ else
+ {
+ split -> data_.resize(split -> d_size_);
+ }
+
+ unsigned char *data = split -> data_.begin();
+
+ data[0] = SPLIT_PATTERN;
+ data[1] = SPLIT_PATTERN;
+ }
+ #ifdef TEST
+ else
+ {
+ //
+ // The message had been already
+ // loaded from disk.
+ //
+
+ if (compressedSize > 0)
+ {
+ if ((int) compressedSize != split -> c_size_)
+ {
+ *logofs << "SplitStore: WARNING! Compressed data size is "
+ << "different than the loaded compressed size.\n"
+ << logofs_flush;
+ }
+
+ *logofs << "SplitStore: Ignoring the new size with "
+ << "loaded compressed size " << split -> c_size_
+ << ".\n" << logofs_flush;
+ }
+ }
+ #endif
+
+ return 1;
+}
+
+int SplitStore::receive(DecodeBuffer &decodeBuffer)
+{
+ if (splits_ -> size() == 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Function receive called with no splits available.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Function receive called with no splits available.\n";
+
+ HandleAbort();
+ }
+
+ if (current_ == splits_ -> end())
+ {
+ start(decodeBuffer);
+ }
+
+ //
+ // Check first if split was aborted, else add
+ // any new data to message being recomposed.
+ //
+
+ Split *split = *current_;
+
+ unsigned int abort = 0;
+
+ decodeBuffer.decodeBoolValue(abort);
+
+ if (abort == 1)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Aborting split for checksum ["
+ << DumpChecksum(split -> checksum_) << "] position "
+ << split -> position_ << " with " << (split ->
+ data_.size() - split -> next_) << " bytes to go "
+ << "out of " << split -> data_.size()
+ << ".\n" << logofs_flush;
+ #endif
+
+ statistics -> addSplitAborted();
+
+ statistics -> addSplitAbortedBytesOut(split -> r_size_ - split -> next_);
+
+ split -> next_ = split -> r_size_;
+
+ split -> state_ = split_aborted;
+ }
+ else
+ {
+ //
+ // Get the size of the packet.
+ //
+
+ unsigned int count;
+
+ decodeBuffer.decodeValue(count, 32, 10);
+
+ //
+ // If the split was not already loaded from
+ // disk, decode the packet and update our
+ // copy of the data. The encoding side may
+ // have not received the abort event, yet,
+ // and may be unaware that the message is
+ // stored in compressed form at our side.
+ //
+
+ #ifdef TEST
+ *logofs << "SplitStore: Receiving split for checksum ["
+ << DumpChecksum(split -> checksum_) << "] count "
+ << count << " position " << split -> position_
+ << ". Data size is " << split -> data_.size() << " ("
+ << split -> d_size_ << "/" << split -> c_size_ << "/"
+ << split -> r_size_ << "), " << split -> r_size_ -
+ (split -> next_ + count) << " to go.\n"
+ << logofs_flush;
+ #endif
+
+ if (split -> next_ + count > (unsigned) split -> r_size_)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Invalid data count "
+ << count << "provided in the split.\n"
+ << logofs_flush;
+
+ *logofs << "SplitStore: PANIC! While receiving split for "
+ << "checksum [" << DumpChecksum(split -> checksum_)
+ << "] with count " << count << " action ["
+ << DumpAction(split -> action_) << "] state ["
+ << DumpState(split -> state_) << "]. Data size is "
+ << split -> data_.size() << " (" << split -> d_size_
+ << "/" << split -> c_size_ << "), " << split ->
+ data_.size() - (split -> next_ + count)
+ << " to go.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Invalid data count "
+ << count << "provided in the split.\n";
+
+ HandleAbort();
+ }
+
+ if (split -> state_ != split_loaded)
+ {
+ #ifdef TEST
+
+ if (split -> next_ + count > split -> data_.size())
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Inconsistent split data size "
+ << split -> data_.size() << " with expected size "
+ << split -> r_size_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ HandleAbort();
+ }
+
+ #endif
+
+ memcpy(split -> data_.begin() + split -> next_,
+ decodeBuffer.decodeMemory(count), count);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: WARNING! Data discarded with split "
+ << "loaded from disk.\n" << logofs_flush;
+ #endif
+
+ decodeBuffer.decodeMemory(count);
+ }
+
+ split -> next_ += count;
+ }
+
+ //
+ // Is unsplit complete?
+ //
+
+ if (split -> next_ != split -> r_size_)
+ {
+ return 0;
+ }
+
+ //
+ // If the persistent cache is enabled,
+ // we have a valid checksum and the
+ // split was not originally retrieved
+ // from disk, save the message on disk.
+ //
+
+ if (split -> state_ != split_loaded &&
+ split -> state_ != split_aborted)
+ {
+ save(split);
+ }
+
+ //
+ // Move the split at the head of the
+ // list to the commits.
+ //
+
+ remove(split);
+
+ //
+ // Reset the current position to the
+ // end of the repository.
+ //
+
+ current_ = splits_ -> end();
+
+ #ifdef TEST
+ *logofs << "SplitStore: Removed split at head of the list. "
+ << "Resource is " << split -> resource_ << " request "
+ << (unsigned) split -> store_ -> opcode() << " position "
+ << split -> position_ << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+Split *SplitStore::pop()
+{
+ if (splits_ -> size() == 0)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: The split store is empty.\n"
+ << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+
+ //
+ // Move the pointer at the end of the list.
+ // The next send operation will eventually
+ // start a new split.
+ //
+
+ current_ = splits_ -> end();
+
+ Split *split = *(splits_ -> begin());
+
+ splits_ -> pop_front();
+
+ #ifdef TEST
+ *logofs << "SplitStore: Removed split at the head of the "
+ << "list with resource " << split -> resource_
+ << " request " << (unsigned) split -> store_ ->
+ opcode() << " position " << split -> position_
+ << ".\n" << logofs_flush;
+ #endif
+
+ splitStorageSize_ -= getNodeSize(split);
+
+ totalSplitSize_--;
+
+ totalSplitStorageSize_ -= getNodeSize(split);
+
+ #ifdef TEST
+ *logofs << "SplitStore: There are " << splits_ -> size()
+ << " messages in store [" << resource_ << "] with "
+ << "storage size " << splitStorageSize_ << ".\n"
+ << logofs_flush;
+
+ *logofs << "SplitStore: Total messages in stores are "
+ << totalSplitSize_ << " with total storage size "
+ << totalSplitStorageSize_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return split;
+}
+
+void SplitStore::remove(Split *split)
+{
+ #ifdef TEST
+ *logofs << "SplitStore: Going to remove the split from the list.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef TEST
+
+ if (split != getFirstSplit())
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Trying to remove a split "
+ << "not at the head of the list.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Trying to remove a split "
+ << "not at the head of the list.\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ //
+ // Move the split to the commit store.
+ //
+
+ splits_ -> pop_front();
+
+ commits_ -> splits_ -> push_back(split);
+
+ splitStorageSize_ -= getNodeSize(split);
+
+ totalSplitSize_--;
+
+ totalSplitStorageSize_ -= getNodeSize(split);
+
+ #ifdef TEST
+ *logofs << "SplitStore: There are " << splits_ -> size()
+ << " messages in store [" << resource_ << "] with "
+ << "storage size " << splitStorageSize_ << ".\n"
+ << logofs_flush;
+
+ *logofs << "SplitStore: Total messages in stores are "
+ << totalSplitSize_ << " with total storage size "
+ << totalSplitStorageSize_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef TEST
+
+ if (splits_ -> size() == 0)
+ {
+ if (splitStorageSize_ != 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Internal error calculating "
+ << "split data size. It is " << splitStorageSize_
+ << " while should be 0.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Internal error calculating "
+ << "split data size. It is " << splitStorageSize_
+ << " while should be 0.\n";
+
+ HandleAbort();
+ }
+ }
+
+ #endif
+}
+
+const char *SplitStore::name(const T_checksum checksum)
+{
+ if (checksum == NULL)
+ {
+ return NULL;
+ }
+
+ char *pathName = control -> ImageCachePath;
+
+ if (pathName == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot determine directory of "
+ << "NX image files.\n" << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+
+ int pathSize = strlen(pathName);
+
+ //
+ // File name is "[path][/I-c/I-][checksum][\0]",
+ // where c is the first hex digit of checksum.
+ //
+
+ int nameSize = pathSize + 7 + MD5_LENGTH * 2 + 1;
+
+ char *fileName = new char[nameSize];
+
+ if (fileName == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot allocate space for "
+ << "NX image file name.\n" << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+
+ strcpy(fileName, pathName);
+
+ sprintf(fileName + pathSize, "/I-%1X/I-",
+ *((unsigned char *) checksum) >> 4);
+
+ for (unsigned int i = 0; i < MD5_LENGTH; i++)
+ {
+ sprintf(fileName + pathSize + 7 + (i * 2), "%02X",
+ ((unsigned char *) checksum)[i]);
+ }
+
+ return fileName;
+}
+
+int SplitStore::save(Split *split)
+{
+ //
+ // Check if saving the message on the
+ // persistent cache is enabled.
+ //
+
+ if (split -> save_ == 0)
+ {
+ return 0;
+ }
+
+ T_checksum checksum = split -> checksum_;
+
+ const char *fileName = name(checksum);
+
+ if (fileName == NULL)
+ {
+ return 0;
+ }
+
+ unsigned int splitSize;
+
+ ostream *fileStream = NULL;
+
+ unsigned char *fileHeader = NULL;
+
+ //
+ // Get the other data from the split.
+ //
+
+ unsigned char opcode = split -> store_ -> opcode();
+
+ unsigned char *data = split -> data_.begin();
+
+ int dataSize = split -> d_size_;
+ int compressedSize = split -> c_size_;
+
+ #ifdef DEBUG
+ *logofs << "SplitStore: Going to save split OPCODE#"
+ << (unsigned int) opcode << " to file '" << fileName
+ << "' with size " << dataSize << " and compressed size "
+ << compressedSize << ".\n" << logofs_flush;
+ #endif
+
+ DisableSignals();
+
+ //
+ // Change the mask to make the file only
+ // readable by the user, then restore the
+ // old mask.
+ //
+
+ mode_t fileMode;
+
+ //
+ // Check if the file already exists. We try to
+ // load the message when the split is started
+ // and save it only if it is not found. Still
+ // the remote side may send the same image mul-
+ // tiple time and we may not have the time to
+ // notify the abort.
+ //
+
+ struct stat fileStat;
+
+ if (stat(fileName, &fileStat) == 0)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Image file '" << fileName
+ << "' already present on disk.\n"
+ << logofs_flush;
+ #endif
+
+ goto SplitStoreSaveError;
+ }
+
+ fileMode = umask(0077);
+
+ fileStream = new ofstream(fileName, ios::out | ios::binary);
+
+ umask(fileMode);
+
+ if (CheckData(fileStream) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot open file '" << fileName
+ << "' for output.\n" << logofs_flush;
+ #endif
+
+ goto SplitStoreSaveError;
+ }
+
+ fileHeader = new unsigned char[SPLIT_HEADER_SIZE];
+
+ if (fileHeader == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot allocate space for "
+ << "NX image header.\n" << logofs_flush;
+ #endif
+
+ goto SplitStoreSaveError;
+ }
+
+ //
+ // Leave 3 bytes for future use. Please note
+ // that, on some CPUs, we can't use PutULONG()
+ // to write integers that are not aligned to
+ // the word boundary.
+ //
+
+ *fileHeader = opcode;
+
+ *(fileHeader + 1) = 0;
+ *(fileHeader + 2) = 0;
+ *(fileHeader + 3) = 0;
+
+ PutULONG(dataSize, fileHeader + 4, false);
+ PutULONG(compressedSize, fileHeader + 8, false);
+
+ splitSize = (compressedSize > 0 ? compressedSize : dataSize);
+
+ if (PutData(fileStream, fileHeader, SPLIT_HEADER_SIZE) < 0 ||
+ PutData(fileStream, data, splitSize) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot write to NX "
+ << "image file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ goto SplitStoreSaveError;
+ }
+
+ //
+ // Check if all the data was written on the
+ // disk and, if not, remove the faulty copy.
+ //
+
+ FlushData(fileStream);
+
+ if (CheckData(fileStream) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Failed to write NX "
+ << "image file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Failed to write NX "
+ << "image file '" << fileName << "'.\n";
+
+ goto SplitStoreSaveError;
+ }
+
+ #ifdef TEST
+ *logofs << "SplitStore: Saved split to file '" << fileName
+ << "' with data size " << dataSize << " and "
+ << "compressed data size " << compressedSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ delete fileStream;
+
+ delete [] fileName;
+ delete [] fileHeader;
+
+ EnableSignals();
+
+ //
+ // Update the timestamp as the operation
+ // may have taken some time.
+ //
+
+ getNewTimestamp();
+
+ return 1;
+
+SplitStoreSaveError:
+
+ delete fileStream;
+
+ if (fileName != NULL)
+ {
+ unlink(fileName);
+ }
+
+ delete [] fileName;
+ delete [] fileHeader;
+
+ EnableSignals();
+
+ return -1;
+}
+
+int SplitStore::find(Split *split)
+{
+ const char *fileName = name(split -> checksum_);
+
+ if (fileName == NULL)
+ {
+ return 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "SplitStore: Going to find split OPCODE#"
+ << (unsigned) split -> store_ -> opcode()
+ << " in file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Check if the file exists and, at the
+ // same time, update the modification
+ // time to prevent its deletion.
+ //
+
+ if (utime(fileName, NULL) == 0)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Found split OPCODE#"
+ << (unsigned) split -> store_ -> opcode()
+ << " in file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ delete [] fileName;
+
+ return 1;
+ }
+
+ #ifdef TEST
+ *logofs << "SplitStore: WARNING! Can't find split "
+ << "OPCODE#" << (unsigned) split -> store_ ->
+ opcode() << " in file '" << fileName
+ << "'.\n" << logofs_flush;
+ #endif
+
+ delete [] fileName;
+
+ return 0;
+}
+
+int SplitStore::load(Split *split)
+{
+ //
+ // Check if loading the image is enabled.
+ //
+
+ if (split -> load_ == 0)
+ {
+ return 0;
+ }
+
+ const char *fileName = name(split -> checksum_);
+
+ if (fileName == NULL)
+ {
+ return 0;
+ }
+
+ unsigned char fileOpcode;
+
+ int fileSize;
+ int fileCSize;
+
+ istream *fileStream = NULL;
+
+ unsigned char *fileHeader = NULL;
+
+ DisableSignals();
+
+ #ifdef DEBUG
+ *logofs << "SplitStore: Going to load split OPCODE#"
+ << (unsigned int) split -> store_ -> opcode()
+ << " from file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ fileStream = new ifstream(fileName, ios::in | ios::binary);
+
+ if (CheckData(fileStream) < 0)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: WARNING! Can't open image file '"
+ << fileName << "' on disk.\n" << logofs_flush;
+ #endif
+
+ goto SplitStoreLoadError;
+ }
+
+ fileHeader = new unsigned char[SPLIT_HEADER_SIZE];
+
+ if (fileHeader == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot allocate space for "
+ << "NX image header.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot allocate space for "
+ << "NX image header.\n";
+
+ goto SplitStoreLoadError;
+ }
+
+ if (GetData(fileStream, fileHeader, SPLIT_HEADER_SIZE) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot read header from "
+ << "NX image file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Cannot read header from "
+ << "NX image file '" << fileName << "'.\n";
+
+ goto SplitStoreLoadError;
+ }
+
+ fileOpcode = *fileHeader;
+
+ fileSize = GetULONG(fileHeader + 4, false);
+ fileCSize = GetULONG(fileHeader + 8, false);
+
+ //
+ // Don't complain if we find that data was saved
+ // in compressed form even if we were not aware
+ // of the compressed data size. The remote side
+ // compresses the data only at the time it starts
+ // the transferral of the split. We replace our
+ // copy of the data with whatever we find on the
+ // disk.
+ //
+
+ if (fileOpcode != split -> store_ -> opcode() ||
+ fileSize != split -> d_size_ ||
+ fileSize > control -> MaximumRequestSize ||
+ fileCSize > control -> MaximumRequestSize)
+
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: PANIC! Corrupted image file '" << fileName
+ << "'. Expected " << (unsigned int) split -> store_ -> opcode()
+ << "/" << split -> d_size_ << "/" << split -> c_size_ << " found "
+ << (unsigned int) fileOpcode << "/" << fileSize << "/"
+ << fileCSize << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Corrupted image file '" << fileName
+ << "'. Expected " << (unsigned int) split -> store_ -> opcode()
+ << "/" << split -> d_size_ << "/" << split -> c_size_ << " found "
+ << (unsigned int) fileOpcode << "/" << fileSize << "/"
+ << fileCSize << ".\n";
+
+ goto SplitStoreLoadError;
+ }
+
+ //
+ // Update the data size with the size
+ // we got from the disk record.
+ //
+
+ split -> d_size_ = fileSize;
+ split -> c_size_ = fileCSize;
+
+ unsigned int splitSize;
+
+ if (fileCSize > 0)
+ {
+ splitSize = fileCSize;
+ }
+ else
+ {
+ splitSize = fileSize;
+ }
+
+ //
+ // Allocate a new buffer if we didn't
+ // do that already or if the size is
+ // different.
+ //
+
+ if (split -> data_.size() != splitSize)
+ {
+ split -> data_.clear();
+
+ split -> data_.resize(splitSize);
+ }
+
+ if (GetData(fileStream, split -> data_.begin(), splitSize) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "SplitStore: PANIC! Cannot read data from "
+ << "NX image file '" << fileName << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Cannot read data from "
+ << "NX image file '" << fileName << "'.\n";
+
+ goto SplitStoreLoadError;
+ }
+
+ delete fileStream;
+
+ delete [] fileHeader;
+ delete [] fileName;
+
+ EnableSignals();
+
+ //
+ // Update the timestamp as the operation
+ // may have taken some time.
+ //
+
+ getNewTimestamp();
+
+ return 1;
+
+SplitStoreLoadError:
+
+ delete fileStream;
+
+ unlink(fileName);
+
+ delete [] fileName;
+ delete [] fileHeader;
+
+ EnableSignals();
+
+ return -1;
+}
+
+Split *CommitStore::pop()
+{
+ if (splits_ -> size() == 0)
+ {
+ #ifdef TEST
+ *logofs << "CommitStore: The commit store is empty.\n"
+ << logofs_flush;
+ #endif
+
+ return NULL;
+ }
+
+ Split *split = *(splits_ -> begin());
+
+ splits_ -> pop_front();
+
+ #ifdef TEST
+ *logofs << "CommitStore: Removed commit split at the head "
+ << "of the list with resource " << split -> resource_
+ << " request " << (unsigned) split -> store_ ->
+ opcode() << " position " << split -> position_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return split;
+}
+
+int CommitStore::expand(Split *split, unsigned char *buffer, const int size)
+{
+ #ifdef TEST
+ *logofs << "CommitStore: Expanding split data with "
+ << size << " bytes to write.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef TEST
+
+ if (size < split -> i_size_ + split -> d_size_)
+ {
+ #ifdef PANIC
+ *logofs << "CommitStore: PANIC! Wrong size of the provided "
+ << "buffer. It should be " << split -> i_size_ +
+ split -> d_size_ << " instead of " << size
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Wrong size of the provided "
+ << "buffer. It should be " << split -> i_size_ +
+ split -> d_size_ << " instead of " << size
+ << ".\n";
+
+ HandleAbort();
+ }
+
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "CommitStore: Copying " << split -> i_size_
+ << " bytes of identity.\n" << logofs_flush;
+ #endif
+
+ memcpy(buffer, split -> identity_.begin(), split -> i_size_);
+
+ //
+ // Copy data, if any, to the buffer.
+ //
+
+ if (size > split -> i_size_)
+ {
+ //
+ // Check if message has been stored
+ // in compressed format.
+ //
+
+ if (split -> c_size_ == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "CommitStore: Copying " << split -> d_size_
+ << " bytes of plain data.\n" << logofs_flush;
+ #endif
+
+ memcpy(buffer + split -> i_size_, split -> data_.begin(), split -> d_size_);
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "CommitStore: Decompressing " << split -> c_size_
+ << " bytes and copying " << split -> d_size_
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ if (compressor_ ->
+ decompressBuffer(buffer + split -> i_size_,
+ split -> d_size_, split -> data_.begin(),
+ split -> c_size_) < 0)
+ {
+ #ifdef PANIC
+ *logofs << "CommitStore: PANIC! Split data decompression failed.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Split data decompression failed.\n";
+
+ return -1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+int CommitStore::update(Split *split)
+{
+ if (split -> action_ != IS_ADDED)
+ {
+ return 0;
+ }
+
+ //
+ // We don't need the identity data at
+ // the encoding side.
+ //
+
+ if (split -> identity_.size() == 0)
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Going to update the size "
+ << "for object at position " << split -> position_
+ << " with data size " << split -> d_size_
+ << " and compressed data size " << split ->
+ c_size_ << ".\n" << logofs_flush;
+ #endif
+
+ split -> store_ -> updateData(split -> position_, split -> d_size_,
+ split -> c_size_);
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "SplitStore: Going to update data and size "
+ << "for object at position " << split -> position_
+ << " with data size " << split -> d_size_
+ << " and compressed data size " << split ->
+ c_size_ << ".\n" << logofs_flush;
+ #endif
+
+ split -> store_ -> updateData(split -> position_, split -> data_.begin(),
+ split -> d_size_, split -> c_size_);
+ }
+
+ //
+ // Unlock message so that we can remove
+ // or save it on disk at shutdown.
+ //
+
+ if (split -> action_ == IS_ADDED)
+ {
+ split -> store_ -> unlock(split -> position_);
+
+ #ifdef TEST
+
+ validate(split);
+
+ #endif
+ }
+
+ return 1;
+}
+
+int CommitStore::validate(Split *split)
+{
+ MessageStore *store = split -> store_;
+
+ int p, n, s;
+
+ s = store -> cacheSlots;
+
+ for (p = 0, n = 0; p < s; p++)
+ {
+ if (store -> getLocks(p) == 1)
+ {
+ n++;
+ }
+ else if (store -> getLocks(p) != 0)
+ {
+ #ifdef PANIC
+ *logofs << "CommitStore: PANIC! Repository for OPCODE#"
+ << (unsigned int) store -> opcode() << " has "
+ << store -> getLocks(p) << " locks for message "
+ << "at position " << p << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Repository for OPCODE#"
+ << (unsigned int) store -> opcode() << " has "
+ << store -> getLocks(p) << " locks for message "
+ << "at position " << p << ".\n";
+
+ HandleAbort();
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "CommitStore: Repository for OPCODE#"
+ << (unsigned int) store -> opcode()
+ << " has " << n << " locked messages.\n"
+ << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/Split.h b/nxcomp/src/Split.h
new file mode 100644
index 000000000..ee5eae7fe
--- /dev/null
+++ b/nxcomp/src/Split.h
@@ -0,0 +1,543 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Split_H
+#define Split_H
+
+#include "Types.h"
+#include "Timestamp.h"
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to know how many splits
+// are allocated and deallocated.
+//
+
+#undef REFERENCES
+
+//
+// Size of header of messages saved on
+// disk.
+//
+
+#define SPLIT_HEADER_SIZE 12
+
+//
+// This class is used to divide big messages
+// in smaller chunks and send them at idle
+// time.
+//
+
+class EncodeBuffer;
+class DecodeBuffer;
+
+class SplitStore;
+class CommitStore;
+
+//
+// Preferred message streaming policy.
+//
+
+typedef enum
+{
+ split_none = -1,
+ split_async = 1,
+ split_sync
+
+} T_split_mode;
+
+//
+// Current state of the split. Used to
+// implement the state machine.
+//
+
+typedef enum
+{
+ split_undefined = -1,
+ split_added,
+ split_missed,
+ split_loaded,
+ split_aborted,
+ split_notified
+
+} T_split_state;
+
+class Split
+{
+ friend class SplitStore;
+ friend class CommitStore;
+
+ public:
+
+ Split();
+
+ ~Split();
+
+ //
+ // Note that, differently from the message
+ // store, the split store doesn't account
+ // for the data offset when dealing with
+ // the data. This means that both the size_
+ // and c_size members represent the actual
+ // size of the data part.
+ //
+
+ void compressedSize(int size)
+ {
+ c_size_ = size;
+
+ store_ -> validateSize(d_size_, c_size_);
+ }
+
+ int compressedSize()
+ {
+ return c_size_;
+ }
+
+ int plainSize()
+ {
+ return i_size_ + d_size_;
+ }
+
+ T_checksum getChecksum()
+ {
+ return checksum_;
+ }
+
+ MessageStore *getStore()
+ {
+ return store_;
+ }
+
+ T_split_state getState()
+ {
+ return state_;
+ }
+
+ T_store_action getAction()
+ {
+ return action_;
+ }
+
+ //
+ // We may need to find the resource
+ // associated to the split message
+ // because old protocol version use
+ // a single store for all splits.
+ //
+
+ int getResource()
+ {
+ return resource_;
+ }
+
+ int getRequest()
+ {
+ return store_ -> opcode();
+ }
+
+ int getPosition()
+ {
+ return position_;
+ }
+
+ T_split_mode getMode()
+ {
+ return mode_;
+ }
+
+ void setPolicy(int load, int save)
+ {
+ load_ = load;
+ save_ = save;
+ }
+
+ void setState(T_split_state state)
+ {
+ state_ = state;
+ }
+
+ private:
+
+ //
+ // The agent's resource which is splitting
+ // the message.
+ //
+
+ int resource_;
+
+ //
+ // Where to find the message in the message
+ // store or the X sequence number of the
+ // original request, in recent versions.
+ //
+
+ int position_;
+
+ //
+ // Which store is involved.
+ //
+
+ MessageStore *store_;
+
+ //
+ // Identity size of the message.
+ //
+
+ int i_size_;
+
+ //
+ // This is the uncompressed data size of the
+ // original message.
+ //
+
+ int d_size_;
+
+ //
+ // This is the size of the compressed data,
+ // if the data is stored in this form.
+ //
+
+ int c_size_;
+
+ //
+ // Size of the data buffer, as known by the
+ // encoding side. This field is only used at
+ // the decoding side. The remote size can be
+ // different from the actual data size, if
+ // the encoding side did not confirm that it
+ // received the abort split event.
+ //
+
+ int r_size_;
+
+ //
+ // Position in the data buffer that will be
+ // the target of the next send or receive
+ // operation while streaming the message.
+ //
+
+ int next_;
+
+ //
+ // Load or save the split to disk.
+ //
+
+ int load_;
+ int save_;
+
+ //
+ // Checksum of the original message.
+ //
+
+ T_checksum checksum_;
+
+ //
+ // Was this split confirmed or aborted?
+ //
+
+ T_split_state state_;
+
+ //
+ // What's the policy for sending this split?
+ //
+
+ T_split_mode mode_;
+
+ //
+ // Operation that had been performed on the
+ // store at the time the split was added.
+ //
+
+ T_store_action action_;
+
+ //
+ // Container for the identity and data part
+ // of the X message.
+ //
+
+ T_data identity_;
+ T_data data_;
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+class SplitStore
+{
+ public:
+
+ SplitStore(StaticCompressor *compressor, CommitStore *commits, int resource);
+
+ ~SplitStore();
+
+ Split *getFirstSplit() const
+ {
+ if (splits_ -> size() > 0)
+ {
+ return (*(splits_ -> begin()));
+ }
+
+ return NULL;
+ }
+
+ Split *getLastSplit() const
+ {
+ if (splits_ -> size() > 0)
+ {
+ return (*(--(splits_ -> end())));
+ }
+
+ return NULL;
+ }
+
+ int getNodeSize(const Split *split) const
+ {
+ //
+ // Take in account 64 bytes of overhead
+ // for each node.
+ //
+
+ return (sizeof(class Split) + 64 +
+ split -> i_size_ + split -> d_size_);
+ }
+
+ int getStorageSize()
+ {
+ return splitStorageSize_;
+ }
+
+ static int getTotalSize()
+ {
+ return totalSplitSize_;
+ }
+
+ static int getTotalStorageSize()
+ {
+ return totalSplitStorageSize_;
+ }
+
+ int getResource()
+ {
+ return resource_;
+ }
+
+ int getSize()
+ {
+ return splits_ -> size();
+ }
+
+ T_splits *getSplits()
+ {
+ return splits_;
+ }
+
+ //
+ // Used, respectively, at the encoding
+ // and decoding side.
+ //
+
+ Split *add(MessageStore *store, int resource, T_split_mode mode,
+ int position, T_store_action action, T_checksum checksum,
+ const unsigned char *buffer, const int size);
+
+ Split *add(MessageStore *store, int resource, int position,
+ T_store_action action, T_checksum checksum,
+ unsigned char *buffer, const int size);
+
+ //
+ // Handle the streaming of the message data.
+ //
+
+ int send(EncodeBuffer &encodeBuffer, int packetSize);
+
+ int receive(DecodeBuffer &decodeBuffer);
+
+ //
+ // Remove the top element of the split store
+ // and update the storage size.
+ //
+
+ void remove(Split *split);
+
+ //
+ // Load the message from disk and replace the
+ // message in the store with the new copy.
+ //
+
+ int load(Split *split);
+
+ //
+ // Save the data to disk after the message has
+ // been recomposed at the local side.
+ //
+
+ int save(Split *split);
+
+ //
+ // Find the message on disk and update the last
+ // modification time. This is currently unused.
+ //
+
+ int find(Split *split);
+
+ //
+ // Remove the element on top of the queue and
+ // discard any split data that still needs to
+ // be transferred.
+ //
+
+ Split *pop();
+
+ //
+ // Dump the content of the store.
+ //
+
+ void dump();
+
+ protected:
+
+ //
+ // Repository where to add the splits.
+ //
+
+ T_splits *splits_;
+
+ //
+ // Compress and decompress the data payload.
+ //
+
+ StaticCompressor *compressor_;
+
+ private:
+
+ int start(EncodeBuffer &encodeBuffer);
+
+ int start(DecodeBuffer &decodeBuffer);
+
+ void push(Split *split);
+
+ //
+ // Determine the name of the file object based
+ // on the checksum.
+ //
+
+ const char *name(const T_checksum checksum);
+
+ //
+ // The number of elements and data bytes
+ // in the repository.
+ //
+
+ int splitStorageSize_;
+
+ static int totalSplitSize_;
+ static int totalSplitStorageSize_;
+
+ //
+ // Current element being transferred.
+ //
+
+ T_splits::iterator current_;
+
+ //
+ // Repository where to move the splits
+ // after they are completely recomposed.
+ //
+
+ CommitStore *commits_;
+
+ //
+ // Index in the client store or none,
+ // if this is a commit store.
+ //
+
+ int resource_;
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+class CommitStore : public SplitStore
+{
+ //
+ // This is just a split store.
+ //
+
+ public:
+
+ CommitStore(StaticCompressor *compressor)
+
+ : SplitStore(compressor, NULL, nothing)
+ {
+ }
+
+ //
+ // Move identity and data of the split to the
+ // provided buffer, uncompressing the message,
+ // if needed.
+ //
+
+ int expand(Split *split, unsigned char *buffer, const int size);
+
+ //
+ // We recomposed the data part. If the message
+ // was originally added to the message store,
+ // replace the data and/or update the size.
+ //
+
+ int update(Split *split);
+
+ //
+ // Remove the split from the commit queue.
+ //
+
+ Split *pop();
+
+ //
+ // This is just used for debug. It checks
+ // if any message in the message store has
+ // an invalid number of locks.
+ //
+
+ int validate(Split *split);
+};
+
+#endif /* Split_H */
diff --git a/nxcomp/src/StaticCompressor.cpp b/nxcomp/src/StaticCompressor.cpp
new file mode 100644
index 000000000..b47193354
--- /dev/null
+++ b/nxcomp/src/StaticCompressor.cpp
@@ -0,0 +1,432 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Z.h"
+#include "Misc.h"
+#include "Control.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+#include "StaticCompressor.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+StaticCompressor::StaticCompressor(int compressionLevel,
+ int compressionThreshold)
+{
+ buffer_ = NULL;
+ bufferSize_ = 0;
+
+ compressionStream_.zalloc = (alloc_func) 0;
+ compressionStream_.zfree = (free_func) 0;
+ compressionStream_.opaque = (voidpf) 0;
+
+ decompressionStream_.zalloc = (alloc_func) 0;
+ decompressionStream_.zfree = (free_func) 0;
+ decompressionStream_.opaque = (void *) 0;
+
+ decompressionStream_.next_in = (Bytef *) 0;
+ decompressionStream_.avail_in = 0;
+
+ #ifdef TEST
+ *logofs << "StaticCompressor: Compression level is "
+ << compressionLevel << ".\n" << logofs_flush;
+ #endif
+
+ int result = deflateInit2(&compressionStream_, compressionLevel, Z_DEFLATED,
+ 15, 9, Z_DEFAULT_STRATEGY);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Cannot initialize the "
+ << "compression stream. Error is '" << zError(result)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot initialize the compression "
+ << "stream. Error is '" << zError(result) << "'.\n";
+
+ HandleAbort();
+ }
+
+ result = inflateInit2(&decompressionStream_, 15);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Cannot initialize the "
+ << "decompression stream. Error is '" << zError(result)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot initialize the decompression "
+ << "stream. Error is '" << zError(result) << "'.\n";
+
+ HandleAbort();
+ }
+
+ #ifdef TEST
+ *logofs << "StaticCompressor: Compression threshold is "
+ << compressionThreshold << ".\n" << logofs_flush;
+ #endif
+
+ threshold_ = compressionThreshold;
+}
+
+StaticCompressor::~StaticCompressor()
+{
+ int result = deflateEnd(&compressionStream_);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Cannot deinitialize the "
+ << "compression stream. Error is '" << zError(result)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot deinitialize the compression "
+ << "stream. Error is '" << zError(result) << "'.\n";
+ }
+
+ result = inflateEnd(&decompressionStream_);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Cannot deinitialize the "
+ << "decompression stream. Error is '" << zError(result)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot deinitialize the decompression "
+ << "stream. Error is '" << zError(result) << "'.\n";
+ }
+
+ delete [] buffer_;
+}
+
+//
+// This function compresses and encodes the compressed
+// buffer. It returns a pointer to the internal buffer
+// where data was compressed.
+//
+
+int StaticCompressor::compressBuffer(const unsigned char *plainBuffer,
+ const unsigned int plainSize,
+ unsigned char *&compressedBuffer,
+ unsigned int &compressedSize,
+ EncodeBuffer &encodeBuffer)
+{
+ if (control -> LocalDataCompression == 0 ||
+ compressBuffer(plainBuffer, plainSize,
+ compressedBuffer, compressedSize) <= 0)
+ {
+ encodeBuffer.encodeBoolValue(0);
+
+ encodeBuffer.encodeMemory(plainBuffer, plainSize);
+
+ return 0;
+ }
+ else
+ {
+ encodeBuffer.encodeBoolValue(1);
+
+ encodeBuffer.encodeValue(compressedSize, 32, 14);
+ encodeBuffer.encodeValue(plainSize, 32, 14);
+
+ encodeBuffer.encodeMemory(compressedBuffer, compressedSize);
+
+ return 1;
+ }
+}
+
+//
+// This function compresses data into a dynamically
+// allocated buffer and returns a pointer to it, so
+// application must copy data before the next call.
+//
+
+int StaticCompressor::compressBuffer(const unsigned char *plainBuffer,
+ const unsigned int plainSize,
+ unsigned char *&compressedBuffer,
+ unsigned int &compressedSize)
+{
+ #ifdef DEBUG
+ *logofs << "StaticCompressor: Called for buffer at "
+ << (void *) plainBuffer << ".\n"
+ << logofs_flush;
+ #endif
+
+ compressedSize = plainSize;
+
+ if (plainSize < (unsigned int) threshold_)
+ {
+ #ifdef TEST
+ *logofs << "StaticCompressor: Leaving buffer unchanged. "
+ << "Plain size is " << plainSize << " with threshold "
+ << (unsigned int) threshold_ << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ //
+ // Determine the size of the temporary
+ // buffer.
+ //
+
+ unsigned int newSize = plainSize + (plainSize / 1000) + 12;
+
+ //
+ // Allocate a new buffer if it grows
+ // beyond 64K.
+ //
+
+ if (buffer_ == NULL || (bufferSize_ > 65536 &&
+ newSize < bufferSize_ / 2) || newSize > bufferSize_)
+ {
+ delete [] buffer_;
+
+ buffer_ = new unsigned char[newSize];
+
+ if (buffer_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Can't allocate compression "
+ << "buffer of " << newSize << " bytes. Error is " << EGET()
+ << " ' " << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Warning" << ": Can't allocate compression buffer of "
+ << newSize << " bytes. Error is " << EGET()
+ << " '" << ESTR() << "'.\n";
+
+ bufferSize_ = 0;
+
+ return 0;
+ }
+
+ bufferSize_ = newSize;
+ }
+
+ unsigned int resultingSize = newSize;
+
+ int result = ZCompress(&compressionStream_, buffer_, &resultingSize,
+ plainBuffer, plainSize);
+
+ if (result == Z_OK)
+ {
+ if (resultingSize > newSize)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Overflow in compression "
+ << "buffer size. " << "Expected size was " << newSize
+ << " while it is " << resultingSize << ".\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Overflow in compress buffer size. "
+ << "Expected size was " << newSize << " while it is "
+ << resultingSize << ".\n";
+
+ return -1;
+ }
+ else if (resultingSize >= plainSize)
+ {
+ #ifdef TEST
+ *logofs << "StaticCompressor: Leaving buffer unchanged. "
+ << "Plain size is " << plainSize << " compressed "
+ << "size is " << resultingSize << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ compressedBuffer = buffer_;
+ compressedSize = resultingSize;
+
+ #ifdef TEST
+ *logofs << "StaticCompressor: Compressed buffer from "
+ << plainSize << " to " << resultingSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ return 1;
+ }
+
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Failed compression of buffer. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed compression of buffer. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+}
+
+int StaticCompressor::decompressBuffer(unsigned char *plainBuffer,
+ unsigned int plainSize,
+ const unsigned char *&compressedBuffer,
+ unsigned int &compressedSize,
+ DecodeBuffer &decodeBuffer)
+{
+ #ifdef DEBUG
+ *logofs << "StaticCompressor: Called for buffer at "
+ << (void *) plainBuffer << ".\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int value;
+
+ decodeBuffer.decodeBoolValue(value);
+
+ if (value == 0)
+ {
+ memcpy(plainBuffer,
+ decodeBuffer.decodeMemory(plainSize),
+ plainSize);
+
+ return 0;
+ }
+
+ unsigned int checkSize = plainSize;
+
+ decodeBuffer.decodeValue(value, 32, 14);
+ compressedSize = value;
+
+ decodeBuffer.decodeValue(value, 32, 14);
+ checkSize = value;
+
+ //
+ // If caller needs the original compressed
+ // data it must copy this to its own buffer
+ // before using any further decode function.
+ //
+
+ compressedBuffer = decodeBuffer.decodeMemory(compressedSize);
+
+ int result = ZDecompress(&decompressionStream_, plainBuffer, &checkSize,
+ compressedBuffer, compressedSize);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Failure decompressing buffer. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failure decompressing buffer. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ return -1;
+ }
+ else if (plainSize != checkSize)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Expected decompressed size was "
+ << plainSize << " while it is " << checkSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Expected decompressed size was "
+ << plainSize << " while it is " << checkSize
+ << ".\n";
+
+ return -1;
+ }
+
+ return 1;
+}
+
+//
+// This is used to uncompress on-the-fly
+// messages whose data has been stored
+// in compressed format.
+//
+
+int StaticCompressor::decompressBuffer(unsigned char *plainBuffer,
+ const unsigned int plainSize,
+ const unsigned char *compressedBuffer,
+ const unsigned int compressedSize)
+{
+ #ifdef TEST
+ *logofs << "StaticCompressor: Called for buffer at "
+ << (void *) plainBuffer << ".\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int checkSize = plainSize;
+
+ int result = ZDecompress(&decompressionStream_, plainBuffer, &checkSize,
+ compressedBuffer, compressedSize);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Failure decompressing buffer. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ if (plainSize != checkSize)
+ {
+ #ifdef PANIC
+ *logofs << "StaticCompressor: PANIC! Expected decompressed size was "
+ << plainSize << " while it is " << checkSize
+ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Expected decompressed size was "
+ << plainSize << " while it is " << checkSize
+ << ".\n";
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "StaticCompressor: Decompressed buffer from "
+ << compressedSize << " to " << plainSize
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
diff --git a/nxcomp/src/StaticCompressor.h b/nxcomp/src/StaticCompressor.h
new file mode 100644
index 000000000..e0b81a527
--- /dev/null
+++ b/nxcomp/src/StaticCompressor.h
@@ -0,0 +1,80 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef StaticCompressor_H
+#define StaticCompressor_H
+
+#include "Z.h"
+
+class EncodeBuffer;
+class DecodeBuffer;
+
+class StaticCompressor
+{
+ public:
+
+ StaticCompressor(int compressionLevel, int compressionThreshold);
+
+ ~StaticCompressor();
+
+ int compressBuffer(const unsigned char *plainBuffer, const unsigned int plainSize,
+ unsigned char *&compressedBuffer, unsigned int &compressedSize,
+ EncodeBuffer &encodeBuffer);
+
+ int compressBuffer(const unsigned char *plainBuffer, const unsigned int plainSize,
+ unsigned char *&compressedBuffer, unsigned int &compressedSize);
+
+ int decompressBuffer(unsigned char *plainBuffer, unsigned int plainSize,
+ const unsigned char *&compressedBuffer, unsigned int &compressedSize,
+ DecodeBuffer &decodeBuffer);
+
+ int decompressBuffer(unsigned char *plainBuffer, const unsigned int plainSize,
+ const unsigned char *compressedBuffer, const unsigned compressedSize);
+
+ static int isCompressionLevel(int compressionLevel)
+ {
+ return (compressionLevel == Z_DEFAULT_COMPRESSION ||
+ (compressionLevel >= Z_NO_COMPRESSION &&
+ compressionLevel <= Z_BEST_COMPRESSION));
+ }
+
+ int fullReset()
+ {
+ return (deflateReset(&compressionStream_) == Z_OK &&
+ inflateReset(&decompressionStream_) == Z_OK);
+ }
+
+ private:
+
+ z_stream compressionStream_;
+ z_stream decompressionStream_;
+
+ unsigned char *buffer_;
+ unsigned int bufferSize_;
+
+ int threshold_;
+};
+
+#endif
diff --git a/nxcomp/src/Statistics.cpp b/nxcomp/src/Statistics.cpp
new file mode 100644
index 000000000..ab8fd74dc
--- /dev/null
+++ b/nxcomp/src/Statistics.cpp
@@ -0,0 +1,2007 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include "Statistics.h"
+
+#include "Control.h"
+
+#include "Proxy.h"
+
+#include "ClientStore.h"
+#include "ServerStore.h"
+
+//
+// Length of temporary buffer
+// used to format output.
+//
+
+#define FORMAT_LENGTH 1024
+
+//
+// Log level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Note that when presenting statistics we invert the
+// correct semantics of X client and server entities.
+// This is questionable, but matches the user's pers-
+// pective of running remote X applications on its
+// local client.
+//
+
+Statistics::Statistics(Proxy *proxy) : proxy_(proxy)
+{
+ transportPartial_.idleTime_ = 0;
+ transportPartial_.readTime_ = 0;
+ transportPartial_.writeTime_ = 0;
+ transportPartial_.proxyBytesIn_ = 0;
+ transportPartial_.proxyBytesOut_ = 0;
+ transportPartial_.proxyFramesIn_ = 0;
+ transportPartial_.proxyFramesOut_ = 0;
+ transportPartial_.proxyWritesOut_ = 0;
+ transportPartial_.compressedBytesIn_ = 0;
+ transportPartial_.compressedBytesOut_ = 0;
+ transportPartial_.decompressedBytesIn_ = 0;
+ transportPartial_.decompressedBytesOut_ = 0;
+ transportPartial_.framingBitsOut_ = 0;
+
+ transportTotal_.idleTime_ = 0;
+ transportTotal_.readTime_ = 0;
+ transportTotal_.writeTime_ = 0;
+ transportTotal_.proxyBytesIn_ = 0;
+ transportTotal_.proxyBytesOut_ = 0;
+ transportTotal_.proxyFramesIn_ = 0;
+ transportTotal_.proxyFramesOut_ = 0;
+ transportTotal_.proxyWritesOut_ = 0;
+ transportTotal_.compressedBytesIn_ = 0;
+ transportTotal_.compressedBytesOut_ = 0;
+ transportTotal_.decompressedBytesIn_ = 0;
+ transportTotal_.decompressedBytesOut_ = 0;
+ transportTotal_.framingBitsOut_ = 0;
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ protocolPartial_.requestCached_[i] = 0;
+ protocolPartial_.requestReplied_[i] = 0;
+ protocolPartial_.requestCount_[i] = 0;
+ protocolPartial_.requestBitsIn_[i] = 0;
+ protocolPartial_.requestBitsOut_[i] = 0;
+
+ protocolPartial_.renderRequestCached_[i] = 0;
+ protocolPartial_.renderRequestCount_[i] = 0;
+ protocolPartial_.renderRequestBitsIn_[i] = 0;
+ protocolPartial_.renderRequestBitsOut_[i] = 0;
+
+ protocolPartial_.replyCached_[i] = 0;
+ protocolPartial_.replyCount_[i] = 0;
+ protocolPartial_.replyBitsIn_[i] = 0;
+ protocolPartial_.replyBitsOut_[i] = 0;
+
+ protocolPartial_.eventCached_[i] = 0;
+ protocolPartial_.eventCount_[i] = 0;
+ protocolPartial_.eventBitsIn_[i] = 0;
+ protocolPartial_.eventBitsOut_[i] = 0;
+
+ protocolTotal_.requestCached_[i] = 0;
+ protocolTotal_.requestReplied_[i] = 0;
+ protocolTotal_.requestCount_[i] = 0;
+ protocolTotal_.requestBitsIn_[i] = 0;
+ protocolTotal_.requestBitsOut_[i] = 0;
+
+ protocolTotal_.renderRequestCached_[i] = 0;
+ protocolTotal_.renderRequestCount_[i] = 0;
+ protocolTotal_.renderRequestBitsIn_[i] = 0;
+ protocolTotal_.renderRequestBitsOut_[i] = 0;
+
+ protocolTotal_.replyCached_[i] = 0;
+ protocolTotal_.replyCount_[i] = 0;
+ protocolTotal_.replyBitsIn_[i] = 0;
+ protocolTotal_.replyBitsOut_[i] = 0;
+
+ protocolTotal_.eventCached_[i] = 0;
+ protocolTotal_.eventCount_[i] = 0;
+ protocolTotal_.eventBitsIn_[i] = 0;
+ protocolTotal_.eventBitsOut_[i] = 0;
+ }
+
+ protocolPartial_.cupsCount_ = 0;
+ protocolPartial_.cupsBitsIn_ = 0;
+ protocolPartial_.cupsBitsOut_ = 0;
+
+ protocolPartial_.smbCount_ = 0;
+ protocolPartial_.smbBitsIn_ = 0;
+ protocolPartial_.smbBitsOut_ = 0;
+
+ protocolPartial_.mediaCount_ = 0;
+ protocolPartial_.mediaBitsIn_ = 0;
+ protocolPartial_.mediaBitsOut_ = 0;
+
+ protocolPartial_.httpCount_ = 0;
+ protocolPartial_.httpBitsIn_ = 0;
+ protocolPartial_.httpBitsOut_ = 0;
+
+ protocolPartial_.fontCount_ = 0;
+ protocolPartial_.fontBitsIn_ = 0;
+ protocolPartial_.fontBitsOut_ = 0;
+
+ protocolPartial_.slaveCount_ = 0;
+ protocolPartial_.slaveBitsIn_ = 0;
+ protocolPartial_.slaveBitsOut_ = 0;
+
+ protocolTotal_.cupsCount_ = 0;
+ protocolTotal_.cupsBitsIn_ = 0;
+ protocolTotal_.cupsBitsOut_ = 0;
+
+ protocolTotal_.smbCount_ = 0;
+ protocolTotal_.smbBitsIn_ = 0;
+ protocolTotal_.smbBitsOut_ = 0;
+
+ protocolTotal_.mediaCount_ = 0;
+ protocolTotal_.mediaBitsIn_ = 0;
+ protocolTotal_.mediaBitsOut_ = 0;
+
+ protocolTotal_.httpCount_ = 0;
+ protocolTotal_.httpBitsIn_ = 0;
+ protocolTotal_.httpBitsOut_ = 0;
+
+ protocolTotal_.fontCount_ = 0;
+ protocolTotal_.fontBitsIn_ = 0;
+ protocolTotal_.fontBitsOut_ = 0;
+
+ protocolTotal_.slaveCount_ = 0;
+ protocolTotal_.slaveBitsIn_ = 0;
+ protocolTotal_.slaveBitsOut_ = 0;
+
+ packedPartial_.packedBytesIn_ = 0;
+ packedPartial_.packedBytesOut_ = 0;
+
+ packedTotal_.packedBytesIn_ = 0;
+ packedTotal_.packedBytesOut_ = 0;
+
+ splitPartial_.splitCount_ = 0;
+ splitPartial_.splitAborted_ = 0;
+ splitPartial_.splitAbortedBytesOut_ = 0;
+
+ splitTotal_.splitCount_ = 0;
+ splitTotal_.splitAborted_ = 0;
+ splitTotal_.splitAbortedBytesOut_ = 0;
+
+ overallPartial_.overallBytesIn_ = 0;
+ overallPartial_.overallBytesOut_ = 0;
+
+ overallTotal_.overallBytesIn_ = 0;
+ overallTotal_.overallBytesOut_ = 0;
+
+ proxyData_.protocolCount_ = 0;
+ proxyData_.controlCount_ = 0;
+ proxyData_.splitCount_ = 0;
+ proxyData_.dataCount_ = 0;
+
+ proxyData_.streamRatio_ = 1;
+
+ startShortFrameTs_ = getTimestamp();
+ startLongFrameTs_ = getTimestamp();
+ startFrameTs_ = getTimestamp();
+
+ bytesInShortFrame_ = 0;
+ bytesInLongFrame_ = 0;
+
+ bitrateInShortFrame_ = 0;
+ bitrateInLongFrame_ = 0;
+
+ topBitrate_ = 0;
+
+ congestionInFrame_ = 0;
+}
+
+Statistics::~Statistics()
+{
+}
+
+int Statistics::resetPartialStats()
+{
+ transportPartial_.idleTime_ = 0;
+ transportPartial_.readTime_ = 0;
+ transportPartial_.writeTime_ = 0;
+ transportPartial_.proxyBytesIn_ = 0;
+ transportPartial_.proxyBytesOut_ = 0;
+ transportPartial_.proxyFramesIn_ = 0;
+ transportPartial_.proxyFramesOut_ = 0;
+ transportPartial_.proxyWritesOut_ = 0;
+ transportPartial_.compressedBytesIn_ = 0;
+ transportPartial_.compressedBytesOut_ = 0;
+ transportPartial_.decompressedBytesIn_ = 0;
+ transportPartial_.decompressedBytesOut_ = 0;
+ transportPartial_.framingBitsOut_ = 0;
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ protocolPartial_.requestCached_[i] = 0;
+ protocolPartial_.requestReplied_[i] = 0;
+ protocolPartial_.requestCount_[i] = 0;
+ protocolPartial_.requestBitsIn_[i] = 0;
+ protocolPartial_.requestBitsOut_[i] = 0;
+
+ protocolPartial_.renderRequestCached_[i] = 0;
+ protocolPartial_.renderRequestCount_[i] = 0;
+ protocolPartial_.renderRequestBitsIn_[i] = 0;
+ protocolPartial_.renderRequestBitsOut_[i] = 0;
+
+ protocolPartial_.replyCached_[i] = 0;
+ protocolPartial_.replyCount_[i] = 0;
+ protocolPartial_.replyBitsIn_[i] = 0;
+ protocolPartial_.replyBitsOut_[i] = 0;
+
+ protocolPartial_.eventCached_[i] = 0;
+ protocolPartial_.eventCount_[i] = 0;
+ protocolPartial_.eventBitsIn_[i] = 0;
+ protocolPartial_.eventBitsOut_[i] = 0;
+ }
+
+ protocolPartial_.cupsCount_ = 0;
+ protocolPartial_.cupsBitsIn_ = 0;
+ protocolPartial_.cupsBitsOut_ = 0;
+
+ protocolPartial_.smbCount_ = 0;
+ protocolPartial_.smbBitsIn_ = 0;
+ protocolPartial_.smbBitsOut_ = 0;
+
+ protocolPartial_.mediaCount_ = 0;
+ protocolPartial_.mediaBitsIn_ = 0;
+ protocolPartial_.mediaBitsOut_ = 0;
+
+ protocolPartial_.httpCount_ = 0;
+ protocolPartial_.httpBitsIn_ = 0;
+ protocolPartial_.httpBitsOut_ = 0;
+
+ protocolPartial_.fontCount_ = 0;
+ protocolPartial_.fontBitsIn_ = 0;
+ protocolPartial_.fontBitsOut_ = 0;
+
+ protocolPartial_.slaveCount_ = 0;
+ protocolPartial_.slaveBitsIn_ = 0;
+ protocolPartial_.slaveBitsOut_ = 0;
+
+ packedPartial_.packedBytesIn_ = 0;
+ packedPartial_.packedBytesOut_ = 0;
+
+ splitPartial_.splitCount_ = 0;
+ splitPartial_.splitAborted_ = 0;
+ splitPartial_.splitAbortedBytesOut_ = 0;
+
+ overallPartial_.overallBytesIn_ = 0;
+ overallPartial_.overallBytesOut_ = 0;
+
+ return 1;
+}
+
+void Statistics::addCompressedBytes(unsigned int bytesIn, unsigned int bytesOut)
+{
+ transportPartial_.compressedBytesIn_ += bytesIn;
+ transportTotal_.compressedBytesIn_ += bytesIn;
+
+ transportPartial_.compressedBytesOut_ += bytesOut;
+ transportTotal_.compressedBytesOut_ += bytesOut;
+
+ double ratio = bytesIn / bytesOut;
+
+ if (ratio < 1)
+ {
+ ratio = 1;
+ }
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Old ratio was "
+ << proxyData_.streamRatio_ << " current is "
+ << (double) ratio << " new ratio is " << (double)
+ ((proxyData_.streamRatio_ * 2) + ratio) / 3 << ".\n"
+ << logofs_flush;
+ #endif
+
+ proxyData_.streamRatio_ = ((proxyData_.streamRatio_ * 2) + ratio) / 3;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Updated compressed bytes "
+ << "with " << bytesIn << " in " << bytesOut << " out "
+ << "and ratio " << (double) proxyData_.streamRatio_
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+//
+// Recalculate the current bitrate. The bytes written
+// are accounted at the time the transport actually
+// writes the data to the network, not at the time it
+// receives the data from the upper layers. The reason
+// is that data can be compressed by the stream com-
+// pressor, so we can become aware of the new bitrate
+// only afer having flushed the ZLIB stream. This also
+// means that, to have a reliable estimate, we need to
+// flush the link often.
+//
+
+void Statistics::updateBitrate(int bytes)
+{
+ T_timestamp thisFrameTs = getNewTimestamp();
+
+ int diffFramesInMs = diffTimestamp(startFrameTs_, thisFrameTs);
+
+ #ifdef DEBUG
+ *logofs << "Statistics: Difference since previous timestamp is "
+ << diffFramesInMs << " Ms.\n" << logofs_flush;
+ #endif
+
+ if (diffFramesInMs > 0)
+ {
+ #ifdef DEBUG
+ *logofs << "Statistics: Removing " << diffFramesInMs
+ << " Ms in short and long time frame.\n"
+ << logofs_flush;
+ #endif
+
+ int shortBytesToRemove = (int) (((double) bytesInShortFrame_ * (double) diffFramesInMs) /
+ (double) control -> ShortBitrateTimeFrame);
+
+ int longBytesToRemove = (int) (((double) bytesInLongFrame_ * (double) diffFramesInMs) /
+ (double) control -> LongBitrateTimeFrame);
+
+ #ifdef DEBUG
+ *logofs << "Statistics: Removing " << shortBytesToRemove
+ << " bytes from " << bytesInShortFrame_
+ << " in the short frame.\n" << logofs_flush;
+ #endif
+
+ bytesInShortFrame_ -= shortBytesToRemove;
+
+ if (bytesInShortFrame_ < 0)
+ {
+ #ifdef TEST
+ *logofs << "Statistics: Bytes in short frame are "
+ << bytesInShortFrame_ << ". Set to 0.\n"
+ << logofs_flush;
+ #endif
+
+ bytesInShortFrame_ = 0;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Statistics: Removing " << longBytesToRemove
+ << " bytes from " << bytesInLongFrame_
+ << " in the long frame.\n" << logofs_flush;
+ #endif
+
+ bytesInLongFrame_ -= longBytesToRemove;
+
+ if (bytesInLongFrame_ < 0)
+ {
+ #ifdef TEST
+ *logofs << "Statistics: Bytes in long frame are "
+ << bytesInLongFrame_ << ". Set to 0.\n"
+ << logofs_flush;
+ #endif
+
+ bytesInLongFrame_ = 0;
+ }
+
+ int diffStartInMs;
+
+ diffStartInMs = diffTimestamp(thisFrameTs, startShortFrameTs_);
+
+ if (diffStartInMs > control -> ShortBitrateTimeFrame)
+ {
+ addMsTimestamp(startShortFrameTs_, diffStartInMs);
+ }
+
+ diffStartInMs = diffTimestamp(thisFrameTs, startLongFrameTs_);
+
+ if (diffStartInMs > control -> LongBitrateTimeFrame)
+ {
+ addMsTimestamp(startLongFrameTs_, diffStartInMs);
+ }
+
+ startFrameTs_ = thisFrameTs;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Statistics: Adding " << bytes << " bytes to "
+ << bytesInShortFrame_ << " in the short frame.\n"
+ << logofs_flush;
+ #endif
+
+ bytesInShortFrame_ = bytesInShortFrame_ + bytes;
+
+ #ifdef DEBUG
+ *logofs << "Statistics: Adding " << bytes << " bytes to "
+ << bytesInLongFrame_ << " in the long frame.\n"
+ << logofs_flush;
+ #endif
+
+ bytesInLongFrame_ = bytesInLongFrame_ + bytes;
+
+ bitrateInShortFrame_ = (int) ((double) bytesInShortFrame_ /
+ ((double) control -> ShortBitrateTimeFrame / 1000));
+
+ bitrateInLongFrame_ = (int) ((double) bytesInLongFrame_ /
+ ((double) control -> LongBitrateTimeFrame / 1000));
+
+ if (bitrateInShortFrame_ > topBitrate_)
+ {
+ topBitrate_ = bitrateInShortFrame_;
+ }
+
+ #ifdef TEST
+ *logofs << "Statistics: Current bitrate is short " << bitrateInShortFrame_
+ << " long " << bitrateInLongFrame_ << " top " << topBitrate_
+ << ".\n" << logofs_flush;
+ #endif
+}
+
+void Statistics::updateCongestion(int remaining, int limit)
+{
+ #ifdef TEST
+ *logofs << "Statistics: Updating the congestion "
+ << "counters at " << strMsTimestamp()
+ << ".\n" << logofs_flush;
+ #endif
+
+ double current = remaining;
+
+ if (current < 0)
+ {
+ current = 0;
+ }
+
+ current = 9 * (limit - current) / limit;
+
+ #ifdef TEST
+ *logofs << "Statistics: Current congestion is "
+ << current << " with " << limit << " tokens "
+ << "and " << remaining << " remaining.\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // If the current congestion counter is greater
+ // than the previous, take the current value,
+ // otherwise ramp down the value by calculating
+ // the average of the last 8 updates.
+ //
+
+ #ifdef TEST
+ *logofs << "Statistics: Old congestion was "
+ << congestionInFrame_;
+ #endif
+
+ if (current >= congestionInFrame_)
+ {
+ congestionInFrame_ = current;
+ }
+ else
+ {
+ congestionInFrame_ = ((congestionInFrame_ * 7) + current) / 8;
+ }
+
+ #ifdef TEST
+ *logofs << " new congestion is "
+ << ((congestionInFrame_ * 7) + current) / 8
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Call the function with 0 bytes flushed
+ // so the agent can update its congestion
+ // counter.
+ //
+
+ FlushCallback(0);
+}
+
+int Statistics::getClientCacheStats(int type, char *&buffer)
+{
+ if (type != PARTIAL_STATS && type != TOTAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Statistics: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Print message cache data according
+ // to local and remote view.
+ //
+
+ MessageStore *currentStore = NULL;
+ MessageStore *anyStore = NULL;
+
+ char format[FORMAT_LENGTH];
+
+ strcat(buffer, "\nNX Cache Statistics\n");
+ strcat(buffer, "-------------------\n\n");
+
+ for (int m = proxy_client; m <= proxy_server; m++)
+ {
+ if (m == proxy_client)
+ {
+ strcat(buffer, "Request\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
+ strcat(buffer, "-------\t------\t--------------\t\t--------------\t\t-----------\n");
+ }
+ else
+ {
+ strcat(buffer, "\nReply\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
+ strcat(buffer, "-----\t------\t--------------\t\t--------------\t\t-----------\n");
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (m == proxy_client)
+ {
+ currentStore = proxy_ -> getClientStore() -> getRequestStore(i);
+ }
+ else
+ {
+ currentStore = proxy_ -> getServerStore() -> getReplyStore(i);
+ }
+
+ if (currentStore != NULL &&
+ (currentStore -> getLocalStorageSize() ||
+ currentStore -> getRemoteStorageSize()))
+ {
+ anyStore = currentStore;
+
+ sprintf(format, "#%d\t%d\t", i, currentStore -> getSize());
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getLocalStorageSize(),
+ ((double) currentStore -> getLocalStorageSize()) / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getRemoteStorageSize(),
+ ((double) currentStore -> getRemoteStorageSize()) / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d/%.0f KB\n", currentStore -> cacheSlots,
+ ((double) control -> getUpperStorageSize() / 100 *
+ currentStore -> cacheThreshold) / 1024);
+
+ strcat(buffer, format);
+ }
+ }
+
+ if (anyStore == NULL)
+ {
+ strcat(buffer, "N/A\n");
+ }
+ }
+
+ if (anyStore != NULL)
+ {
+ sprintf(format, "\ncache: %d bytes (%d KB) available at server.\n",
+ control -> ClientTotalStorageSize,
+ control -> ClientTotalStorageSize / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) available at client.\n\n",
+ control -> ServerTotalStorageSize,
+ control -> ServerTotalStorageSize / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) allocated at server.\n",
+ anyStore -> getLocalTotalStorageSize(),
+ anyStore -> getLocalTotalStorageSize() / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) allocated at client.\n\n\n",
+ anyStore -> getRemoteTotalStorageSize(),
+ anyStore -> getRemoteTotalStorageSize() / 1024);
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "\ncache: N/A\n\n");
+ }
+
+ return 1;
+}
+
+int Statistics::getClientProtocolStats(int type, char *&buffer)
+{
+ if (type != PARTIAL_STATS && type != TOTAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Statistics: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ struct T_transportData *transportData;
+ struct T_protocolData *protocolData;
+ struct T_overallData *overallData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ protocolData = &protocolPartial_;
+ overallData = &overallPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ protocolData = &protocolTotal_;
+ overallData = &overallTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ double countRequestIn = 0;
+ double countCachedRequestIn = 0;
+ double countRepliedRequestIn = 0;
+
+ double countRequestBitsIn = 0;
+ double countRequestBitsOut = 0;
+
+ double countAnyIn = 0;
+ double countBitsIn = 0;
+ double countBitsOut = 0;
+
+ //
+ // Print request data.
+ //
+
+ strcat(buffer, "NX Server Side Protocol Statistics\n");
+ strcat(buffer, "----------------------------------\n\n");
+
+ //
+ // Print render data.
+ //
+
+ strcat(buffer, "Render Total\tCached\tBits In\t\tBits Out\tBits/Request\t\tRatio\n");
+ strcat(buffer, "------- -----\t------\t-------\t\t--------\t------------\t\t-----\n");
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ if (protocolData -> renderRequestCount_[i])
+ {
+ sprintf(format, "#%d ", i);
+
+ while (strlen(format) < 8)
+ {
+ strcat(format, " ");
+ }
+
+ strcat(buffer, format);
+
+ if (protocolData -> renderRequestCached_[i] > 0)
+ {
+ sprintf(format, "%.0f\t%.0f", protocolData -> renderRequestCount_[i],
+ protocolData -> renderRequestCached_[i]);
+ }
+ else
+ {
+ sprintf(format, "%.0f\t", protocolData -> renderRequestCount_[i]);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ protocolData -> renderRequestBitsIn_[i], protocolData -> renderRequestBitsIn_[i] / 8192,
+ protocolData -> renderRequestBitsOut_[i], protocolData -> renderRequestBitsOut_[i] / 8192,
+ protocolData -> renderRequestBitsIn_[i] / protocolData -> renderRequestCount_[i],
+ protocolData -> renderRequestBitsOut_[i] / protocolData -> renderRequestCount_[i]);
+
+ strcat(buffer, format);
+
+ if (protocolData -> renderRequestBitsOut_[i] > 0)
+ {
+ sprintf(format, "%5.3f:1\n", protocolData -> renderRequestBitsIn_[i] /
+ protocolData -> renderRequestBitsOut_[i]);
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "1:1\n");
+ }
+ }
+
+ countRequestIn += protocolData -> renderRequestCount_[i];
+ countCachedRequestIn += protocolData -> renderRequestCached_[i];
+
+ countRequestBitsIn += protocolData -> renderRequestBitsIn_[i];
+ countRequestBitsOut += protocolData -> renderRequestBitsOut_[i];
+
+ countAnyIn += protocolData -> renderRequestCount_[i];
+ countBitsIn += protocolData -> renderRequestBitsIn_[i];
+ countBitsOut += protocolData -> renderRequestBitsOut_[i];
+ }
+
+ if (countRequestIn > 0)
+ {
+ if (countCachedRequestIn > 0)
+ {
+ sprintf(format, "\ntotal: %.0f\t%.0f", countRequestIn, countCachedRequestIn);
+ }
+ else
+ {
+ sprintf(format, "\ntotal: %.0f\t", countRequestIn);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ countRequestBitsIn, countRequestBitsIn / 8192, countRequestBitsOut,
+ countRequestBitsOut / 8192, countRequestBitsIn / countRequestIn,
+ countRequestBitsOut / countRequestIn);
+
+ strcat(buffer, format);
+
+ if (countRequestBitsOut > 0)
+ {
+ sprintf(format, "%5.3f:1\n", countRequestBitsIn / countRequestBitsOut);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "N/A\n\n");
+ }
+
+ countRequestIn = 0;
+ countCachedRequestIn = 0;
+
+ countRequestBitsIn = 0;
+ countRequestBitsOut = 0;
+
+ countAnyIn = 0;
+ countBitsIn = 0;
+ countBitsOut = 0;
+
+ //
+ // Print other requests' data.
+ //
+
+ strcat(buffer, "\nRequest Total\tCached\tBits In\t\tBits Out\tBits/Request\t\tRatio\n");
+ strcat(buffer, "------- -----\t------\t-------\t\t--------\t------------\t\t-----\n");
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ if (protocolData -> requestCount_[i])
+ {
+ sprintf(format, "#%d ", i);
+
+ while (strlen(format) < 5)
+ {
+ strcat(format, " ");
+ }
+
+ //
+ // Mark NX agent-related requests, those
+ // having a reply and finally those that
+ // have been probably tainted by client
+ // side.
+ //
+
+ if (i >= X_NXFirstOpcode && i <= X_NXLastOpcode)
+ {
+ strcat(format, "A");
+ }
+
+ if (i != X_NXInternalGenericReply && protocolData -> requestReplied_[i] > 0)
+ {
+ strcat(format, "R");
+ }
+
+ if (i == X_NoOperation && control -> TaintReplies)
+ {
+ strcat(format, "T");
+ }
+
+ while (strlen(format) < 8)
+ {
+ strcat(format, " ");
+ }
+
+ strcat(buffer, format);
+
+ if (protocolData -> requestCached_[i] > 0)
+ {
+ sprintf(format, "%.0f\t%.0f", protocolData -> requestCount_[i],
+ protocolData -> requestCached_[i]);
+ }
+ else
+ {
+ sprintf(format, "%.0f\t", protocolData -> requestCount_[i]);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ protocolData -> requestBitsIn_[i], protocolData -> requestBitsIn_[i] / 8192,
+ protocolData -> requestBitsOut_[i], protocolData -> requestBitsOut_[i] / 8192,
+ protocolData -> requestBitsIn_[i] / protocolData -> requestCount_[i],
+ protocolData -> requestBitsOut_[i] / protocolData -> requestCount_[i]);
+
+ strcat(buffer, format);
+
+ if (protocolData -> requestBitsOut_[i] > 0)
+ {
+ sprintf(format, "%5.3f:1\n", protocolData -> requestBitsIn_[i] /
+ protocolData -> requestBitsOut_[i]);
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "1:1\n");
+ }
+ }
+
+ countRequestIn += protocolData -> requestCount_[i];
+ countCachedRequestIn += protocolData -> requestCached_[i];
+ countRepliedRequestIn += protocolData -> requestReplied_[i];
+
+ countRequestBitsIn += protocolData -> requestBitsIn_[i];
+ countRequestBitsOut += protocolData -> requestBitsOut_[i];
+
+ countAnyIn += protocolData -> requestCount_[i];
+ countBitsIn += protocolData -> requestBitsIn_[i];
+ countBitsOut += protocolData -> requestBitsOut_[i];
+ }
+
+ if (countRequestIn > 0)
+ {
+ if (countCachedRequestIn > 0)
+ {
+ sprintf(format, "\ntotal: %.0f\t%.0f", countRequestIn, countCachedRequestIn);
+ }
+ else
+ {
+ sprintf(format, "\ntotal: %.0f\t", countRequestIn);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ countRequestBitsIn, countRequestBitsIn / 8192, countRequestBitsOut,
+ countRequestBitsOut / 8192, countRequestBitsIn / countRequestIn,
+ countRequestBitsOut / countRequestIn);
+
+ strcat(buffer, format);
+
+ if (countRequestBitsOut > 0)
+ {
+ sprintf(format, "%5.3f:1\n", countRequestBitsIn / countRequestBitsOut);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "N/A\n\n");
+ }
+
+ //
+ // Print transport data.
+ //
+
+ getTimeStats(type, buffer);
+
+ countAnyIn += protocolData -> cupsCount_;
+ countBitsIn += protocolData -> cupsBitsIn_;
+ countBitsOut += protocolData -> cupsBitsOut_;
+
+ countAnyIn += protocolData -> smbCount_;
+ countBitsIn += protocolData -> smbBitsIn_;
+ countBitsOut += protocolData -> smbBitsOut_;
+
+ countAnyIn += protocolData -> mediaCount_;
+ countBitsIn += protocolData -> mediaBitsIn_;
+ countBitsOut += protocolData -> mediaBitsOut_;
+
+ countAnyIn += protocolData -> httpCount_;
+ countBitsIn += protocolData -> httpBitsIn_;
+ countBitsOut += protocolData -> httpBitsOut_;
+
+ countAnyIn += protocolData -> fontCount_;
+ countBitsIn += protocolData -> fontBitsIn_;
+ countBitsOut += protocolData -> fontBitsOut_;
+
+ countAnyIn += protocolData -> slaveCount_;
+ countBitsIn += protocolData -> slaveBitsIn_;
+ countBitsOut += protocolData -> slaveBitsOut_;
+
+ //
+ // Save the overall amount of bytes
+ // coming from X clients.
+ //
+
+ overallData -> overallBytesIn_ = countBitsIn / 8;
+
+ //
+ // Print performance data.
+ //
+
+ if (transportData -> readTime_ > 0)
+ {
+ sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n",
+ countAnyIn / (transportData -> readTime_ / 1000),
+ (countBitsIn + transportData -> framingBitsOut_) / 8192 /
+ (transportData -> readTime_ / 1000));
+ }
+ else
+ {
+ sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n",
+ countAnyIn, (countBitsIn + transportData ->
+ framingBitsOut_) / 8192);
+ }
+
+ strcat(buffer, format);
+
+ strcat(buffer, "link: ");
+
+ //
+ // ZLIB compression stats.
+ //
+
+ getStreamStats(type, buffer);
+
+ //
+ // Save the overall amount of bytes
+ // sent on NX proxy link.
+ //
+
+ if (transportData -> compressedBytesOut_ > 0)
+ {
+ overallData -> overallBytesOut_ = transportData -> compressedBytesOut_;
+ }
+ else
+ {
+ overallData -> overallBytesOut_ = countBitsOut / 8;
+ }
+
+ //
+ // Print info on multiplexing overhead.
+ //
+
+ getFramingStats(type, buffer);
+
+ //
+ // Print stats about additional channels.
+ //
+
+ getServicesStats(type, buffer);
+
+ //
+ // Compression summary.
+ //
+
+ double ratio = 1;
+
+ if (transportData -> compressedBytesOut_ / 1024 > 0)
+ {
+ ratio = ((countBitsIn + transportData -> framingBitsOut_) / 8192) /
+ (transportData -> compressedBytesOut_ / 1024);
+
+ }
+ else if (countBitsOut > 0)
+ {
+ ratio = (countBitsIn + transportData -> framingBitsOut_) /
+ countBitsOut;
+ }
+
+ sprintf(format, " Protocol compression ratio is %5.3f:1.\n\n",
+ ratio);
+
+ strcat(buffer, format);
+
+ getBitrateStats(type, buffer);
+
+ getSplitStats(type, buffer);
+
+ sprintf(format, " %.0f total handled replies (%.0f unmatched).\n\n\n",
+ countRepliedRequestIn, protocolData -> requestReplied_[X_NXInternalGenericReply]);
+
+ strcat(buffer, format);
+
+ return 1;
+}
+
+int Statistics::getClientOverallStats(int type, char *&buffer)
+{
+ if (type != PARTIAL_STATS && type != TOTAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Statistics: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ struct T_overallData *overallData;
+ struct T_packedData *packedData;
+
+ if (type == PARTIAL_STATS)
+ {
+ overallData = &overallPartial_;
+ packedData = &packedPartial_;
+ }
+ else
+ {
+ overallData = &overallTotal_;
+ packedData = &packedTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ //
+ // Print header including link type,
+ // followed by info on packed images.
+ //
+
+ strcat(buffer, "NX Compression Summary\n");
+ strcat(buffer, "----------------------\n\n");
+
+ char label[FORMAT_LENGTH];
+
+ switch (control -> LinkMode)
+ {
+ case LINK_TYPE_NONE:
+ {
+ strcpy(label, "NONE");
+
+ break;
+ }
+ case LINK_TYPE_MODEM:
+ {
+ strcpy(label, "MODEM");
+
+ break;
+ }
+ case LINK_TYPE_ISDN:
+ {
+ strcpy(label, "ISDN");
+
+ break;
+ }
+ case LINK_TYPE_ADSL:
+ {
+ strcpy(label, "ADSL");
+
+ break;
+ }
+ case LINK_TYPE_WAN:
+ {
+ strcpy(label, "WAN");
+
+ break;
+ }
+ case LINK_TYPE_LAN:
+ {
+ strcpy(label, "LAN");
+
+ break;
+ }
+ default:
+ {
+ strcpy(label, "Unknown");
+
+ break;
+ }
+ }
+
+ sprintf(format, "link: %s", label);
+
+ if (control -> LocalDeltaCompression == 1)
+ {
+ strcat(format, " with protocol compression enabled.");
+ }
+ else
+ {
+ strcat(format, " with protocol compression disabled.");
+ }
+
+ strcat(format, "\n\n");
+
+ strcat(buffer, format);
+
+ if (packedData -> packedBytesIn_ > 0)
+ {
+ sprintf(format, "images: %.0f bytes (%.0f KB) packed to %.0f (%.0f KB).\n\n",
+ packedData -> packedBytesOut_, packedData -> packedBytesOut_ / 1024,
+ packedData -> packedBytesIn_, packedData -> packedBytesIn_ / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " Images compression ratio is %5.3f:1.\n\n",
+ packedData -> packedBytesOut_ / packedData -> packedBytesIn_);
+
+ strcat(buffer, format);
+ }
+
+ double overallIn = overallData -> overallBytesIn_ - packedData -> packedBytesIn_ +
+ packedData -> packedBytesOut_;
+
+ double overallOut = overallData -> overallBytesOut_;
+
+ sprintf(format, "overall: %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ overallIn, overallIn / 1024, overallOut, overallOut / 1024);
+
+ strcat(buffer, format);
+
+ if (overallData -> overallBytesOut_ > 0)
+ {
+ sprintf(format, " Overall NX server compression ratio is %5.3f:1.\n\n\n",
+ overallIn / overallOut);
+ }
+ else
+ {
+ sprintf(format, " Overall NX server compression ratio is 1:1.\n\n\n");
+ }
+
+ strcat(buffer, format);
+
+ return 1;
+}
+
+int Statistics::getServerCacheStats(int type, char *&buffer)
+{
+ if (type != PARTIAL_STATS && type != TOTAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Statistics: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Print message cache data according
+ // to local and remote view.
+ //
+
+ MessageStore *currentStore = NULL;
+ MessageStore *anyStore = NULL;
+
+ char format[FORMAT_LENGTH];
+
+ strcat(buffer, "\nNX Cache Statistics\n");
+ strcat(buffer, "-------------------\n\n");
+
+ for (int m = proxy_client; m <= proxy_server; m++)
+ {
+ if (m == proxy_client)
+ {
+ strcat(buffer, "Request\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
+ strcat(buffer, "-------\t------\t--------------\t\t--------------\t\t-----------\n");
+ }
+ else
+ {
+ strcat(buffer, "\nReply\tCached\tSize at Server\t\tSize at Client\t\tCache limit\n");
+ strcat(buffer, "-----\t------\t--------------\t\t--------------\t\t-----------\n");
+ }
+
+ for (int i = 0; i < CHANNEL_STORE_OPCODE_LIMIT; i++)
+ {
+ if (m == proxy_client)
+ {
+ currentStore = proxy_ -> getClientStore() -> getRequestStore(i);
+ }
+ else
+ {
+ currentStore = proxy_ -> getServerStore() -> getReplyStore(i);
+ }
+
+ if (currentStore != NULL &&
+ (currentStore -> getLocalStorageSize() ||
+ currentStore -> getRemoteStorageSize()))
+ {
+ anyStore = currentStore;
+
+ sprintf(format, "#%d\t%d\t", i, currentStore -> getSize());
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getRemoteStorageSize(),
+ ((double) currentStore -> getRemoteStorageSize()) / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d (%.0f KB)\t\t", currentStore -> getLocalStorageSize(),
+ ((double) currentStore -> getLocalStorageSize()) / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, "%d/%.0f KB\n", currentStore -> cacheSlots,
+ ((double) control -> getUpperStorageSize() / 100 *
+ currentStore -> cacheThreshold) / 1024);
+
+ strcat(buffer, format);
+ }
+ }
+
+ if (anyStore == NULL)
+ {
+ strcat(buffer, "N/A\n");
+ }
+ }
+
+ if (anyStore != NULL)
+ {
+ sprintf(format, "\ncache: %d bytes (%d KB) available at server.\n",
+ control -> ClientTotalStorageSize,
+ control -> ClientTotalStorageSize / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) available at client.\n\n",
+ control -> ServerTotalStorageSize,
+ control -> ServerTotalStorageSize / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) allocated at server.\n",
+ anyStore -> getRemoteTotalStorageSize(),
+ anyStore -> getRemoteTotalStorageSize() / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %d bytes (%d KB) allocated at client.\n\n\n",
+ anyStore -> getLocalTotalStorageSize(),
+ anyStore -> getLocalTotalStorageSize() / 1024);
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "\ncache: N/A\n\n");
+ }
+
+ return 1;
+}
+
+int Statistics::getServerProtocolStats(int type, char *&buffer)
+{
+ if (type != PARTIAL_STATS && type != TOTAL_STATS)
+ {
+ #ifdef PANIC
+ *logofs << "Statistics: PANIC! Cannot produce statistics "
+ << "with qualifier '" << type << "'.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ struct T_transportData *transportData;
+ struct T_protocolData *protocolData;
+ struct T_overallData *overallData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ protocolData = &protocolPartial_;
+ overallData = &overallPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ protocolData = &protocolTotal_;
+ overallData = &overallTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ double countReplyBitsIn = 0;
+ double countReplyBitsOut = 0;
+
+ double countReplyIn = 0;
+ double countCachedReplyIn = 0;
+
+ double countEventBitsIn = 0;
+ double countEventBitsOut = 0;
+
+ double countEventIn = 0;
+ double countCachedEventIn = 0;
+
+ double countAnyIn = 0;
+ double countBitsIn = 0;
+ double countBitsOut = 0;
+
+ //
+ // Print reply data.
+ //
+
+ strcat(buffer, "NX Client Side Protocol Statistics\n");
+ strcat(buffer, "----------------------------------\n\n");
+
+ strcat(buffer, "Reply Total\tCached\tBits In\t\tBits Out\tBits/Reply\t\tRatio\n");
+ strcat(buffer, "------- -----\t------\t-------\t\t--------\t----------\t\t-----\n");
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ if (protocolData -> replyCount_[i])
+ {
+ sprintf(format, "#%d ", i);
+
+ while (strlen(format) < 5)
+ {
+ strcat(format, " ");
+ }
+
+ //
+ // Mark replies originated
+ // by NX agent requests.
+ //
+
+ if (i >= X_NXFirstOpcode && i <= X_NXLastOpcode)
+ {
+ strcat(format, "A");
+ }
+
+ //
+ // Mark replies that we didn't
+ // match against a request.
+ //
+
+ if (i == 1)
+ {
+ strcat(format, "U");
+ }
+
+ while (strlen(format) < 8)
+ {
+ strcat(format, " ");
+ }
+
+ strcat(buffer, format);
+
+ if (protocolData -> replyCached_[i] > 0)
+ {
+ sprintf(format, "%.0f\t%.0f", protocolData -> replyCount_[i],
+ protocolData -> replyCached_[i]);
+ }
+ else
+ {
+ sprintf(format, "%.0f\t", protocolData -> replyCount_[i]);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ protocolData -> replyBitsIn_[i], protocolData -> replyBitsIn_[i] / 8192,
+ protocolData -> replyBitsOut_[i], protocolData -> replyBitsOut_[i] / 8192,
+ protocolData -> replyBitsIn_[i] / protocolData -> replyCount_[i],
+ protocolData -> replyBitsOut_[i] / protocolData -> replyCount_[i]);
+
+ strcat(buffer, format);
+
+ if (protocolData -> replyBitsOut_[i] > 0)
+ {
+ sprintf(format, "%5.3f:1\n", protocolData -> replyBitsIn_[i] /
+ protocolData -> replyBitsOut_[i]);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+
+ countReplyIn += protocolData -> replyCount_[i];
+ countCachedReplyIn += protocolData -> replyCached_[i];
+
+ countReplyBitsIn += protocolData -> replyBitsIn_[i];
+ countReplyBitsOut += protocolData -> replyBitsOut_[i];
+
+ countAnyIn += protocolData -> replyCount_[i];
+ countBitsIn += protocolData -> replyBitsIn_[i];
+ countBitsOut += protocolData -> replyBitsOut_[i];
+ }
+
+ if (countReplyIn > 0)
+ {
+ if (countCachedReplyIn > 0)
+ {
+ sprintf(format, "\ntotal: %.0f\t%.0f", countReplyIn, countCachedReplyIn);
+ }
+ else
+ {
+ sprintf(format, "\ntotal: %.0f\t", countReplyIn);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ countReplyBitsIn, countReplyBitsIn / 8192, countReplyBitsOut,
+ countReplyBitsOut / 8192, countReplyBitsIn / countReplyIn,
+ countReplyBitsOut / countReplyIn);
+
+ strcat(buffer, format);
+
+ if (countReplyBitsOut > 0)
+ {
+ sprintf(format, "%5.3f:1\n", countReplyBitsIn / countReplyBitsOut);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "N/A\n");
+ }
+
+ strcat(buffer, "\n");
+
+ //
+ // Print event and error data.
+ //
+
+ strcat(buffer, "Event Total\tCached\tBits In\t\tBits Out\tBits/Event\t\tRatio\n");
+ strcat(buffer, "------- -----\t------\t-------\t\t--------\t----------\t\t-----\n");
+
+ for (int i = 0; i < STATISTICS_OPCODE_MAX; i++)
+ {
+ if (protocolData -> eventCount_[i])
+ {
+ sprintf(format, "#%d ", i);
+
+ while (strlen(format) < 8)
+ {
+ strcat(format, " ");
+ }
+
+ strcat(buffer, format);
+
+ if (protocolData -> eventCached_[i] > 0)
+ {
+ sprintf(format, "%.0f\t%.0f", protocolData -> eventCount_[i],
+ protocolData -> eventCached_[i]);
+ }
+ else
+ {
+ sprintf(format, "%.0f\t", protocolData -> eventCount_[i]);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ protocolData -> eventBitsIn_[i], protocolData -> eventBitsIn_[i] / 8192,
+ protocolData -> eventBitsOut_[i], protocolData -> eventBitsOut_[i] / 8192,
+ protocolData -> eventBitsIn_[i] / protocolData -> eventCount_[i],
+ protocolData -> eventBitsOut_[i] / protocolData -> eventCount_[i]);
+
+ strcat(buffer, format);
+
+ if (protocolData -> eventBitsOut_[i] > 0)
+ {
+ sprintf(format, "%5.3f:1\n", protocolData -> eventBitsIn_[i] /
+ protocolData -> eventBitsOut_[i]);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+
+ countEventIn += protocolData -> eventCount_[i];
+ countCachedEventIn += protocolData -> eventCached_[i];
+
+ countEventBitsIn += protocolData -> eventBitsIn_[i];
+ countEventBitsOut += protocolData -> eventBitsOut_[i];
+
+ countAnyIn += protocolData -> eventCount_[i];
+ countBitsIn += protocolData -> eventBitsIn_[i];
+ countBitsOut += protocolData -> eventBitsOut_[i];
+ }
+
+ if (countEventIn > 0)
+ {
+ if (countCachedEventIn > 0)
+ {
+ sprintf(format, "\ntotal: %.0f\t%.0f", countEventIn, countCachedEventIn);
+ }
+ else
+ {
+ sprintf(format, "\ntotal: %.0f\t", countEventIn);
+ }
+
+ strcat(buffer, format);
+
+ sprintf(format, "\t%.0f (%.0f KB)\t%.0f (%.0f KB)\t%.0f/1 -> %.0f/1 \t",
+ countEventBitsIn, countEventBitsIn / 8192, countEventBitsOut,
+ countEventBitsOut / 8192, countEventBitsIn / countEventIn,
+ countEventBitsOut / countEventIn);
+
+ strcat(buffer, format);
+
+ if (countEventBitsOut > 0)
+ {
+ sprintf(format, "%5.3f:1\n", countEventBitsIn / countEventBitsOut);
+ }
+ else
+ {
+ sprintf(format, "1:1\n");
+ }
+
+ strcat(buffer, format);
+ }
+ else
+ {
+ strcat(buffer, "N/A\n\n");
+ }
+
+ //
+ // Print transport data.
+ //
+
+ getTimeStats(type, buffer);
+
+ countAnyIn += protocolData -> cupsCount_;
+ countBitsIn += protocolData -> cupsBitsIn_;
+ countBitsOut += protocolData -> cupsBitsOut_;
+
+ countAnyIn += protocolData -> smbCount_;
+ countBitsIn += protocolData -> smbBitsIn_;
+ countBitsOut += protocolData -> smbBitsOut_;
+
+ countAnyIn += protocolData -> mediaCount_;
+ countBitsIn += protocolData -> mediaBitsIn_;
+ countBitsOut += protocolData -> mediaBitsOut_;
+
+ countAnyIn += protocolData -> httpCount_;
+ countBitsIn += protocolData -> httpBitsIn_;
+ countBitsOut += protocolData -> httpBitsOut_;
+
+ countAnyIn += protocolData -> fontCount_;
+ countBitsIn += protocolData -> fontBitsIn_;
+ countBitsOut += protocolData -> fontBitsOut_;
+
+ countAnyIn += protocolData -> slaveCount_;
+ countBitsIn += protocolData -> slaveBitsIn_;
+ countBitsOut += protocolData -> slaveBitsOut_;
+
+ //
+ // Save the overall amount of bytes
+ // coming from X clients.
+ //
+
+ overallData -> overallBytesIn_ = countBitsIn / 8;
+
+ //
+ // Print performance data.
+ //
+
+ if (transportData -> readTime_ > 0)
+ {
+ sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n",
+ countAnyIn / (transportData -> readTime_ / 1000),
+ (countBitsIn + transportData -> framingBitsOut_) / 8192 /
+ (transportData -> readTime_ / 1000));
+ }
+ else
+ {
+ sprintf(format, " %.0f messages (%.0f KB) encoded per second.\n\n",
+ countAnyIn, (countBitsIn + transportData ->
+ framingBitsOut_) / 8192);
+ }
+
+ strcat(buffer, format);
+
+ strcat(buffer, "link: ");
+
+ //
+ // ZLIB compression stats.
+ //
+
+ getStreamStats(type, buffer);
+
+ //
+ // Save the overall amount of bytes
+ // sent on NX proxy link.
+ //
+
+ if (transportData -> compressedBytesOut_ > 0)
+ {
+ overallData -> overallBytesOut_ = transportData -> compressedBytesOut_;
+ }
+ else
+ {
+ overallData -> overallBytesOut_ = countBitsOut / 8;
+ }
+
+ //
+ // Print info on multiplexing overhead.
+ //
+
+ getFramingStats(type, buffer);
+
+ //
+ // Print stats about additional channels.
+ //
+
+ getServicesStats(type, buffer);
+
+ //
+ // Compression summary.
+ //
+
+ double ratio = 1;
+
+ if (transportData -> compressedBytesOut_ / 1024 > 0)
+ {
+ ratio = ((countBitsIn + transportData -> framingBitsOut_) / 8192) /
+ (transportData -> compressedBytesOut_ / 1024);
+
+ }
+ else if (countBitsOut > 0)
+ {
+ ratio = (countBitsIn + transportData -> framingBitsOut_) /
+ countBitsOut;
+ }
+
+ sprintf(format, " Protocol compression ratio is %5.3f:1.\n\n",
+ ratio);
+
+ strcat(buffer, format);
+
+ getBitrateStats(type, buffer);
+
+ //
+ // These are not included in output.
+ //
+ // getSplitStats(type, buffer);
+ //
+
+ strcat(buffer, "\n");
+
+ //
+ // These statistics are not included in output.
+ // You can check it anyway to get the effective
+ // amount of bytes produced by unpack procedure.
+ //
+ // getClientOverallStats(type, buffer);
+ //
+
+ return 1;
+}
+
+int Statistics::getServerOverallStats(int type, char *&buffer)
+{
+ return 1;
+}
+
+int Statistics::getTimeStats(int type, char *&buffer)
+{
+ struct T_transportData *transportData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ sprintf(format, "\ntime: %.0f Ms idle, %.0f Ms (%.0f Ms in read, %.0f Ms in write) running.\n\n",
+ transportData -> idleTime_, transportData -> readTime_,
+ transportData -> readTime_ - transportData -> writeTime_,
+ transportData -> writeTime_);
+
+ strcat(buffer, format);
+
+ return 1;
+}
+
+int Statistics::getStreamStats(int type, char *&buffer)
+{
+ struct T_transportData *transportData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ if (transportData -> compressedBytesOut_ > 0)
+ {
+ sprintf(format, "%.0f bytes (%.0f KB) compressed to %.0f (%.0f KB).\n",
+ transportData -> compressedBytesIn_, transportData -> compressedBytesIn_ / 1024,
+ transportData -> compressedBytesOut_, transportData -> compressedBytesOut_ / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %5.3f:1 stream compression ratio.\n\n",
+ transportData -> compressedBytesIn_ / transportData -> compressedBytesOut_);
+
+ strcat(buffer, format);
+ }
+
+ if (transportData -> decompressedBytesOut_ > 0)
+ {
+ if (transportData -> compressedBytesOut_ > 0)
+ {
+ strcat(buffer, " ");
+ }
+
+ sprintf(format, "%.0f bytes (%.0f KB) decompressed to %.0f (%.0f KB).\n",
+ transportData -> decompressedBytesIn_, transportData -> decompressedBytesIn_ / 1024,
+ transportData -> decompressedBytesOut_, transportData -> decompressedBytesOut_ / 1024);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %5.3f:1 stream compression ratio.\n\n",
+ transportData -> decompressedBytesOut_ / transportData -> decompressedBytesIn_);
+
+ strcat(buffer, format);
+ }
+
+ if (transportData -> compressedBytesOut_ > 0 ||
+ transportData -> decompressedBytesOut_ > 0)
+ {
+ strcat(buffer, " ");
+ }
+
+ return 1;
+}
+
+int Statistics::getServicesStats(int type, char *&buffer)
+{
+ struct T_protocolData *protocolData;
+
+ if (type == PARTIAL_STATS)
+ {
+ protocolData = &protocolPartial_;
+ }
+ else
+ {
+ protocolData = &protocolTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ if (protocolData -> cupsBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f CUPS messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> cupsCount_ , protocolData -> cupsBitsIn_ / 8,
+ protocolData -> cupsBitsIn_ / 8192, protocolData -> cupsBitsOut_ / 8,
+ protocolData -> cupsBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ if (protocolData -> smbBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f SMB messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> smbCount_ , protocolData -> smbBitsIn_ / 8,
+ protocolData -> smbBitsIn_ / 8192, protocolData -> smbBitsOut_ / 8,
+ protocolData -> smbBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ if (protocolData -> mediaBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f multimedia messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> mediaCount_ , protocolData -> mediaBitsIn_ / 8,
+ protocolData -> mediaBitsIn_ / 8192, protocolData -> mediaBitsOut_ / 8,
+ protocolData -> mediaBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ if (protocolData -> httpBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f HTTP messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> httpCount_ , protocolData -> httpBitsIn_ / 8,
+ protocolData -> httpBitsIn_ / 8192, protocolData -> httpBitsOut_ / 8,
+ protocolData -> httpBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ if (protocolData -> fontBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f font server messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> fontCount_ , protocolData -> fontBitsIn_ / 8,
+ protocolData -> fontBitsIn_ / 8192, protocolData -> fontBitsOut_ / 8,
+ protocolData -> fontBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ if (protocolData -> slaveBitsOut_ > 0)
+ {
+ sprintf(format, " %.0f slave messages, %.0f bytes (%.0f KB) in, %.0f bytes (%.0f KB) out.\n\n",
+ protocolData -> slaveCount_ , protocolData -> slaveBitsIn_ / 8,
+ protocolData -> slaveBitsIn_ / 8192, protocolData -> slaveBitsOut_ / 8,
+ protocolData -> slaveBitsOut_ / 8192);
+
+ strcat(buffer, format);
+ }
+
+ return 1;
+}
+
+int Statistics::getFramingStats(int type, char *&buffer)
+{
+ struct T_transportData *transportData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ //
+ // Print info on multiplexing overhead.
+ //
+
+ sprintf(format, "%.0f frames in, %.0f frames out, %.0f writes out.\n\n",
+ transportData -> proxyFramesIn_, transportData -> proxyFramesOut_,
+ transportData -> proxyWritesOut_);
+
+ strcat(buffer, format);
+
+ sprintf(format, " %.0f bytes (%.0f KB) used for framing and multiplexing.\n\n",
+ transportData -> framingBitsOut_ / 8, transportData -> framingBitsOut_ / 8192);
+
+ strcat(buffer, format);
+
+ return 1;
+}
+
+int Statistics::getBitrateStats(int type, char *&buffer)
+{
+ struct T_transportData *transportData;
+ struct T_overallData *overallData;
+
+ if (type == PARTIAL_STATS)
+ {
+ transportData = &transportPartial_;
+ overallData = &overallPartial_;
+ }
+ else
+ {
+ transportData = &transportTotal_;
+ overallData = &overallTotal_;
+ }
+
+ double total = 0;
+
+ if (transportData -> idleTime_ + transportData -> readTime_ > 0)
+ {
+ total = overallData -> overallBytesOut_ /
+ ((transportData -> idleTime_ + transportData -> readTime_) / 1000);
+ }
+
+ char format[FORMAT_LENGTH];
+
+ sprintf(format, " %.0f B/s average, %d B/s %ds, %d B/s %ds, %d B/s maximum.\n\n",
+ total, getBitrateInShortFrame(), control -> ShortBitrateTimeFrame / 1000,
+ getBitrateInLongFrame(), control -> LongBitrateTimeFrame / 1000,
+ getTopBitrate());
+
+ strcat(buffer, format);
+
+ resetTopBitrate();
+
+ return 1;
+}
+
+int Statistics::getSplitStats(int type, char *&buffer)
+{
+ //
+ // Don't print these statistics if persistent
+ // cache of images is disabled.
+ //
+
+ if (control -> ImageCacheEnableLoad == 0 &&
+ control -> ImageCacheEnableSave == 0)
+ {
+ return 0;
+ }
+
+ struct T_splitData *splitData;
+
+ if (type == PARTIAL_STATS)
+ {
+ splitData = &splitPartial_;
+ }
+ else
+ {
+ splitData = &splitTotal_;
+ }
+
+ char format[FORMAT_LENGTH];
+
+ //
+ // Print info on split messages restored from disk.
+ //
+
+ sprintf(format, " %.0f images streamed, %.0f restored, %.0f bytes (%.0f KB) cached.\n\n",
+ splitData -> splitCount_, splitData -> splitAborted_, splitData -> splitAbortedBytesOut_,
+ splitData -> splitAbortedBytesOut_ / 1024);
+
+ strcat(buffer, format);
+
+ return 1;
+}
diff --git a/nxcomp/src/Statistics.h b/nxcomp/src/Statistics.h
new file mode 100644
index 000000000..1ffb6b5d6
--- /dev/null
+++ b/nxcomp/src/Statistics.h
@@ -0,0 +1,745 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Statistics_H
+#define Statistics_H
+
+#include "NXproto.h"
+
+#include "Misc.h"
+#include "Timestamp.h"
+
+class Proxy;
+
+//
+// Opcode 255 is for generic requests, 1 is for
+// generic replies (those which haven't a speci-
+// fic differential encoding), opcode 0 is for
+// generic messages from the auxiliary channels.
+//
+
+#define STATISTICS_OPCODE_MAX 256
+
+//
+// Maximum length of the buffer allocated for
+// the statistics output.
+//
+
+#define STATISTICS_LENGTH 16384
+
+//
+// Query type.
+//
+
+#define TOTAL_STATS 1
+#define PARTIAL_STATS 2
+#define NO_STATS 3
+
+//
+// Log level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Log the operations related to updating
+// the control tokens.
+//
+
+#undef TOKEN
+
+class Statistics
+{
+ public:
+
+ //
+ // Use the proxy class to get access
+ // to the message stores.
+ //
+
+ Statistics(Proxy *proxy);
+
+ ~Statistics();
+
+ void addIdleTime(unsigned int numMs)
+ {
+ transportPartial_.idleTime_ += numMs;
+ transportTotal_.idleTime_ += numMs;
+ }
+
+ void subIdleTime(unsigned int numMs)
+ {
+ transportPartial_.idleTime_ -= numMs;
+ transportTotal_.idleTime_ -= numMs;
+ }
+
+ void addReadTime(unsigned int numMs)
+ {
+ transportPartial_.readTime_ += numMs;
+ transportTotal_.readTime_ += numMs;
+ }
+
+ void subReadTime(unsigned int numMs)
+ {
+ transportPartial_.readTime_ -= numMs;
+ transportTotal_.readTime_ -= numMs;
+ }
+
+ void addWriteTime(unsigned int numMs)
+ {
+ transportPartial_.writeTime_ += numMs;
+ transportTotal_.writeTime_ += numMs;
+ }
+
+ void subWriteTime(unsigned int numMs)
+ {
+ transportPartial_.writeTime_ -= numMs;
+ transportTotal_.writeTime_ -= numMs;
+ }
+
+ void addBytesIn(unsigned int numBytes)
+ {
+ transportPartial_.proxyBytesIn_ += numBytes;
+ transportTotal_.proxyBytesIn_ += numBytes;
+ }
+
+ double getBytesIn()
+ {
+ return transportTotal_.proxyBytesIn_;
+ }
+
+ void addBytesOut(unsigned int numBytes)
+ {
+ transportPartial_.proxyBytesOut_ += numBytes;
+ transportTotal_.proxyBytesOut_ += numBytes;
+ }
+
+ double getBytesOut()
+ {
+ return transportTotal_.proxyBytesOut_;
+ }
+
+ void addFrameIn()
+ {
+ transportPartial_.proxyFramesIn_++;
+ transportTotal_.proxyFramesIn_++;
+
+ #ifdef TEST
+ *logofs << "Statistics: Updated total proxy frames in to "
+ << transportTotal_.proxyFramesIn_ << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+ }
+
+ void addFrameOut()
+ {
+ transportPartial_.proxyFramesOut_++;
+ transportTotal_.proxyFramesOut_++;
+
+ #ifdef TEST
+ *logofs << "Statistics: Updated total proxy frames out to "
+ << transportTotal_.proxyFramesOut_ << " at "
+ << strMsTimestamp() << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ void addWriteOut()
+ {
+ transportPartial_.proxyWritesOut_++;
+ transportTotal_.proxyWritesOut_++;
+
+ #ifdef TEST
+ *logofs << "Statistics: Updated total proxy writes out to "
+ << transportTotal_.proxyWritesOut_ << " at "
+ << strMsTimestamp() << ".\n" << logofs_flush;
+ #endif
+ }
+
+ void addCompressedBytes(unsigned int bytesIn, unsigned int bytesOut);
+
+ void addDecompressedBytes(unsigned int bytesIn, unsigned int bytesOut)
+ {
+ transportPartial_.decompressedBytesIn_ += bytesIn;
+ transportTotal_.decompressedBytesIn_ += bytesIn;
+
+ transportPartial_.decompressedBytesOut_ += bytesOut;
+ transportTotal_.decompressedBytesOut_ += bytesOut;
+ }
+
+ void addFramingBits(unsigned int bitsOut)
+ {
+ transportPartial_.framingBitsOut_ += bitsOut;
+ transportTotal_.framingBitsOut_ += bitsOut;
+
+ proxyData_.protocolCount_ += bitsOut;
+ }
+
+ void addCachedRequest(unsigned int opcode)
+ {
+ protocolPartial_.requestCached_[opcode]++;
+ protocolTotal_.requestCached_[opcode]++;
+ }
+
+ void addRenderCachedRequest(unsigned int opcode)
+ {
+ protocolPartial_.renderRequestCached_[opcode]++;
+ protocolTotal_.renderRequestCached_[opcode]++;
+ }
+
+ void addRepliedRequest(unsigned int opcode)
+ {
+ protocolPartial_.requestReplied_[opcode]++;
+ protocolTotal_.requestReplied_[opcode]++;
+ }
+
+ void addCachedReply(unsigned int opcode)
+ {
+ protocolPartial_.replyCached_[opcode]++;
+ protocolTotal_.replyCached_[opcode]++;
+ }
+
+ void addRequestBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut)
+ {
+ #ifdef DEBUG
+ *logofs << "Statistcs: Added " << bitsIn << " bits in and "
+ << bitsOut << " bits out to opcode " << opcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ protocolPartial_.requestCount_[opcode]++;
+ protocolTotal_.requestCount_[opcode]++;
+
+ protocolPartial_.requestBitsIn_[opcode] += bitsIn;
+ protocolTotal_.requestBitsIn_[opcode] += bitsIn;
+
+ protocolPartial_.requestBitsOut_[opcode] += bitsOut;
+ protocolTotal_.requestBitsOut_[opcode] += bitsOut;
+
+ //
+ // Don't account the split bits
+ // to the control token.
+ //
+
+ if (opcode != X_NXSplitData && opcode != X_NXSplitEvent)
+ {
+ proxyData_.protocolCount_ += bitsOut;
+ }
+ }
+
+ void addRenderRequestBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut)
+ {
+ #ifdef DEBUG
+ *logofs << "Statistcs: Added " << bitsIn << " bits in and "
+ << bitsOut << " bits out to render opcode " << opcode
+ << ".\n" << logofs_flush;
+ #endif
+
+ protocolPartial_.renderRequestCount_[opcode]++;
+ protocolTotal_.renderRequestCount_[opcode]++;
+
+ protocolPartial_.renderRequestBitsIn_[opcode] += bitsIn;
+ protocolTotal_.renderRequestBitsIn_[opcode] += bitsIn;
+
+ protocolPartial_.renderRequestBitsOut_[opcode] += bitsOut;
+ protocolTotal_.renderRequestBitsOut_[opcode] += bitsOut;
+ }
+
+ void addReplyBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.replyCount_[opcode]++;
+ protocolTotal_.replyCount_[opcode]++;
+
+ protocolPartial_.replyBitsIn_[opcode] += bitsIn;
+ protocolTotal_.replyBitsIn_[opcode] += bitsIn;
+
+ protocolPartial_.replyBitsOut_[opcode] += bitsOut;
+ protocolTotal_.replyBitsOut_[opcode] += bitsOut;
+
+ proxyData_.protocolCount_ += bitsOut;
+ }
+
+ void addEventBits(unsigned int opcode, unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.eventCount_[opcode]++;
+ protocolTotal_.eventCount_[opcode]++;
+
+ protocolPartial_.eventBitsIn_[opcode] += bitsIn;
+ protocolTotal_.eventBitsIn_[opcode] += bitsIn;
+
+ protocolPartial_.eventBitsOut_[opcode] += bitsOut;
+ protocolTotal_.eventBitsOut_[opcode] += bitsOut;
+
+ proxyData_.protocolCount_ += bitsOut;
+ }
+
+ void addCupsBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.cupsCount_++;
+ protocolTotal_.cupsCount_++;
+
+ protocolPartial_.cupsBitsIn_ += bitsIn;
+ protocolTotal_.cupsBitsIn_ += bitsIn;
+
+ protocolPartial_.cupsBitsOut_ += bitsOut;
+ protocolTotal_.cupsBitsOut_ += bitsOut;
+ }
+
+ void addSmbBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.smbCount_++;
+ protocolTotal_.smbCount_++;
+
+ protocolPartial_.smbBitsIn_ += bitsIn;
+ protocolTotal_.smbBitsIn_ += bitsIn;
+
+ protocolPartial_.smbBitsOut_ += bitsOut;
+ protocolTotal_.smbBitsOut_ += bitsOut;
+ }
+
+ void addMediaBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.mediaCount_++;
+ protocolTotal_.mediaCount_++;
+
+ protocolPartial_.mediaBitsIn_ += bitsIn;
+ protocolTotal_.mediaBitsIn_ += bitsIn;
+
+ protocolPartial_.mediaBitsOut_ += bitsOut;
+ protocolTotal_.mediaBitsOut_ += bitsOut;
+ }
+
+ void addHttpBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.httpCount_++;
+ protocolTotal_.httpCount_++;
+
+ protocolPartial_.httpBitsIn_ += bitsIn;
+ protocolTotal_.httpBitsIn_ += bitsIn;
+
+ protocolPartial_.httpBitsOut_ += bitsOut;
+ protocolTotal_.httpBitsOut_ += bitsOut;
+ }
+
+ void addFontBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.fontCount_++;
+ protocolTotal_.fontCount_++;
+
+ protocolPartial_.fontBitsIn_ += bitsIn;
+ protocolTotal_.fontBitsIn_ += bitsIn;
+
+ protocolPartial_.fontBitsOut_ += bitsOut;
+ protocolTotal_.fontBitsOut_ += bitsOut;
+ }
+
+ void addSlaveBits(unsigned int bitsIn, unsigned int bitsOut)
+ {
+ protocolPartial_.slaveCount_++;
+ protocolTotal_.slaveCount_++;
+
+ protocolPartial_.slaveBitsIn_ += bitsIn;
+ protocolTotal_.slaveBitsIn_ += bitsIn;
+
+ protocolPartial_.slaveBitsOut_ += bitsOut;
+ protocolTotal_.slaveBitsOut_ += bitsOut;
+ }
+
+ void addPackedBytesIn(unsigned int numBytes)
+ {
+ packedPartial_.packedBytesIn_ += numBytes;
+ packedTotal_.packedBytesIn_ += numBytes;
+ }
+
+ void addPackedBytesOut(unsigned int numBytes)
+ {
+ packedPartial_.packedBytesOut_ += numBytes;
+ packedTotal_.packedBytesOut_ += numBytes;
+ }
+
+ void addSplit()
+ {
+ splitPartial_.splitCount_++;
+ splitTotal_.splitCount_++;
+ }
+
+ void addSplitAborted()
+ {
+ splitPartial_.splitAborted_++;
+ splitTotal_.splitAborted_++;
+ }
+
+ void addSplitAbortedBytesOut(unsigned int numBytes)
+ {
+ splitPartial_.splitAbortedBytesOut_ += numBytes;
+ splitTotal_.splitAbortedBytesOut_ += numBytes;
+ }
+
+ //
+ // Add the recorded protocol data to the proxy
+ // token counters. We want to send bpth the token
+ // request message and the data payload using a
+ // single system write, so we must guess how many
+ // output bytes we will generate.
+ //
+
+ void updateControlToken(int &count)
+ {
+ //
+ // Total is the total number of protocol bytes
+ // generated so far. We have saved the number
+ // of bytes generated the last time the function
+ // was called so we can calculate the difference.
+ //
+ // The number of protocol bits is updated as soon
+ // as new bits are accumulated, to avoid summing
+ // up all the opcodes in this routine. We add a
+ // byte to the control bytes difference to account
+ // for the framing bits that are very likely to
+ // be added to the payload.
+ //
+
+ double total = proxyData_.protocolCount_ / 8;
+
+ double diff = total - proxyData_.controlCount_ + 1;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Protocol bytes are "
+ << total << " control bytes are "
+ << proxyData_.controlCount_ << " difference is "
+ << diff << ".\n" << logofs_flush;
+ #endif
+
+ count += (int) (diff / proxyData_.streamRatio_);
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Adding "
+ << (int) (diff / proxyData_.streamRatio_)
+ << " bytes to the control token with ratio "
+ << proxyData_.streamRatio_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ proxyData_.controlCount_ = total;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! New control token has "
+ << count << " bytes with total control bytes "
+ << proxyData_.controlCount_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ void updateSplitToken(int &count)
+ {
+ double total = (protocolTotal_.requestBitsOut_[X_NXSplitData] +
+ protocolTotal_.eventBitsOut_[X_NXSplitEvent]) / 8;
+
+ double diff = total - proxyData_.splitCount_;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Protocol bytes are "
+ << total << " split bytes are "
+ << proxyData_.splitCount_ << " difference is "
+ << diff << ".\n" << logofs_flush;
+ #endif
+
+ count += (int) (diff / proxyData_.streamRatio_);
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Adding "
+ << (int) (diff / proxyData_.streamRatio_)
+ << " bytes to the split token with ratio "
+ << proxyData_.streamRatio_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ proxyData_.splitCount_ = total;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! New split token has "
+ << count << " bytes with total split bytes "
+ << proxyData_.splitCount_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ void updateDataToken(int &count)
+ {
+ double total = (protocolTotal_.cupsBitsOut_ +
+ protocolTotal_.smbBitsOut_ +
+ protocolTotal_.mediaBitsOut_ +
+ protocolTotal_.httpBitsOut_ +
+ protocolTotal_.fontBitsOut_ +
+ protocolTotal_.slaveBitsOut_) / 8;
+
+ double diff = total - proxyData_.dataCount_;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Protocol bytes are "
+ << total << " data bytes are "
+ << proxyData_.dataCount_ << " difference is "
+ << diff << ".\n" << logofs_flush;
+ #endif
+
+ count += (int) (diff / proxyData_.streamRatio_);
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! Adding "
+ << (int) (diff / proxyData_.streamRatio_)
+ << " bytes to the data token with ratio "
+ << proxyData_.streamRatio_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ proxyData_.dataCount_ = total;
+
+ #if defined(TEST) || defined(TOKEN)
+ *logofs << "Statistics: TOKEN! New data token has "
+ << count << " bytes with total data bytes "
+ << proxyData_.dataCount_ << ".\n"
+ << logofs_flush;
+ #endif
+ }
+
+ //
+ // Update the current bitrate.
+ //
+
+ void updateBitrate(int bytes);
+
+ //
+ // Return the current bitrate.
+ //
+
+ int getBitrateInShortFrame()
+ {
+ return bitrateInShortFrame_;
+ }
+
+ int getBitrateInLongFrame()
+ {
+ return bitrateInLongFrame_;
+ }
+
+ int getTopBitrate()
+ {
+ return topBitrate_;
+ }
+
+ void resetTopBitrate()
+ {
+ topBitrate_ = 0;
+ }
+
+ double getStreamRatio()
+ {
+ return proxyData_.streamRatio_;
+ }
+
+ //
+ // Manage the congestion level.
+ //
+
+ void updateCongestion(int remaining, int limit);
+
+ double getCongestionInFrame()
+ {
+ return congestionInFrame_;
+ }
+
+ //
+ // Produce a dump of the statistics on
+ // the provided buffer.
+ //
+
+ int getClientCacheStats(int type, char *&buffer);
+ int getClientProtocolStats(int type, char *&buffer);
+ int getClientOverallStats(int type, char *&buffer);
+
+ int getServerCacheStats(int type, char *&buffer);
+ int getServerProtocolStats(int type, char *&buffer);
+ int getServerOverallStats(int type, char *&buffer);
+
+ int resetPartialStats();
+
+ private:
+
+ int getTimeStats(int type, char *&buffer);
+ int getServicesStats(int type, char *&buffer);
+ int getFramingStats(int type, char *&buffer);
+ int getBitrateStats(int type, char *&buffer);
+ int getStreamStats(int type, char *&buffer);
+ int getSplitStats(int type, char *&buffer);
+
+ struct T_protocolData
+ {
+ double requestCached_[STATISTICS_OPCODE_MAX];
+ double requestReplied_[STATISTICS_OPCODE_MAX];
+ double requestCount_[STATISTICS_OPCODE_MAX];
+ double requestBitsIn_[STATISTICS_OPCODE_MAX];
+ double requestBitsOut_[STATISTICS_OPCODE_MAX];
+
+ double renderRequestCached_[STATISTICS_OPCODE_MAX];
+ double renderRequestCount_[STATISTICS_OPCODE_MAX];
+ double renderRequestBitsIn_[STATISTICS_OPCODE_MAX];
+ double renderRequestBitsOut_[STATISTICS_OPCODE_MAX];
+
+ double replyCached_[STATISTICS_OPCODE_MAX];
+ double replyCount_[STATISTICS_OPCODE_MAX];
+ double replyBitsIn_[STATISTICS_OPCODE_MAX];
+ double replyBitsOut_[STATISTICS_OPCODE_MAX];
+
+ double eventCached_[STATISTICS_OPCODE_MAX];
+ double eventCount_[STATISTICS_OPCODE_MAX];
+ double eventBitsIn_[STATISTICS_OPCODE_MAX];
+ double eventBitsOut_[STATISTICS_OPCODE_MAX];
+
+ double cupsCount_;
+ double cupsBitsIn_;
+ double cupsBitsOut_;
+
+ double smbCount_;
+ double smbBitsIn_;
+ double smbBitsOut_;
+
+ double mediaCount_;
+ double mediaBitsIn_;
+ double mediaBitsOut_;
+
+ double httpCount_;
+ double httpBitsIn_;
+ double httpBitsOut_;
+
+ double fontCount_;
+ double fontBitsIn_;
+ double fontBitsOut_;
+
+ double slaveCount_;
+ double slaveBitsIn_;
+ double slaveBitsOut_;
+ };
+
+ struct T_transportData
+ {
+ double idleTime_;
+ double readTime_;
+ double writeTime_;
+
+ double proxyBytesIn_;
+ double proxyBytesOut_;
+
+ double proxyFramesIn_;
+ double proxyFramesOut_;
+ double proxyWritesOut_;
+
+ double compressedBytesIn_;
+ double compressedBytesOut_;
+
+ double decompressedBytesIn_;
+ double decompressedBytesOut_;
+
+ double framingBitsOut_;
+ };
+
+ struct T_packedData
+ {
+ double packedBytesIn_;
+ double packedBytesOut_;
+ };
+
+ struct T_splitData
+ {
+ double splitCount_;
+ double splitAborted_;
+ double splitAbortedBytesOut_;
+ };
+
+ struct T_overallData
+ {
+ double overallBytesIn_;
+ double overallBytesOut_;
+ };
+
+ struct T_proxyData
+ {
+ double protocolCount_;
+ double controlCount_;
+ double splitCount_;
+ double dataCount_;
+
+ double streamRatio_;
+ };
+
+ T_protocolData protocolPartial_;
+ T_protocolData protocolTotal_;
+
+ T_transportData transportPartial_;
+ T_transportData transportTotal_;
+
+ T_packedData packedPartial_;
+ T_packedData packedTotal_;
+
+ T_splitData splitPartial_;
+ T_splitData splitTotal_;
+
+ T_overallData overallPartial_;
+ T_overallData overallTotal_;
+
+ T_proxyData proxyData_;
+
+ //
+ // Used to calculate the bandwidth usage
+ // of the proxy link.
+ //
+
+ T_timestamp startShortFrameTs_;
+ T_timestamp startLongFrameTs_;
+ T_timestamp startFrameTs_;
+
+ int bytesInShortFrame_;
+ int bytesInLongFrame_;
+
+ int bitrateInShortFrame_;
+ int bitrateInLongFrame_;
+
+ int topBitrate_;
+
+ double congestionInFrame_;
+
+ //
+ // Need the proxy pointer to print the
+ // statistics related to the client and
+ // server stores and to add the protocol
+ // data to the proxy token accumulators.
+ //
+
+ Proxy *proxy_;
+};
+
+#endif /* Statistics_H */
diff --git a/nxcomp/src/Timestamp.cpp b/nxcomp/src/Timestamp.cpp
new file mode 100644
index 000000000..e7e0c494a
--- /dev/null
+++ b/nxcomp/src/Timestamp.cpp
@@ -0,0 +1,77 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Timestamp.h"
+
+//
+// Log level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Last timestamp taken from the system.
+//
+
+T_timestamp timestamp;
+
+//
+// The following functions all use the ctime
+// static buffer from the C library.
+//
+
+char *strTimestamp(const T_timestamp &ts)
+{
+ char *ctime_now = ctime((time_t *) &ts.tv_sec);
+
+ ctime_now[24] = '\0';
+
+ return ctime_now;
+}
+
+//
+// This is especially dirty.
+//
+
+char *strMsTimestamp(const T_timestamp &ts)
+{
+ char *ctime_now = ctime((time_t *) &ts.tv_sec);
+
+ char ctime_new[25];
+
+ sprintf(ctime_new, "%.8s:%3.3f", ctime_now + 11,
+ (float) ts.tv_usec / 1000);
+
+ strncpy(ctime_now, ctime_new, 24);
+
+ return ctime_now;
+}
diff --git a/nxcomp/src/Timestamp.h b/nxcomp/src/Timestamp.h
new file mode 100644
index 000000000..604f5a3bc
--- /dev/null
+++ b/nxcomp/src/Timestamp.h
@@ -0,0 +1,307 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Timestamp_H
+#define Timestamp_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <time.h>
+#include <sys/time.h>
+
+#include "Misc.h"
+
+//
+// Log level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// If not defined, always query the system time.
+//
+
+#undef CACHE_TIMESTAMP
+
+//
+// Log a warning if the time difference since
+// the last update exceeds the given number
+// of milliseconds.
+//
+
+#define DRIFT_TIMESTAMP 1
+
+//
+// Type used for timeout manipulation.
+//
+
+typedef struct timeval T_timestamp;
+
+//
+// Last timestamp taken from the system. If the
+// timestamp is cached, we need to explicitly
+// get a new timestamp after any operation that
+// may have required a relevant amount of time.
+//
+
+extern T_timestamp timestamp;
+
+//
+// Get a timestamp instance with values set
+// at the given amount of milliseconds.
+//
+
+inline T_timestamp getTimestamp(long ms)
+{
+ struct timeval ts;
+
+ ts.tv_sec = ms / 1000;
+ ts.tv_usec = (ms % 1000) * 1000;
+
+ return ts;
+}
+
+//
+// Return the difference in milliseconds
+// between the two timestamps.
+//
+
+inline long diffTimestamp(const T_timestamp &ts1, const T_timestamp &ts2)
+{
+ //
+ // Add 500 microseconds to round up
+ // to the nearest millisecond.
+ //
+
+ return ((ts2.tv_sec * 1000 + (ts2.tv_usec + 500) / 1000) -
+ (ts1.tv_sec * 1000 + (ts1.tv_usec + 500) / 1000));
+}
+
+//
+// The same in microseconds. It doesn't
+// round the value.
+//
+
+inline long diffUsTimestamp(const T_timestamp &ts1, const T_timestamp &ts2)
+{
+ return ((ts2.tv_sec * 1000000 + ts2.tv_usec) -
+ (ts1.tv_sec * 1000000 + ts1.tv_usec));
+}
+
+//
+// Return the last timestamp taken from the
+// system. It doesn't update the timestamp.
+//
+
+inline T_timestamp getTimestamp()
+{
+ #ifdef CACHE_TIMESTAMP
+
+ #ifdef TEST
+
+ T_timestamp ts;
+
+ gettimeofday(&ts, NULL);
+
+ long diffTs = diffTimestamp(timestamp, ts);
+
+ if (diffTs > DRIFT_TIMESTAMP)
+ {
+ *logofs << "Timestamp: WARNING! Time difference since the "
+ << "current timestamp is " << diffTs << " Ms.\n"
+ << logofs_flush;
+ }
+
+ #endif
+
+ return timestamp;
+
+ #else
+
+ gettimeofday(&timestamp, 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(&timestamp, NULL);
+
+ return timestamp;
+}
+
+#endif /* Timestamp_H */
diff --git a/nxcomp/src/TranslateCoords.cpp b/nxcomp/src/TranslateCoords.cpp
new file mode 100644
index 000000000..f61caea47
--- /dev/null
+++ b/nxcomp/src/TranslateCoords.cpp
@@ -0,0 +1,115 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "TranslateCoords.h"
+
+#include "ClientCache.h"
+
+#include "EncodeBuffer.h"
+#include "DecodeBuffer.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Here are the methods to handle messages' content.
+//
+
+int TranslateCoordsStore::parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message;
+
+ //
+ // Here is the fingerprint.
+ //
+
+ translateCoords -> src_window = GetULONG(buffer + 4, bigEndian);
+ translateCoords -> dst_window = GetULONG(buffer + 8, bigEndian);
+
+ translateCoords -> src_x = GetUINT(buffer + 12, bigEndian);
+ translateCoords -> src_y = GetUINT(buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Parsed Identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+int TranslateCoordsStore::unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message;
+
+ //
+ // Fill all the message's fields.
+ //
+
+ PutULONG(translateCoords -> src_window, buffer + 4, bigEndian);
+ PutULONG(translateCoords -> dst_window, buffer + 8, bigEndian);
+
+ PutUINT(translateCoords -> src_x, buffer + 12, bigEndian);
+ PutUINT(translateCoords -> src_y, buffer + 14, bigEndian);
+
+ #ifdef DEBUG
+ *logofs << name() << ": Unparsed identity for message at " << this << ".\n" << logofs_flush;
+ #endif
+
+ return 1;
+}
+
+void TranslateCoordsStore::dumpIdentity(const Message *message) const
+{
+ #ifdef DUMP
+
+ TranslateCoordsMessage *translateCoords = (TranslateCoordsMessage *) message;
+
+ *logofs << name() << ": Identity src_window " << translateCoords -> src_window << ", dst_window "
+ << translateCoords -> dst_window << ", src_x " << translateCoords -> src_x << ", src_y "
+ << translateCoords -> src_y << ", size " << translateCoords -> size_ << ".\n" << logofs_flush;
+
+ #endif
+}
+
+void TranslateCoordsStore::identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const
+{
+ md5_append(md5_state_, buffer + 4, 4);
+ md5_append(md5_state_, buffer + 8, 4);
+ md5_append(md5_state_, buffer + 12, 2);
+ md5_append(md5_state_, buffer + 14, 2);
+}
diff --git a/nxcomp/src/TranslateCoords.h b/nxcomp/src/TranslateCoords.h
new file mode 100644
index 000000000..997d079e1
--- /dev/null
+++ b/nxcomp/src/TranslateCoords.h
@@ -0,0 +1,185 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef TranslateCoords_H
+#define TranslateCoords_H
+
+#include "Message.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef DUMP
+
+//
+// Set default values.
+//
+
+#define TRANSLATECOORDS_ENABLE_CACHE 1
+#define TRANSLATECOORDS_ENABLE_DATA 0
+#define TRANSLATECOORDS_ENABLE_SPLIT 0
+#define TRANSLATECOORDS_ENABLE_COMPRESS 0
+
+#define TRANSLATECOORDS_DATA_LIMIT 0
+#define TRANSLATECOORDS_DATA_OFFSET 16
+
+#define TRANSLATECOORDS_CACHE_SLOTS 3000
+#define TRANSLATECOORDS_CACHE_THRESHOLD 3
+#define TRANSLATECOORDS_CACHE_LOWER_THRESHOLD 1
+
+//
+// The message class.
+//
+
+class TranslateCoordsMessage : public Message
+{
+ friend class TranslateCoordsStore;
+
+ public:
+
+ TranslateCoordsMessage()
+ {
+ }
+
+ ~TranslateCoordsMessage()
+ {
+ }
+
+ //
+ // Put here the fields which constitute
+ // the 'identity' part of the message.
+ //
+
+ private:
+
+ unsigned int src_window;
+ unsigned int dst_window;
+ unsigned int src_x;
+ unsigned int src_y;
+
+ unsigned char r_same_screen;
+ unsigned int r_child_window;
+ unsigned int r_dst_x;
+ unsigned int r_dst_y;
+};
+
+class TranslateCoordsStore : public MessageStore
+{
+ //
+ // Constructors and destructors.
+ //
+
+ public:
+
+ TranslateCoordsStore() : MessageStore()
+ {
+ enableCache = TRANSLATECOORDS_ENABLE_CACHE;
+ enableData = TRANSLATECOORDS_ENABLE_DATA;
+ enableSplit = TRANSLATECOORDS_ENABLE_SPLIT;
+ enableCompress = TRANSLATECOORDS_ENABLE_COMPRESS;
+
+ dataLimit = TRANSLATECOORDS_DATA_LIMIT;
+ dataOffset = TRANSLATECOORDS_DATA_OFFSET;
+
+ cacheSlots = TRANSLATECOORDS_CACHE_SLOTS;
+ cacheThreshold = TRANSLATECOORDS_CACHE_THRESHOLD;
+ cacheLowerThreshold = TRANSLATECOORDS_CACHE_LOWER_THRESHOLD;
+
+ messages_ -> resize(cacheSlots);
+
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ *i = NULL;
+ }
+
+ temporary_ = NULL;
+ }
+
+ virtual ~TranslateCoordsStore()
+ {
+ for (T_messages::iterator i = messages_ -> begin();
+ i < messages_ -> end(); i++)
+ {
+ destroy(*i);
+ }
+
+ destroy(temporary_);
+ }
+
+ virtual const char *name() const
+ {
+ return "TranslateCoords";
+ }
+
+ virtual unsigned char opcode() const
+ {
+ return X_TranslateCoords;
+ }
+
+ virtual unsigned int storage() const
+ {
+ return sizeof(TranslateCoordsMessage);
+ }
+
+ //
+ // Message handling methods.
+ //
+
+ public:
+
+ virtual Message *create() const
+ {
+ return new TranslateCoordsMessage();
+ }
+
+ virtual Message *create(const Message &message) const
+ {
+ return new TranslateCoordsMessage((const TranslateCoordsMessage &) message);
+ }
+
+ virtual void destroy(Message *message) const
+ {
+ delete (TranslateCoordsMessage *) message;
+ }
+
+ virtual int parseIdentity(Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual int unparseIdentity(const Message *message, unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void identityChecksum(const Message *message, const unsigned char *buffer,
+ unsigned int size, int bigEndian) const;
+
+ virtual void dumpIdentity(const Message *message) const;
+};
+
+#endif /* TranslateCoords_H */
diff --git a/nxcomp/src/Transport.cpp b/nxcomp/src/Transport.cpp
new file mode 100644
index 000000000..897bf32f1
--- /dev/null
+++ b/nxcomp/src/Transport.cpp
@@ -0,0 +1,3068 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include "Transport.h"
+
+#include "Statistics.h"
+
+//
+// Set the verbosity level. You also
+// need to define DUMP in Misc.cpp
+// if DUMP is defined here.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+#undef INSPECT
+#undef DUMP
+
+//
+// Used to lock and unlock the transport
+// buffers before they are accessed by
+// different threads.
+//
+
+#undef THREADS
+
+//
+// Define this to get logging all the
+// operations performed by the parent
+// thread, the one that enqueues and
+// dequeues data.
+//
+
+#define PARENT
+
+//
+// Define this to know when a channel
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Reference count for allocated buffers.
+//
+
+#ifdef REFERENCES
+
+int Transport::references_;
+int ProxyTransport::references_;
+int InternalTransport::references_;
+
+#endif
+
+//
+// This is the base class providing methods for read
+// and write buffering.
+//
+
+Transport::Transport(int fd) : fd_(fd)
+{
+ #ifdef TEST
+ *logofs << "Transport: Going to create base transport "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ type_ = transport_base;
+
+ //
+ // Set up the write buffer.
+ //
+
+ w_buffer_.length_ = 0;
+ w_buffer_.start_ = 0;
+
+ initialSize_ = TRANSPORT_BUFFER_DEFAULT_SIZE;
+ thresholdSize_ = TRANSPORT_BUFFER_DEFAULT_SIZE << 1;
+ maximumSize_ = TRANSPORT_BUFFER_DEFAULT_SIZE << 4;
+
+ w_buffer_.data_.resize(initialSize_);
+
+ //
+ // Set non-blocking IO on socket.
+ //
+
+ SetNonBlocking(fd_, 1);
+
+ blocked_ = 0;
+ finish_ = 0;
+
+ #ifdef REFERENCES
+ *logofs << "Transport: Created new object at "
+ << this << " out of " << ++references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+Transport::~Transport()
+{
+ #ifdef TEST
+ *logofs << "Transport: Going to destroy base class "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ ::close(fd_);
+
+ #ifdef REFERENCES
+ *logofs << "Transport: Deleted object at "
+ << this << " out of " << --references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+//
+// Read data from its file descriptor.
+//
+
+int Transport::read(unsigned char *data, unsigned int size)
+{
+ #ifdef DEBUG
+ *logofs << "Transport: Going to read " << size << " bytes from "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Read the available data from the socket.
+ //
+
+ int result = ::read(fd_, data, size);
+
+ //
+ // Update the current timestamp as the read
+ // can have scheduled some other process.
+ //
+
+ getNewTimestamp();
+
+ if (result < 0)
+ {
+ if (EGET() == EAGAIN)
+ {
+ #ifdef TEST
+ *logofs << "Transport: WARNING! Read of " << size << " bytes from "
+ << "FD#" << fd_ << " would block.\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Read of " << size << " bytes from "
+ << "FD#" << fd_ << " was interrupted.\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Error reading from "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+ }
+ else if (result == 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: No data read from "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+
+ #ifdef TEST
+ *logofs << "Transport: Read " << result << " bytes out of "
+ << size << " from FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef DUMP
+
+ *logofs << "Transport: Dumping content of read data.\n"
+ << logofs_flush;
+
+ DumpData(data, result);
+
+ #endif
+
+ return result;
+}
+
+//
+// Write as many bytes as possible to socket.
+// Append the remaining data bytes to the end
+// of the buffer and update length to reflect
+// changes.
+//
+
+int Transport::write(T_write type, const unsigned char *data, const unsigned int size)
+{
+ //
+ // If an immediate write was requested then
+ // flush the enqueued data first.
+ //
+ // Alternatively may try to write only if
+ // the socket is not blocked.
+ //
+ // if (w_buffer_.length_ > 0 && blocked_ == 0 &&
+ // type == write_immediate)
+ // {
+ // ...
+ // }
+ //
+
+ if (w_buffer_.length_ > 0 && type == write_immediate)
+
+ {
+ #ifdef TEST
+ *logofs << "Transport: Writing " << w_buffer_.length_
+ << " bytes of previous data to FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = Transport::flush();
+
+ if (result < 0)
+ {
+ return -1;
+ }
+ }
+
+ //
+ // If nothing is remained, write immediately
+ // to the socket.
+ //
+
+ unsigned int written = 0;
+
+ if (w_buffer_.length_ == 0 && blocked_ == 0 &&
+ type == write_immediate)
+ {
+ //
+ // Limit the amount of data sent.
+ //
+
+ unsigned int toWrite = size;
+
+ #ifdef DUMP
+
+ *logofs << "Transport: Going to write " << toWrite
+ << " bytes to FD#" << fd_ << " with checksum ";
+
+ DumpChecksum(data, size);
+
+ *logofs << ".\n" << logofs_flush;
+
+ #endif
+
+ T_timestamp writeTs;
+
+ int diffTs;
+
+ while (written < toWrite)
+ {
+ //
+ // Trace system time spent writing data.
+ //
+
+ writeTs = getTimestamp();
+
+ int result = ::write(fd_, data + written, toWrite - written);
+
+ diffTs = diffTimestamp(writeTs, getNewTimestamp());
+
+ statistics -> addWriteTime(diffTs);
+
+ if (result <= 0)
+ {
+ if (EGET() == EAGAIN)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write of " << toWrite - written
+ << " bytes on FD#" << fd_ << " would block.\n"
+ << logofs_flush;
+ #endif
+
+ blocked_ = 1;
+
+ break;
+ }
+ else if (EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write of " << toWrite - written
+ << " bytes on FD#" << fd_ << " was interrupted.\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write to " << "FD#"
+ << fd_ << " failed.\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Immediately written " << result
+ << " bytes on " << "FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ written += result;
+ }
+ }
+
+ #ifdef DUMP
+
+ if (written > 0)
+ {
+ *logofs << "Transport: Dumping content of immediately written data.\n"
+ << logofs_flush;
+
+ DumpData(data, written);
+ }
+
+ #endif
+ }
+
+ if (written == size)
+ {
+ //
+ // We will not affect the write buffer.
+ //
+
+ return written;
+ }
+
+ #ifdef DEBUG
+ *logofs << "Transport: Going to append " << size - written
+ << " bytes to write buffer for " << "FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (resize(w_buffer_, size - written) < 0)
+ {
+ return -1;
+ }
+
+ memmove(w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_,
+ data + written, size - written);
+
+ w_buffer_.length_ += size - written;
+
+ #ifdef TEST
+ *logofs << "Transport: Write buffer for FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "Transport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " size is "
+ << w_buffer_.data_.size() << " capacity is "
+ << w_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Note that this function always returns the whole
+ // size of buffer that was provided, either if not
+ // all the data could be actually written.
+ //
+
+ return size;
+}
+
+//
+// Write pending data to its file descriptor.
+//
+
+int Transport::flush()
+{
+ if (w_buffer_.length_ == 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: No data to flush on "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef WARNING
+ if (blocked_ != 0)
+ {
+ *logofs << "Transport: Blocked flag is " << blocked_
+ << " with no data to flush on FD#" << fd_
+ << ".\n" << logofs_flush;
+ }
+ #endif
+
+ return 0;
+ }
+
+ //
+ // It's time to move data from the
+ // write buffer to the real link.
+ //
+
+ int written = 0;
+
+ int toWrite = w_buffer_.length_;
+
+ //
+ // We will do our best to write any available
+ // data to the socket, so let's say we start
+ // from a clean state.
+ //
+
+ blocked_ = 0;
+
+ #ifdef TEST
+ *logofs << "Transport: Going to flush " << toWrite
+ << " bytes on FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ T_timestamp writeTs;
+
+ int diffTs;
+
+ while (written < toWrite)
+ {
+ writeTs = getTimestamp();
+
+ int result = ::write(fd_, w_buffer_.data_.begin() + w_buffer_.start_ +
+ written, toWrite - written);
+
+ diffTs = diffTimestamp(writeTs, getNewTimestamp());
+
+ statistics -> addWriteTime(diffTs);
+
+ if (result <= 0)
+ {
+ if (EGET() == EAGAIN)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write of " << toWrite - written
+ << " bytes on FD#" << fd_ << " would block.\n"
+ << logofs_flush;
+ #endif
+
+ blocked_ = 1;
+
+ break;
+ }
+ else if (EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write of " << toWrite - written
+ << " bytes on FD#" << fd_ << " was interrupted.\n"
+ << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write to " << "FD#"
+ << fd_ << " failed.\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Flushed " << result << " bytes on "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ written += result;
+ }
+ }
+
+ if (written > 0)
+ {
+ #ifdef DUMP
+
+ *logofs << "Transport: Dumping content of flushed data.\n"
+ << logofs_flush;
+
+ DumpData(w_buffer_.data_.begin() + w_buffer_.start_, written);
+
+ #endif
+
+ //
+ // Update the buffer status.
+ //
+
+ w_buffer_.length_ -= written;
+
+ if (w_buffer_.length_ == 0)
+ {
+ w_buffer_.start_ = 0;
+ }
+ else
+ {
+ w_buffer_.start_ += written;
+ }
+ }
+
+ //
+ // It can be that we wrote less bytes than
+ // available because of the write limit.
+ //
+
+ if (w_buffer_.length_ > 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: There are still " << w_buffer_.length_
+ << " bytes in write buffer for " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ blocked_ = 1;
+ }
+
+ #ifdef TEST
+ *logofs << "Transport: Write buffer for FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "Transport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " size is "
+ << w_buffer_.data_.size() << " capacity is "
+ << w_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // No new data was produced for the link except
+ // any outstanding data from previous writes.
+ //
+
+ return 0;
+}
+
+int Transport::drain(int limit, int timeout)
+{
+ if (w_buffer_.length_ <= limit)
+ {
+ return 1;
+ }
+
+ //
+ // Write the data accumulated in the write
+ // buffer until it is below the limit or
+ // the timeout is elapsed.
+ //
+
+ int toWrite = w_buffer_.length_;
+
+ int written = 0;
+
+ #ifdef TEST
+ *logofs << "Transport: Draining " << toWrite - limit
+ << " bytes on FD#" << fd_ << " with limit set to "
+ << limit << ".\n" << logofs_flush;
+ #endif
+
+ T_timestamp startTs = getNewTimestamp();
+
+ T_timestamp selectTs;
+ T_timestamp writeTs;
+ T_timestamp idleTs;
+
+ T_timestamp nowTs = startTs;
+
+ int diffTs;
+
+ fd_set writeSet;
+ fd_set readSet;
+
+ FD_ZERO(&writeSet);
+ FD_ZERO(&readSet);
+
+ int result;
+ int ready;
+
+ while (w_buffer_.length_ - written > limit)
+ {
+ nowTs = getNewTimestamp();
+
+ //
+ // Wait for descriptor to become
+ // readable or writable.
+ //
+
+ FD_SET(fd_, &writeSet);
+ FD_SET(fd_, &readSet);
+
+ setTimestamp(selectTs, timeout / 2);
+
+ idleTs = nowTs;
+
+ result = select(fd_ + 1, &readSet, &writeSet, NULL, &selectTs);
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(idleTs, nowTs);
+
+ statistics -> addIdleTime(diffTs);
+
+ statistics -> subReadTime(diffTs);
+
+ if (result < 0)
+ {
+ if (EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Select on FD#" << fd_
+ << " was interrupted.\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Select on FD#" << fd_
+ << " failed.\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+ }
+ else if (result > 0)
+ {
+ ready = result;
+
+ if (FD_ISSET(fd_, &writeSet))
+ {
+ writeTs = getNewTimestamp();
+
+ result = ::write(fd_, w_buffer_.data_.begin() + w_buffer_.start_ +
+ written, toWrite - written);
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(writeTs, nowTs);
+
+ statistics -> addWriteTime(diffTs);
+
+ if (result > 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Forced flush of " << result
+ << " bytes on " << "FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ written += result;
+ }
+ else if (result < 0 && EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write to FD#" << fd_
+ << " was interrupted.\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Write to FD#" << fd_
+ << " failed.\n" << logofs_flush;
+ #endif
+
+ finish();
+
+ return -1;
+ }
+
+ ready--;
+ }
+
+ if (ready > 0)
+ {
+ if (FD_ISSET(fd_, &readSet))
+ {
+ #ifdef TEST
+ *logofs << "Transport: Not draining further "
+ << "due to data readable on FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Transport: Timeout encountered "
+ << "waiting for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ }
+ #endif
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(startTs, nowTs);
+
+ if (diffTs >= timeout)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Not draining further "
+ << "due to the timeout on FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ break;
+ }
+ }
+
+ if (written > 0)
+ {
+ #ifdef DUMP
+
+ *logofs << "Transport: Dumping content of flushed data.\n"
+ << logofs_flush;
+
+ DumpData(w_buffer_.data_.begin() + w_buffer_.start_, written);
+
+ #endif
+
+ //
+ // Update the buffer status.
+ //
+
+ w_buffer_.length_ -= written;
+
+ if (w_buffer_.length_ == 0)
+ {
+ w_buffer_.start_ = 0;
+
+ blocked_ = 0;
+ }
+ else
+ {
+ w_buffer_.start_ += written;
+
+ #ifdef TEST
+ *logofs << "Transport: There are still " << w_buffer_.length_
+ << " bytes in write buffer for " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ blocked_ = 1;
+ }
+ }
+ #ifdef TEST
+ else
+ {
+ *logofs << "Transport: WARNING! No data written to FD#" << fd_
+ << " with " << toWrite << " bytes to drain and limit "
+ << "set to " << limit << ".\n" << logofs_flush;
+ }
+ #endif
+
+ #ifdef TEST
+ *logofs << "Transport: Write buffer for FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "Transport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " size is "
+ << w_buffer_.data_.size() << " capacity is "
+ << w_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ return (w_buffer_.length_ <= limit);
+}
+
+int Transport::wait(int timeout) const
+{
+ T_timestamp startTs = getNewTimestamp();
+
+ T_timestamp idleTs;
+ T_timestamp selectTs;
+
+ T_timestamp nowTs = startTs;
+
+ long available = 0;
+ int result = 0;
+
+ int diffTs;
+
+ fd_set readSet;
+
+ FD_ZERO(&readSet);
+ FD_SET(fd_, &readSet);
+
+ for (;;)
+ {
+ available = readable();
+
+ diffTs = diffTimestamp(startTs, nowTs);
+
+ if (available != 0 || timeout == 0 ||
+ (diffTs + (timeout / 10)) >= timeout)
+ {
+ #ifdef TEST
+ *logofs << "Transport: There are " << available
+ << " bytes on FD#" << fd_ << " after "
+ << diffTs << " Ms.\n" << logofs_flush;
+ #endif
+
+ return available;
+ }
+ else if (available == 0 && result > 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Read on " << "FD#"
+ << fd_ << " failed.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ //
+ // TODO: Should subtract the time
+ // already spent in select.
+ //
+
+ selectTs.tv_sec = 0;
+ selectTs.tv_usec = timeout * 1000;
+
+ idleTs = nowTs;
+
+ //
+ // Wait for descriptor to become readable.
+ //
+
+ result = select(fd_ + 1, &readSet, NULL, NULL, &selectTs);
+
+ nowTs = getNewTimestamp();
+
+ diffTs = diffTimestamp(idleTs, nowTs);
+
+ statistics -> addIdleTime(diffTs);
+
+ statistics -> subReadTime(diffTs);
+
+ if (result < 0)
+ {
+ if (EGET() == EINTR)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Select on FD#" << fd_
+ << " was interrupted.\n" << logofs_flush;
+ #endif
+
+ continue;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Transport: Select on " << "FD#"
+ << fd_ << " failed.\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+ #ifdef TEST
+ else if (result == 0)
+ {
+ *logofs << "Transport: No data available on FD#" << fd_
+ << " after " << diffTimestamp(startTs, nowTs)
+ << " Ms.\n" << logofs_flush;
+ }
+ else
+ {
+ *logofs << "Transport: Data became available on FD#" << fd_
+ << " after " << diffTimestamp(startTs, nowTs)
+ << " Ms.\n" << logofs_flush;
+ }
+ #endif
+ }
+}
+
+void Transport::setSize(unsigned int initialSize, unsigned int thresholdSize,
+ unsigned int maximumSize)
+{
+ initialSize_ = initialSize;
+ thresholdSize_ = thresholdSize;
+ maximumSize_ = maximumSize;
+
+ #ifdef TEST
+ *logofs << "Transport: Set buffer sizes for FD#" << fd_
+ << " to " << initialSize_ << "/" << thresholdSize_
+ << "/" << maximumSize_ << ".\n" << logofs_flush;
+ #endif
+}
+
+void Transport::fullReset()
+{
+ blocked_ = 0;
+ finish_ = 0;
+
+ fullReset(w_buffer_);
+}
+
+int Transport::resize(T_buffer &buffer, const int &size)
+{
+ if ((int) buffer.data_.size() >= (buffer.length_ + size) &&
+ (buffer.start_ + buffer.length_ + size) >
+ (int) buffer.data_.size())
+ {
+ if (buffer.length_ > 0)
+ {
+ //
+ // There is enough space in buffer but we need
+ // to move existing data at the beginning.
+ //
+
+ #ifdef TEST
+ *logofs << "Transport: Moving " << buffer.length_
+ << " bytes of data for " << "FD#" << fd_
+ << " to make room in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ memmove(buffer.data_.begin(), buffer.data_.begin() +
+ buffer.start_, buffer.length_);
+ }
+
+ buffer.start_ = 0;
+
+ #ifdef DEBUG
+ *logofs << "Transport: Made room for "
+ << buffer.data_.size() - buffer.start_
+ << " bytes in buffer for " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+ }
+ else if ((buffer.length_ + size) > (int) buffer.data_.size())
+ {
+ //
+ // Not enough space, so increase
+ // the size of the buffer.
+ //
+
+ if (buffer.start_ != 0 && buffer.length_ > 0)
+ {
+ #ifdef TEST
+ *logofs << "Transport: Moving " << buffer.length_
+ << " bytes of data for " << "FD#" << fd_
+ << " to resize the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ memmove(buffer.data_.begin(), buffer.data_.begin() +
+ buffer.start_, buffer.length_);
+ }
+
+ buffer.start_ = 0;
+
+ unsigned int newSize = thresholdSize_;
+
+ while (newSize < (unsigned int) buffer.length_ + size)
+ {
+ newSize <<= 1;
+
+ if (newSize >= maximumSize_)
+ {
+ newSize = buffer.length_ + size + initialSize_;
+ }
+ }
+
+ #ifdef DEBUG
+ *logofs << "Transport: Buffer for " << "FD#" << fd_
+ << " will be enlarged from " << buffer.data_.size()
+ << " to at least " << buffer.length_ + size
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ buffer.data_.resize(newSize);
+
+ #ifdef TEST
+ if (newSize >= maximumSize_)
+ {
+ *logofs << "Transport: WARNING! Buffer for FD#" << fd_
+ << " grown to reach size of " << newSize
+ << " bytes.\n" << logofs_flush;
+ }
+ #endif
+
+ #ifdef TEST
+ *logofs << "Transport: Data buffer for " << "FD#"
+ << fd_ << " has now size " << buffer.data_.size()
+ << " and capacity " << buffer.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ return (buffer.length_ + size);
+}
+
+void Transport::fullReset(T_buffer &buffer)
+{
+ //
+ // Force deallocation and allocation
+ // of the initial size.
+ //
+
+ #ifdef TEST
+ *logofs << "Transport: Resetting buffer for " << "FD#"
+ << fd_ << " with size " << buffer.data_.size()
+ << " and capacity " << buffer.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+
+ buffer.start_ = 0;
+ buffer.length_ = 0;
+
+ if (buffer.data_.size() > (unsigned int) initialSize_ &&
+ buffer.data_.capacity() > (unsigned int) initialSize_)
+ {
+ buffer.data_.clear();
+
+ buffer.data_.resize(initialSize_);
+
+ #ifdef TEST
+ *logofs << "Transport: Data buffer for " << "FD#"
+ << fd_ << " shrunk to size " << buffer.data_.size()
+ << " and capacity " << buffer.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+ }
+}
+
+ProxyTransport::ProxyTransport(int fd) : Transport(fd)
+{
+ #ifdef TEST
+ *logofs << "ProxyTransport: Going to create proxy transport "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ type_ = transport_proxy;
+
+ //
+ // Set up the read buffer.
+ //
+
+ r_buffer_.length_ = 0;
+ r_buffer_.start_ = 0;
+
+ r_buffer_.data_.resize(initialSize_);
+
+ //
+ // For now we own the buffer.
+ //
+
+ owner_ = 1;
+
+ //
+ // Set up ZLIB compression.
+ //
+
+ int result;
+
+ r_stream_.zalloc = NULL;
+ r_stream_.zfree = NULL;
+ r_stream_.opaque = NULL;
+
+ r_stream_.next_in = NULL;
+ r_stream_.avail_in = 0;
+
+ if ((result = inflateInit2(&r_stream_, 15)) != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Failed initialization of ZLIB read stream. "
+ << "Error is '" << zError(result) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed initialization of ZLIB read stream. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ HandleCleanup();
+ }
+
+ if (control -> LocalStreamCompression)
+ {
+ w_stream_.zalloc = NULL;
+ w_stream_.zfree = NULL;
+ w_stream_.opaque = NULL;
+
+ if ((result = deflateInit2(&w_stream_, control -> LocalStreamCompressionLevel, Z_DEFLATED,
+ 15, 9, Z_DEFAULT_STRATEGY)) != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Failed initialization of ZLIB write stream. "
+ << "Error is '" << zError(result) << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Failed initialization of ZLIB write stream. "
+ << "Error is '" << zError(result) << "'.\n";
+
+ HandleCleanup();
+ }
+ }
+
+ //
+ // No ZLIB stream to flush yet.
+ //
+
+ flush_ = 0;
+
+ #ifdef REFERENCES
+ *logofs << "ProxyTransport: Created new object at "
+ << this << " out of " << ++references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+ProxyTransport::~ProxyTransport()
+{
+ #ifdef TEST
+ *logofs << "ProxyTransport: Going to destroy derived class "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Deallocate the ZLIB stream state.
+ //
+
+ inflateEnd(&r_stream_);
+
+ if (control -> LocalStreamCompression)
+ {
+ deflateEnd(&w_stream_);
+ }
+
+ #ifdef REFERENCES
+ *logofs << "ProxyTransport: Deleted object at "
+ << this << " out of " << --references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+//
+// Read data from its file descriptor.
+//
+
+int ProxyTransport::read(unsigned char *data, unsigned int size)
+{
+ //
+ // If the remote peer is not compressing
+ // the stream then just return any byte
+ // read from the socket.
+ //
+
+ if (control -> RemoteStreamCompression == 0)
+ {
+ int result = Transport::read(data, size);
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ statistics -> addBytesIn(result);
+
+ return result;
+ }
+
+ //
+ // Return any pending data first.
+ //
+
+ if (r_buffer_.length_ > 0)
+ {
+ //
+ // If the size of the buffer doesn't
+ // match the amount of data pending,
+ // force the caller to retry.
+ //
+
+ if ((int) size < r_buffer_.length_)
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! Forcing a retry with "
+ << r_buffer_.length_ << " bytes pending and "
+ << size << " in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+
+ return -1;
+ }
+
+ int copied = (r_buffer_.length_ > ((int) size) ?
+ ((int) size) : r_buffer_.length_);
+
+ memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied);
+
+ //
+ // Update the buffer status.
+ //
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Going to immediately return " << copied
+ << " bytes from proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ r_buffer_.length_ -= copied;
+
+ if (r_buffer_.length_ == 0)
+ {
+ r_buffer_.start_ = 0;
+ }
+ else
+ {
+ r_buffer_.start_ += copied;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: There are still " << r_buffer_.length_
+ << " bytes in read buffer for proxy " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ return copied;
+ }
+
+ //
+ // Read data in the user buffer.
+ //
+
+ int result = Transport::read(data, size);
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ statistics -> addBytesIn(result);
+
+ //
+ // Decompress the data into the read
+ // buffer.
+ //
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Going to decompress data for "
+ << "proxy FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ int saveTotalIn = r_stream_.total_in;
+ int saveTotalOut = r_stream_.total_out;
+
+ int oldTotalIn = saveTotalIn;
+ int oldTotalOut = saveTotalOut;
+
+ int diffTotalIn;
+ int diffTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut
+ << ".\n" << logofs_flush;
+ #endif
+
+ r_stream_.next_in = (Bytef *) data;
+ r_stream_.avail_in = result;
+
+ //
+ // Let ZLIB use all the space already
+ // available in the buffer.
+ //
+
+ unsigned int newAvailOut = r_buffer_.data_.size() - r_buffer_.start_ -
+ r_buffer_.length_;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Initial decompress buffer is "
+ << newAvailOut << " bytes.\n" << logofs_flush;
+ #endif
+
+ for (;;)
+ {
+ #ifdef INSPECT
+ *logofs << "\nProxyTransport: Running the decompress loop.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_buffer_.length_ = " << r_buffer_.length_
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_buffer_.data_.size() = " << r_buffer_.data_.size()
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: newAvailOut = " << newAvailOut
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (resize(r_buffer_, newAvailOut) < 0)
+ {
+ return -1;
+ }
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_buffer_.data_.size() = "
+ << r_buffer_.data_.size() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.next_in = "
+ << (void *) r_stream_.next_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.avail_in = "
+ << r_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ r_stream_.next_out = r_buffer_.data_.begin() + r_buffer_.start_ +
+ r_buffer_.length_;
+
+ r_stream_.avail_out = newAvailOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.next_out = "
+ << (void *) r_stream_.next_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.avail_out = "
+ << r_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ result = inflate(&r_stream_, Z_SYNC_FLUSH);
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: Called inflate() result is "
+ << result << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.avail_in = "
+ << r_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.avail_out = "
+ << r_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.total_in = "
+ << r_stream_.total_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_stream_.total_out = "
+ << r_stream_.total_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ diffTotalIn = r_stream_.total_in - oldTotalIn;
+ diffTotalOut = r_stream_.total_out - oldTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalIn = "
+ << diffTotalIn << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalOut = "
+ << diffTotalOut << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_buffer_.length_ = "
+ << r_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ r_buffer_.length_ += diffTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: r_buffer_.length_ = "
+ << r_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ oldTotalIn = r_stream_.total_in;
+ oldTotalOut = r_stream_.total_out;
+
+ if (result == Z_OK)
+ {
+ if (r_stream_.avail_in == 0 && r_stream_.avail_out > 0)
+ {
+ break;
+ }
+ }
+ else if (result == Z_BUF_ERROR && r_stream_.avail_out > 0 &&
+ r_stream_.avail_in == 0)
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR decompressing data.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Decompression of data failed. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Decompression of data failed. Error is '"
+ << zError(result) << "'.\n";
+
+ finish();
+
+ return -1;
+ }
+
+ //
+ // Add more bytes to the buffer.
+ //
+
+ if (newAvailOut < thresholdSize_)
+ {
+ newAvailOut = thresholdSize_;
+ }
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Need to add " << newAvailOut
+ << " bytes to the decompress buffer in read.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ diffTotalIn = r_stream_.total_in - saveTotalIn;
+ diffTotalOut = r_stream_.total_out - saveTotalOut;
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Decompressed data from "
+ << diffTotalIn << " to " << diffTotalOut
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ statistics -> addDecompressedBytes(diffTotalIn, diffTotalOut);
+
+ //
+ // Check if the size of the buffer
+ // matches the produced data.
+ //
+
+ if ((int) size < r_buffer_.length_)
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! Forcing a retry with "
+ << r_buffer_.length_ << " bytes pending and "
+ << size << " in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+
+ return -1;
+ }
+
+ //
+ // Copy the decompressed data to the
+ // provided buffer.
+ //
+
+ int copied = (r_buffer_.length_ > ((int) size) ?
+ ((int) size) : r_buffer_.length_);
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Going to return " << copied
+ << " bytes from proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied);
+
+ //
+ // Update the buffer status.
+ //
+
+ r_buffer_.length_ -= copied;
+
+ if (r_buffer_.length_ == 0)
+ {
+ r_buffer_.start_ = 0;
+ }
+ else
+ {
+ r_buffer_.start_ += copied;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: There are still " << r_buffer_.length_
+ << " bytes in read buffer for proxy FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+ }
+
+ return copied;
+}
+
+//
+// If required compress data, else write it to socket.
+//
+
+int ProxyTransport::write(T_write type, const unsigned char *data, const unsigned int size)
+{
+ #ifdef TEST
+ if (size == 0)
+ {
+ *logofs << "ProxyTransport: WARNING! Write called for FD#"
+ << fd_ << " without any data to write.\n"
+ << logofs_flush;
+
+ return 0;
+ }
+ #endif
+
+ //
+ // If there is no compression revert to
+ // plain socket management.
+ //
+
+ if (control -> LocalStreamCompression == 0)
+ {
+ int result = Transport::write(type, data, size);
+
+ if (result <= 0)
+ {
+ return result;
+ }
+
+ statistics -> addBytesOut(result);
+
+ statistics -> updateBitrate(result);
+
+ FlushCallback(result);
+
+ return result;
+ }
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Going to compress " << size
+ << " bytes to write buffer for proxy FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Compress data into the write buffer.
+ //
+
+ int saveTotalIn = w_stream_.total_in;
+ int saveTotalOut = w_stream_.total_out;
+
+ int oldTotalIn = saveTotalIn;
+ int oldTotalOut = saveTotalOut;
+
+ int diffTotalIn;
+ int diffTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut
+ << ".\n" << logofs_flush;
+ #endif
+
+ w_stream_.next_in = (Bytef *) data;
+ w_stream_.avail_in = size;
+
+ //
+ // Let ZLIB use all the space already
+ // available in the buffer.
+ //
+
+ unsigned int newAvailOut = w_buffer_.data_.size() - w_buffer_.start_ -
+ w_buffer_.length_;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Initial compress buffer is "
+ << newAvailOut << " bytes.\n" << logofs_flush;
+ #endif
+
+ for (;;)
+ {
+ #ifdef INSPECT
+ *logofs << "\nProxyTransport: Running the compress loop.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.data_.size() = "
+ << w_buffer_.data_.size() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: newAvailOut = "
+ << newAvailOut << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (resize(w_buffer_, newAvailOut) < 0)
+ {
+ return -1;
+ }
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.data_.size() = "
+ << w_buffer_.data_.size() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.next_in = "
+ << (void *) w_stream_.next_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_in = "
+ << w_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ w_stream_.next_out = w_buffer_.data_.begin() + w_buffer_.start_ +
+ w_buffer_.length_;
+
+ w_stream_.avail_out = newAvailOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.next_out = "
+ << (void *) w_stream_.next_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_out = "
+ << w_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = deflate(&w_stream_, (type == write_delayed ?
+ Z_NO_FLUSH : Z_SYNC_FLUSH));
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: Called deflate() result is "
+ << result << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_in = "
+ << w_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_out = "
+ << w_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.total_in = "
+ << w_stream_.total_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.total_out = "
+ << w_stream_.total_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ diffTotalOut = w_stream_.total_out - oldTotalOut;
+ diffTotalIn = w_stream_.total_in - oldTotalIn;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalIn = "
+ << diffTotalIn << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalOut = "
+ << diffTotalOut << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ w_buffer_.length_ += diffTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ oldTotalOut = w_stream_.total_out;
+ oldTotalIn = w_stream_.total_in;
+
+ if (result == Z_OK)
+ {
+ if (w_stream_.avail_in == 0 && w_stream_.avail_out > 0)
+ {
+ break;
+ }
+ }
+ else if (result == Z_BUF_ERROR && w_stream_.avail_out > 0 &&
+ w_stream_.avail_in == 0)
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR compressing data.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Compression of data failed. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Compression of data failed. Error is '"
+ << zError(result) << "'.\n";
+
+ finish();
+
+ return -1;
+ }
+
+ //
+ // Add more bytes to the buffer.
+ //
+
+ if (newAvailOut < thresholdSize_)
+ {
+ newAvailOut = thresholdSize_;
+ }
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Need to add " << newAvailOut
+ << " bytes to the compress buffer in write.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ diffTotalIn = w_stream_.total_in - saveTotalIn;
+ diffTotalOut = w_stream_.total_out - saveTotalOut;
+
+ #ifdef TEST
+
+ *logofs << "ProxyTransport: Compressed data from "
+ << diffTotalIn << " to " << diffTotalOut
+ << " bytes.\n" << logofs_flush;
+
+ if (diffTotalIn != (int) size)
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Bytes provided to ZLIB stream "
+ << "should be " << size << " but they look to be "
+ << diffTotalIn << ".\n" << logofs_flush;
+ #endif
+ }
+
+ #endif
+
+ //
+ // Find out what we have to do with the
+ // produced data.
+ //
+
+ if (type == write_immediate)
+ {
+ //
+ // If user requested an immediate write we
+ // flushed the ZLIB buffer. We can now reset
+ // the counter and write data to socket.
+ //
+
+ flush_ = 0;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "ProxyTransport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " flush is "
+ << flush_ << " size is " << w_buffer_.data_.size()
+ << " capacity is " << w_buffer_.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Alternatively may try to write only if
+ // the socket is not blocked.
+ //
+ // if (w_buffer_.length_ > 0 && blocked_ == 0)
+ // {
+ // ...
+ // }
+ //
+
+ if (w_buffer_.length_ > 0)
+
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: Writing " << w_buffer_.length_
+ << " bytes of produced data to FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = Transport::flush();
+
+ if (result < 0)
+ {
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ //
+ // We haven't flushed the ZLIB compression
+ // buffer, so user will have to call proxy
+ // transport's flush explicitly.
+ //
+
+ flush_ += diffTotalIn;
+ }
+
+ //
+ // We either wrote the data or added it to the
+ // write buffer. It's convenient to update the
+ // counters at this stage to get the current
+ // bitrate earlier.
+ //
+
+ statistics -> addCompressedBytes(diffTotalIn, diffTotalOut);
+
+ statistics -> addBytesOut(diffTotalOut);
+
+ statistics -> updateBitrate(diffTotalOut);
+
+ FlushCallback(diffTotalOut);
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "ProxyTransport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " flush is "
+ << flush_ << " size is " << w_buffer_.data_.size()
+ << " capacity is " << w_buffer_.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+
+ return size;
+}
+
+//
+// Write data to its file descriptor.
+//
+
+int ProxyTransport::flush()
+{
+ //
+ // If there is no compression or we already compressed
+ // outgoing data and just need to write it to socket
+ // because of previous incomplete writes then revert
+ // to plain socket management.
+ //
+
+ if (flush_ == 0 || control -> LocalStreamCompression == 0)
+ {
+ int result = Transport::flush();
+
+ if (result < 0)
+ {
+ return -1;
+ }
+
+ return result;
+ }
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Going to flush compression on "
+ << "proxy FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Flush counter for proxy FD#" << fd_
+ << " is " << flush_ << " bytes.\n" << logofs_flush;
+ #endif
+
+ //
+ // Flush ZLIB stream into the write buffer.
+ //
+
+ int saveTotalIn = w_stream_.total_in;
+ int saveTotalOut = w_stream_.total_out;
+
+ int oldTotalIn = saveTotalIn;
+ int oldTotalOut = saveTotalOut;
+
+ int diffTotalOut;
+ int diffTotalIn;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalIn = " << oldTotalIn
+ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: oldTotalOut = " << oldTotalOut
+ << ".\n" << logofs_flush;
+ #endif
+
+ w_stream_.next_in = w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_;
+ w_stream_.avail_in = 0;
+
+ //
+ // Let ZLIB use all the space already
+ // available in the buffer.
+ //
+
+ unsigned int newAvailOut = w_buffer_.data_.size() - w_buffer_.start_ -
+ w_buffer_.length_;
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Initial flush buffer is "
+ << newAvailOut << " bytes.\n" << logofs_flush;
+ #endif
+
+ for (;;)
+ {
+ #ifdef INSPECT
+ *logofs << "\nProxyTransport: Running the flush loop.\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.data_.size() = "
+ << w_buffer_.data_.size() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: newAvailOut = "
+ << newAvailOut << ".\n"
+ << logofs_flush;
+ #endif
+
+ if (resize(w_buffer_, newAvailOut) < 0)
+ {
+ return -1;
+ }
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.data_.size() = "
+ << w_buffer_.data_.size() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.next_in = "
+ << (void *) w_stream_.next_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_in = "
+ << w_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ w_stream_.next_out = w_buffer_.data_.begin() + w_buffer_.start_ +
+ w_buffer_.length_;
+
+ w_stream_.avail_out = newAvailOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.next_out = "
+ << (void *) w_stream_.next_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_out = "
+ << w_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ int result = deflate(&w_stream_, Z_SYNC_FLUSH);
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: Called deflate() result is "
+ << result << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_in = "
+ << w_stream_.avail_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.avail_out = "
+ << w_stream_.avail_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.total_in = "
+ << w_stream_.total_in << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_stream_.total_out = "
+ << w_stream_.total_out << ".\n"
+ << logofs_flush;
+ #endif
+
+ diffTotalOut = w_stream_.total_out - oldTotalOut;
+ diffTotalIn = w_stream_.total_in - oldTotalIn;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalIn = "
+ << diffTotalIn << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: diffTotalOut = "
+ << diffTotalOut << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ w_buffer_.length_ += diffTotalOut;
+
+ #ifdef INSPECT
+ *logofs << "ProxyTransport: w_buffer_.length_ = "
+ << w_buffer_.length_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ oldTotalOut = w_stream_.total_out;
+ oldTotalIn = w_stream_.total_in;
+
+ if (result == Z_OK)
+ {
+ if (w_stream_.avail_in == 0 && w_stream_.avail_out > 0)
+ {
+ break;
+ }
+ }
+ else if (result == Z_BUF_ERROR && w_stream_.avail_out > 0 &&
+ w_stream_.avail_in == 0)
+ {
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! Raised Z_BUF_ERROR flushing data.\n"
+ << logofs_flush;
+ #endif
+
+ break;
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "ProxyTransport: PANIC! Flush of compressed data failed. "
+ << "Error is '" << zError(result) << "'.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Flush of compressed data failed. Error is '"
+ << zError(result) << "'.\n";
+
+ finish();
+
+ return -1;
+ }
+
+ //
+ // Add more bytes to the buffer.
+ //
+
+ if (newAvailOut < thresholdSize_)
+ {
+ newAvailOut = thresholdSize_;
+ }
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Need to add " << newAvailOut
+ << " bytes to the compress buffer in flush.\n"
+ << logofs_flush;
+ #endif
+ }
+
+ diffTotalIn = w_stream_.total_in - saveTotalIn;
+ diffTotalOut = w_stream_.total_out - saveTotalOut;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Compressed flush data from "
+ << diffTotalIn << " to " << diffTotalOut
+ << " bytes.\n" << logofs_flush;
+ #endif
+
+ //
+ // Time to move data from the write
+ // buffer to the real link.
+ //
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Reset flush counter for proxy FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ flush_ = 0;
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: Write buffer for proxy FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "ProxyTransport: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " flush is "
+ << flush_ << " size is " << w_buffer_.data_.size()
+ << " capacity is " << w_buffer_.data_.capacity()
+ << ".\n" << logofs_flush;
+ #endif
+
+ int result = Transport::flush();
+
+ if (result < 0)
+ {
+ return -1;
+ }
+
+ //
+ // Update all the counters.
+ //
+
+ statistics -> addCompressedBytes(diffTotalIn, diffTotalOut);
+
+ statistics -> addBytesOut(diffTotalOut);
+
+ statistics -> updateBitrate(diffTotalOut);
+
+ FlushCallback(diffTotalOut);
+
+ return result;
+}
+
+unsigned int ProxyTransport::getPending(unsigned char *&data)
+{
+ //
+ // Return a pointer to the data in the
+ // read buffer. It is up to the caller
+ // to ensure that the data is consumed
+ // before the read buffer is reused.
+ //
+
+ if (r_buffer_.length_ > 0)
+ {
+ unsigned int size = r_buffer_.length_;
+
+ data = r_buffer_.data_.begin() + r_buffer_.start_;
+
+ #ifdef DEBUG
+ *logofs << "ProxyTransport: Returning " << size
+ << " pending bytes from proxy FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ r_buffer_.length_ = 0;
+ r_buffer_.start_ = 0;
+
+ //
+ // Prevent the deletion of the buffer.
+ //
+
+ owner_ = 0;
+
+ return size;
+ }
+
+ #ifdef TEST
+ *logofs << "ProxyTransport: WARNING! No pending data "
+ << "for proxy FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ data = NULL;
+
+ return 0;
+}
+
+void ProxyTransport::fullReset()
+{
+ blocked_ = 0;
+ finish_ = 0;
+ flush_ = 0;
+
+ if (control -> RemoteStreamCompression)
+ {
+ inflateReset(&r_stream_);
+ }
+
+ if (control -> LocalStreamCompression)
+ {
+ deflateReset(&w_stream_);
+ }
+
+ if (owner_ == 1)
+ {
+ Transport::fullReset(r_buffer_);
+ }
+
+ Transport::fullReset(w_buffer_);
+}
+
+AgentTransport::AgentTransport(int fd) : Transport(fd)
+{
+ #ifdef TEST
+ *logofs << "AgentTransport: Going to create agent transport "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ type_ = transport_agent;
+
+ //
+ // Set up the read buffer.
+ //
+
+ r_buffer_.length_ = 0;
+ r_buffer_.start_ = 0;
+
+ r_buffer_.data_.resize(initialSize_);
+
+ //
+ // For now we own the buffer.
+ //
+
+ owner_ = 1;
+
+ //
+ // Set up the mutexes.
+ //
+
+ #ifdef THREADS
+
+ pthread_mutexattr_t m_attributes;
+
+ pthread_mutexattr_init(&m_attributes);
+
+ //
+ // Interfaces in pthread to handle mutex
+ // type do not work in current version.
+ //
+
+ m_attributes.__mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP;
+
+ if (pthread_mutex_init(&m_read_, &m_attributes) != 0)
+ {
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Creation of read mutex failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ if (pthread_mutex_init(&m_write_, &m_attributes) != 0)
+ {
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Creation of write mutex failed. "
+ << "Error is " << EGET() << " '" << ESTR()
+ << "'.\n" << logofs_flush;
+ #endif
+ }
+
+ #endif
+
+ #ifdef REFERENCES
+ *logofs << "AgentTransport: Child: Created new object at "
+ << this << " out of " << ++references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+AgentTransport::~AgentTransport()
+{
+ #ifdef TEST
+ *logofs << "AgentTransport: Going to destroy derived class "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ //
+ // Unlock and free all mutexes.
+ //
+
+ #ifdef THREADS
+
+ pthread_mutex_unlock(&m_read_);
+ pthread_mutex_unlock(&m_write_);
+
+ pthread_mutex_destroy(&m_read_);
+ pthread_mutex_destroy(&m_write_);
+
+ #endif
+
+ #ifdef REFERENCES
+ *logofs << "AgentTransport: Child: Deleted object at "
+ << this << " out of " << --references_
+ << " allocated references.\n" << logofs_flush;
+ #endif
+}
+
+//
+// Read data enqueued by the other thread.
+//
+
+int AgentTransport::read(unsigned char *data, unsigned int size)
+{
+ #ifdef THREADS
+
+ lockRead();
+
+ #endif
+
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Child: Going to read " << size
+ << " bytes from " << "FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ int copied = -1;
+
+ if (r_buffer_.length_ > 0)
+ {
+ if ((int) size < r_buffer_.length_)
+ {
+ #ifdef TEST
+ *logofs << "AgentTransport: WARNING! Forcing a retry with "
+ << r_buffer_.length_ << " bytes pending and "
+ << size << " in the buffer.\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+ }
+ else
+ {
+ copied = (r_buffer_.length_ > ((int) size) ?
+ ((int) size) : r_buffer_.length_);
+
+ memcpy(data, r_buffer_.data_.begin() + r_buffer_.start_, copied);
+
+ //
+ // Update the buffer status.
+ //
+
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Going to immediately return "
+ << copied << " bytes from FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef DUMP
+
+ *logofs << "AgentTransport: Child: Dumping content of read data.\n"
+ << logofs_flush;
+
+ DumpData(data, copied);
+
+ #endif
+
+ r_buffer_.length_ -= copied;
+
+ if (r_buffer_.length_ == 0)
+ {
+ r_buffer_.start_ = 0;
+ }
+ else
+ {
+ r_buffer_.start_ += copied;
+
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: There are still "
+ << r_buffer_.length_ << " bytes in read buffer for "
+ << "FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+ }
+ }
+ }
+ else
+ {
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Child: No data can be got "
+ << "from read buffer for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+ }
+
+ #ifdef THREADS
+
+ unlockRead();
+
+ #endif
+
+ return copied;
+}
+
+//
+// Write data to buffer so that the other
+// thread can get it.
+//
+
+int AgentTransport::write(T_write type, const unsigned char *data, const unsigned int size)
+{
+ #ifdef THREADS
+
+ lockWrite();
+
+ #endif
+
+ //
+ // Just append data to socket's write buffer.
+ // Note that we don't care if buffer exceeds
+ // the size limits set for this type of
+ // transport.
+ //
+
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Going to append " << size
+ << " bytes to write buffer for " << "FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ int copied = -1;
+
+ if (resize(w_buffer_, size) < 0)
+ {
+ finish();
+
+ ESET(EPIPE);
+ }
+ else
+ {
+ memmove(w_buffer_.data_.begin() + w_buffer_.start_ + w_buffer_.length_, data, size);
+
+ w_buffer_.length_ += size;
+
+ #ifdef DUMP
+
+ *logofs << "AgentTransport: Child: Dumping content of written data.\n"
+ << logofs_flush;
+
+ DumpData(data, size);
+
+ #endif
+
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Write buffer for FD#" << fd_
+ << " has data for " << w_buffer_.length_ << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "AgentTransport: Child: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " size is "
+ << w_buffer_.data_.size() << " capacity is "
+ << w_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ copied = size;
+ }
+
+ //
+ // Let child access again the read buffer.
+ //
+
+ #ifdef THREADS
+
+ unlockWrite();
+
+ #endif
+
+ return copied;
+}
+
+int AgentTransport::flush()
+{
+ //
+ // In case of memory-to-memory transport
+ // this function should never be called.
+ //
+
+ #ifdef PANIC
+ *logofs << "AgentTransport: Child: PANIC! Called flush() for "
+ << "memory to memory transport on " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Called flush() for "
+ << "memory to memory transport on " << "FD#"
+ << fd_ << ".\n";
+
+ HandleAbort();
+}
+
+int AgentTransport::drain(int limit, int timeout)
+{
+ //
+ // We can't drain the channel in the case
+ // of the memory-to-memory transport. Data
+ // is enqueued for the agent to read but
+ // the agent could require multiple loops
+ // to read it all.
+ //
+
+ //
+ // In case of memory-to-memory transport
+ // this function should never be called.
+ //
+
+ #ifdef PANIC
+ *logofs << "AgentTransport: Child: PANIC! Called drain() for "
+ << "memory to memory transport on " << "FD#"
+ << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Called drain() for "
+ << "memory to memory transport on " << "FD#"
+ << fd_ << ".\n";
+
+ HandleAbort();
+}
+
+unsigned int AgentTransport::getPending(unsigned char *&data)
+{
+ #ifdef THREADS
+
+ lockRead();
+
+ #endif
+
+ if (r_buffer_.length_ > 0)
+ {
+ unsigned int size = r_buffer_.length_;
+
+ data = r_buffer_.data_.begin() + r_buffer_.start_;
+
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Child: Returning " << size
+ << " pending bytes from FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ r_buffer_.length_ = 0;
+ r_buffer_.start_ = 0;
+
+ #ifdef THREADS
+
+ unlockRead();
+
+ #endif
+
+ //
+ // Prevent the deletion of the buffer.
+ //
+
+ owner_ = 0;
+
+ return size;
+ }
+
+ #ifdef TEST
+ *logofs << "AgentTransport: WARNING! No pending data "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ #ifdef THREADS
+
+ unlockRead();
+
+ #endif
+
+ data = NULL;
+
+ return 0;
+}
+
+void AgentTransport::fullReset()
+{
+ #ifdef THREADS
+
+ lockRead();
+ lockWrite();
+
+ #endif
+
+ #ifdef TEST
+ *logofs << "AgentTransport: Child: Resetting transport "
+ << "for FD#" << fd_ << ".\n" << logofs_flush;
+ #endif
+
+ blocked_ = 0;
+ finish_ = 0;
+
+ if (owner_ == 1)
+ {
+ Transport::fullReset(r_buffer_);
+ }
+
+ Transport::fullReset(w_buffer_);
+}
+
+int AgentTransport::enqueue(const char *data, const int size)
+{
+ #ifdef THREADS
+
+ lockRead();
+
+ #endif
+
+ if (finish_ == 1)
+ {
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Returning EPIPE in "
+ << "write for finishing FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EPIPE);
+
+ return -1;
+ }
+
+ //
+ // Always allow the agent to write
+ // all its data.
+ //
+
+ int toPut = size;
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Going to put " << toPut
+ << " bytes into read buffer for FD#" << fd_
+ << ". Buffer length is " << r_buffer_.length_
+ << ".\n" << logofs_flush;
+ #endif
+
+ if (resize(r_buffer_, toPut) < 0)
+ {
+ finish();
+
+ #ifdef THREADS
+
+ unlockRead();
+
+ #endif
+
+ return -1;
+ }
+
+ memcpy(r_buffer_.data_.begin() + r_buffer_.start_ + r_buffer_.length_, data, toPut);
+
+ r_buffer_.length_ += toPut;
+
+ #if defined(DUMP) && defined(PARENT)
+
+ *logofs << "AgentTransport: Parent: Dumping content of enqueued data.\n"
+ << logofs_flush;
+
+ DumpData((const unsigned char *) data, toPut);
+
+ #endif
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Read buffer for FD#" << fd_
+ << " has now data for " << r_buffer_.length_
+ << " bytes.\n" << logofs_flush;
+
+ *logofs << "AgentTransport: Parent: Start is " << r_buffer_.start_
+ << " length is " << r_buffer_.length_ << " size is "
+ << r_buffer_.data_.size() << " capacity is "
+ << r_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef THREADS
+
+ unlockRead();
+
+ #endif
+
+ return toPut;
+}
+
+int AgentTransport::dequeue(char *data, int size)
+{
+ #ifdef THREADS
+
+ lockWrite();
+
+ #endif
+
+ if (w_buffer_.length_ == 0)
+ {
+ if (finish_ == 1)
+ {
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Returning 0 in read "
+ << "for finishing FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return 0;
+ }
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: No data can be read "
+ << "from write buffer for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ ESET(EAGAIN);
+
+ #ifdef THREADS
+
+ unlockWrite();
+
+ #endif
+
+ return -1;
+ }
+
+ //
+ // Return as many bytes as possible.
+ //
+
+ int toGet = ((int) size > w_buffer_.length_ ? w_buffer_.length_ : size);
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Going to get " << toGet
+ << " bytes from write buffer for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ memcpy(data, w_buffer_.data_.begin() + w_buffer_.start_, toGet);
+
+ w_buffer_.start_ += toGet;
+ w_buffer_.length_ -= toGet;
+
+ #if defined(DUMP) && defined(PARENT)
+
+ *logofs << "AgentTransport: Parent: Dumping content of dequeued data.\n"
+ << logofs_flush;
+
+ DumpData((const unsigned char *) data, toGet);
+
+ #endif
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Write buffer for FD#" << fd_
+ << " has now data for " << length() << " bytes.\n"
+ << logofs_flush;
+
+ *logofs << "AgentTransport: Parent: Start is " << w_buffer_.start_
+ << " length is " << w_buffer_.length_ << " size is "
+ << w_buffer_.data_.size() << " capacity is "
+ << w_buffer_.data_.capacity() << ".\n"
+ << logofs_flush;
+ #endif
+
+ #ifdef THREADS
+
+ unlockWrite();
+
+ #endif
+
+ return toGet;
+}
+
+int AgentTransport::dequeuable()
+{
+ if (finish_ == 1)
+ {
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Returning EPIPE in "
+ << "readable for finishing FD#" << fd_
+ << ".\n" << logofs_flush;
+ #endif
+
+ ESET(EPIPE);
+
+ return -1;
+ }
+
+ #if defined(PARENT) && defined(TEST)
+ *logofs << "AgentTransport: Parent: Returning "
+ << w_buffer_.length_ << " as data readable "
+ << "from read buffer for FD#" << fd_ << ".\n"
+ << logofs_flush;
+ #endif
+
+ return w_buffer_.length_;
+}
+
+#ifdef THREADS
+
+int AgentTransport::lockRead()
+{
+ for (;;)
+ {
+ int result = pthread_mutex_lock(&m_read_);
+
+ if (result == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Read mutex locked by thread id "
+ << pthread_self() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ continue;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "AgentTransport: WARNING! Locking of read mutex by thread id "
+ << pthread_self() << " returned " << result << ". Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return result;
+ }
+ }
+}
+
+int AgentTransport::lockWrite()
+{
+ for (;;)
+ {
+ int result = pthread_mutex_lock(&m_write_);
+
+ if (result == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Write mutex locked by thread id "
+ << pthread_self() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ continue;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "AgentTransport: WARNING! Locking of write mutex by thread id "
+ << pthread_self() << " returned " << result << ". Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return result;
+ }
+ }
+}
+
+int AgentTransport::unlockRead()
+{
+ for (;;)
+ {
+ int result = pthread_mutex_unlock(&m_read_);
+
+ if (result == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Read mutex unlocked by thread id "
+ << pthread_self() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ continue;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "AgentTransport: WARNING! Unlocking of read mutex by thread id "
+ << pthread_self() << " returned " << result << ". Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return result;
+ }
+ }
+}
+
+int AgentTransport::unlockWrite()
+{
+ for (;;)
+ {
+ int result = pthread_mutex_unlock(&m_write_);
+
+ if (result == 0)
+ {
+ #ifdef DEBUG
+ *logofs << "AgentTransport: Write mutex unlocked by thread id "
+ << pthread_self() << ".\n" << logofs_flush;
+ #endif
+
+ return 0;
+ }
+ else if (EGET() == EINTR)
+ {
+ continue;
+ }
+ else
+ {
+ #ifdef WARNING
+ *logofs << "AgentTransport: WARNING! Unlocking of write mutex by thread id "
+ << pthread_self() << " returned " << result << ". Error is '"
+ << ESTR() << "'.\n" << logofs_flush;
+ #endif
+
+ return result;
+ }
+ }
+}
+
+#endif
diff --git a/nxcomp/src/Transport.h b/nxcomp/src/Transport.h
new file mode 100644
index 000000000..4ae0e3c25
--- /dev/null
+++ b/nxcomp/src/Transport.h
@@ -0,0 +1,577 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Transport_H
+#define Transport_H
+
+#include <zlib.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include "Misc.h"
+#include "Control.h"
+
+#include "Types.h"
+#include "Timestamp.h"
+#include "Socket.h"
+
+//
+// Set the verbosity level.
+//
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Define this to lock and unlock the
+// memory-to-memory transport buffers
+// before they are accessed. The code
+// is outdated and doesn't work with
+// the current pthread library.
+//
+
+#undef THREADS
+
+//
+// Define this to know when a socket
+// is created or destroyed.
+//
+
+#undef REFERENCES
+
+//
+// Size of buffer if not set by user.
+//
+
+#define TRANSPORT_BUFFER_DEFAULT_SIZE 16384
+
+//
+// Type of transport.
+//
+
+typedef enum
+{
+ transport_base,
+ transport_proxy,
+ transport_agent,
+ transport_last_tag
+
+} T_transport_type;
+
+//
+// This class handles the buffered I/O on
+// the network sockets.
+//
+
+//
+// TODO: This class is useful but adds a lot of
+// overhead. There are many improvements we can
+// make here:
+//
+// - There should be a generic Buffer class, ac-
+// comodating a list of memory buffers. This
+// would enable the use of the readv() and
+// writev() functions to perform the I/O on
+// the socket.
+//
+// - The buffering should be moved to the Write-
+// Buffer and ReadBuffer classes. By performing
+// the buffering here and there, we are dupli-
+// cating a lot of code and are adding a lot
+// of useless memory copies.
+//
+// - Stream compression should be removed. The
+// proxy should compress the frames based on
+// the type and should include the length of
+// the decompressed data in the header of the
+// packet. Besides avoiding the compression
+// of packets that cannot be reduced in size,
+// we would also save the additional memory
+// allocations due to the fact that we don't
+// know the size of the decode buffer at the
+// time we read the packet from the network.
+//
+// - The other utilities implemented here, like
+// the functions forcing a write on the socket
+// or waiting for more data to become available
+// should be moved to the Proxy or the Channel
+// classes.
+//
+
+class Transport
+{
+ public:
+
+ //
+ // Member functions.
+ //
+
+ Transport(int fd);
+
+ virtual ~Transport();
+
+ int fd() const
+ {
+ return fd_;
+ }
+
+ T_transport_type getType()
+ {
+ return type_;
+ }
+
+ //
+ // Virtual members redefined by proxy
+ // and 'memory-to-memory' I/O layers.
+ //
+
+ virtual int read(unsigned char *data, unsigned int size);
+
+ virtual int write(T_write type, const unsigned char *data, const unsigned int size);
+
+ virtual int flush();
+
+ virtual int drain(int limit, int timeout);
+
+ virtual void finish()
+ {
+ fullReset();
+
+ finish_ = 1;
+ }
+
+ virtual int length() const
+ {
+ return w_buffer_.length_;
+ }
+
+ virtual int pending() const
+ {
+ return 0;
+ }
+
+ virtual int readable() const
+ {
+ return GetBytesReadable(fd_);
+ }
+
+ virtual int writable() const
+ {
+ return GetBytesWritable(fd_);
+ }
+
+ virtual int queued() const
+ {
+ return GetBytesQueued(fd_);
+ }
+
+ virtual int flushable() const
+ {
+ return 0;
+ }
+
+ virtual int wait(int timeout) const;
+
+ void setSize(unsigned int initialSize,
+ unsigned int thresholdSize,
+ unsigned int maximumSize);
+
+ //
+ // Return a pointer to the data
+ // in the read buffer.
+ //
+
+ virtual unsigned int getPending(unsigned char *&data)
+ {
+ data = NULL;
+
+ return 0;
+ }
+
+ virtual void pendingReset()
+ {
+ }
+
+ virtual void partialReset()
+ {
+ partialReset(w_buffer_);
+ }
+
+ virtual void fullReset();
+
+ int blocked() const
+ {
+ return blocked_;
+ }
+
+ protected:
+
+ //
+ // Make room in the buffer to accommodate
+ // at least size bytes.
+ //
+
+ int resize(T_buffer &buffer, const int &size);
+
+ void partialReset(T_buffer &buffer)
+ {
+ if (buffer.length_ == 0 &&
+ (buffer.data_.size() > initialSize_ ||
+ buffer.data_.capacity() > initialSize_))
+ {
+ fullReset(buffer);
+ }
+ }
+
+ void fullReset(T_buffer &buffer);
+
+ //
+ // Data members.
+ //
+
+ int fd_;
+
+ int blocked_;
+ int finish_;
+
+ T_buffer w_buffer_;
+
+ unsigned int initialSize_;
+ unsigned int thresholdSize_;
+ unsigned int maximumSize_;
+
+ T_transport_type type_;
+
+ private:
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+//
+// This class handles buffered I/O and
+// compression of the proxy stream.
+//
+
+class ProxyTransport : public Transport
+{
+ public:
+
+ ProxyTransport(int fd);
+
+ virtual ~ProxyTransport();
+
+ virtual int read(unsigned char *data, unsigned int size);
+
+ virtual int write(T_write type, const unsigned char *data, const unsigned int size);
+
+ virtual int flush();
+
+ //
+ // Same as in the base class.
+ //
+ // virtual int drain(int limit, int timeout);
+ //
+ // virtual void finish();
+ //
+
+ //
+ // Same as in the base class.
+ //
+ // virtual int length() const
+ //
+
+ virtual int pending() const
+ {
+ return r_buffer_.length_;
+ }
+
+ //
+ // Same as in the base class.
+ //
+ // virtual int readable() const;
+ //
+ // virtual int writable() const;
+ //
+ // virtual int queued() const;
+ //
+
+ virtual int flushable() const
+ {
+ return flush_;
+ }
+
+ //
+ // Same as in the base class, but
+ // should not be called.
+ //
+ // int drained() const;
+ //
+ // Same as in the base class.
+ //
+ // virtual int wait(int timeout) const;
+ //
+ // Same as in the base class.
+ //
+ // void setSize(unsigned int initialSize,
+ // unsigned int thresholdSize,
+ // unsigned int maximumSize);
+ //
+
+ virtual unsigned int getPending(unsigned char *&data);
+
+ virtual void pendingReset()
+ {
+ owner_ = 1;
+ }
+
+ virtual void partialReset()
+ {
+ if (owner_ == 1)
+ {
+ Transport::partialReset(r_buffer_);
+ }
+
+ Transport::partialReset(w_buffer_);
+ }
+
+ virtual void fullReset();
+
+ //
+ // Same as in the base class.
+ //
+ // int blocked() const;
+ //
+
+ protected:
+
+ int flush_;
+ int owner_;
+
+ T_buffer r_buffer_;
+
+ z_stream r_stream_;
+ z_stream w_stream_;
+
+ private:
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+//
+// Handle memory-to-memory data transfers between
+// an agent and the proxy.
+//
+
+class AgentTransport : public Transport
+{
+ public:
+
+ AgentTransport(int fd);
+
+ virtual ~AgentTransport();
+
+ virtual int read(unsigned char *data, unsigned int size);
+
+ virtual int write(T_write type, const unsigned char *data, const unsigned int size);
+
+ //
+ // These two should never be called.
+ //
+
+ virtual int flush() __attribute__((noreturn));
+
+ virtual int drain(int limit, int timeout) __attribute((noreturn));
+
+ //
+ // Same as in the base class.
+ //
+ // virtual void finish();
+ //
+
+ //
+ // Same as in the base class.
+ //
+ // virtual int length() const
+ //
+
+ virtual int pending() const
+ {
+ return r_buffer_.length_;
+ }
+
+ //
+ // These are intended to operate only
+ // on the internal buffers.
+ //
+
+ virtual int readable() const
+ {
+ return r_buffer_.length_;
+ }
+
+ virtual int writable() const
+ {
+ return control -> TransportMaximumBufferSize;
+ }
+
+ virtual int queued() const
+ {
+ return 0;
+ }
+
+ //
+ // Same as in the base class.
+ //
+ // virtual int flushable() const;
+ //
+ // Same as in the base class, but
+ // should not be called.
+ //
+ // int drained() const;
+ //
+
+ //
+ // Return immediately or will
+ // block until the timeout.
+ //
+
+ virtual int wait(int timeout) const
+ {
+ return 0;
+ }
+
+ //
+ // Same as in the base class.
+ //
+ // void setSize(unsigned int initialSize,
+ // unsigned int thresholdSize,
+ // unsigned int maximumSize);
+ //
+
+ virtual unsigned int getPending(unsigned char *&data);
+
+ virtual void pendingReset()
+ {
+ owner_ = 1;
+ }
+
+ virtual void partialReset()
+ {
+ if (owner_ == 1)
+ {
+ Transport::partialReset(r_buffer_);
+ }
+
+ Transport::partialReset(w_buffer_);
+ }
+
+ virtual void fullReset();
+
+ //
+ // Same as in the base class.
+ //
+ // int blocked() const;
+ //
+
+ //
+ // The following are specific of the
+ // memory-to-memory transport.
+ //
+
+ int enqueue(const char *data, const int size);
+
+ int dequeue(char *data, int size);
+
+ int queuable()
+ {
+ //
+ // Always allow the agent to enqueue
+ // more data.
+ //
+
+ return control -> TransportMaximumBufferSize;
+ }
+
+ int dequeuable();
+
+ protected:
+
+ //
+ // Lock the buffer to handle reads and
+ // writes safely.
+ //
+
+ #ifdef THREADS
+
+ int lockRead();
+ int lockWrite();
+
+ int unlockRead();
+ int unlockWrite();
+
+ #endif
+
+ //
+ // Data members.
+ //
+
+ int owner_;
+
+ T_buffer r_buffer_;
+
+ //
+ // Mutexes for safe read and write.
+ //
+
+ #ifdef THREADS
+
+ pthread_mutex_t m_read_;
+ pthread_mutex_t m_write_;
+
+ #endif
+
+ private:
+
+ #ifdef REFERENCES
+
+ static int references_;
+
+ #endif
+};
+
+#endif /* Transport_H */
diff --git a/nxcomp/src/Types.h b/nxcomp/src/Types.h
new file mode 100644
index 000000000..e82664c81
--- /dev/null
+++ b/nxcomp/src/Types.h
@@ -0,0 +1,271 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Types_H
+#define Types_H
+
+using namespace std;
+
+#include <vector>
+#include <list>
+#include <map>
+#include <set>
+
+#include "MD5.h"
+
+//
+// This is MD5 length.
+//
+
+#define MD5_LENGTH 16
+
+//
+// Types of repositories. Replace the original
+// clear() methods from STL in order to actually
+// free the unused memory.
+//
+
+class Message;
+
+class T_data : public vector < unsigned char >
+{
+ public:
+
+ unsigned char *begin()
+ {
+ return &*(vector < unsigned char >::begin());
+ }
+
+ const unsigned char *begin() const
+ {
+ return &*(vector < unsigned char >::begin());
+ }
+
+ // Avoid overriding clear() when using libc++. Fiddling with STL internals
+ // doesn't really seem like a good idea to me anyway.
+ #ifndef _LIBCPP_VECTOR
+ void clear()
+ {
+ #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H)
+
+ #if defined(__GLIBCPP_INTERNAL_VECTOR_H)
+
+ _Destroy(_M_start, _M_finish);
+
+ #else /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ destroy(_M_start, _M_finish);
+
+ #endif /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ _M_deallocate(_M_start, _M_end_of_storage - _M_start);
+
+ _M_start = _M_finish = _M_end_of_storage = 0;
+
+ #else /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ #if defined(_GLIBCXX_VECTOR)
+
+ _Destroy(this->_M_impl._M_start, this->_M_impl._M_finish);
+
+ _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
+
+ this->_M_impl._M_start = this->_M_impl._M_finish = this->_M_impl._M_end_of_storage = 0;
+
+ #else /* #if defined(_GLIBCXX_VECTOR) */
+
+ destroy(start, finish);
+
+ deallocate();
+
+ start = finish = end_of_storage = 0;
+
+ #endif /* #if defined(_GLIBCXX_VECTOR) */
+
+ #endif /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+ }
+ #endif /* #ifdef _LIBCPP_VECTOR */
+};
+
+class T_messages : public vector < Message * >
+{
+ public:
+
+ // Avoid overriding clear() when using libc++. Fiddling with STL internals
+ // doesn't really seem like a good idea to me anyway.
+ #ifndef _LIBCPP_VECTOR
+ void clear()
+ {
+ #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H)
+
+ #if defined(__GLIBCPP_INTERNAL_VECTOR_H)
+
+ _Destroy(_M_start, _M_finish);
+
+ #else /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ destroy(_M_start, _M_finish);
+
+ #endif /* #if defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ _M_deallocate(_M_start, _M_end_of_storage - _M_start);
+
+ _M_start = _M_finish = _M_end_of_storage = 0;
+
+ #else /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+
+ #if defined(_GLIBCXX_VECTOR)
+
+ _Destroy(this->_M_impl._M_start, this->_M_impl._M_finish);
+
+ _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
+
+ this->_M_impl._M_start = this->_M_impl._M_finish = this->_M_impl._M_end_of_storage = 0;
+
+ #else /* #if defined(_GLIBCXX_VECTOR) */
+
+ destroy(start, finish);
+
+ deallocate();
+
+ start = finish = end_of_storage = 0;
+
+ #endif /* #if defined(_GLIBCXX_VECTOR) */
+
+ #endif /* #if defined(__STL_USE_STD_ALLOCATORS) || defined(__GLIBCPP_INTERNAL_VECTOR_H) */
+ }
+ #endif /* #ifndef _LIBCPP_VECTOR */
+};
+
+typedef md5_byte_t * T_checksum;
+
+struct T_less
+{
+ bool operator()(T_checksum a, T_checksum b) const
+ {
+ return (memcmp(a, b, MD5_LENGTH) < 0);
+ }
+};
+
+typedef map < T_checksum, int, T_less > T_checksums;
+
+class Split;
+
+typedef list < Split * > T_splits;
+
+class File;
+
+struct T_older
+{
+ bool operator()(File *a, File *b) const;
+};
+
+typedef set < File *, T_older > T_files;
+
+typedef list < int > T_list;
+
+//
+// Used to accommodate data to be read and
+// written to a socket.
+//
+
+typedef struct
+{
+ T_data data_;
+ int length_;
+ int start_;
+}
+T_buffer;
+
+//
+// The message store operation that was
+// executed for the message. The channels
+// use these values to determine how to
+// handle the message after it has been
+// received at the decoding side.
+//
+
+// Since ProtoStep8 (#issue 108)
+enum T_store_action
+{
+ is_hit,
+ is_added,
+ is_discarded,
+ is_removed
+};
+
+// Since ProtoStep8 (#issue 108)
+#define IS_HIT is_hit
+#define IS_ADDED is_added
+
+enum T_checksum_action
+{
+ use_checksum,
+ discard_checksum
+};
+
+enum T_data_action
+{
+ use_data,
+ discard_data
+};
+
+//
+// Message is going to be weighted for
+// deletion at insert or cleanup time?
+//
+
+enum T_rating
+{
+ rating_for_insert,
+ rating_for_clean
+};
+
+//
+// How to handle the writes to the X
+// and proxy connections.
+//
+
+enum T_write
+{
+ write_immediate,
+ write_delayed
+};
+
+enum T_flush
+{
+ flush_if_needed,
+ flush_if_any
+};
+
+//
+// This is the value to indicate an
+// invalid position in the message
+// store.
+//
+
+static const int nothing = -1;
+
+#endif /* Types_H */
diff --git a/nxcomp/src/Unpack.cpp b/nxcomp/src/Unpack.cpp
new file mode 100644
index 000000000..e63e836f0
--- /dev/null
+++ b/nxcomp/src/Unpack.cpp
@@ -0,0 +1,1514 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Misc.h"
+#include "Unpack.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+//
+// Used for the ZLIB decompression
+// of RGB, alpha and colormap data.
+//
+
+z_stream unpackStream;
+
+static int unpackInitialized;
+
+int Unpack8To8(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To8(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To16(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To24(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack8To32(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack15To16(const unsigned char *data, unsigned char *out,
+ unsigned char *end);
+
+int Unpack15To24(const unsigned char *data, unsigned char *out,
+ unsigned char *end);
+
+int Unpack15To32(const unsigned char *data, unsigned char *out,
+ unsigned char *end);
+
+int Unpack16To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack16To16(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder);
+
+int Unpack16To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack16To24(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder);
+
+int Unpack16To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack16To32(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder);
+
+int Unpack24To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack24To24(const unsigned char *data, unsigned char *out,
+ unsigned char *end);
+
+int Unpack24To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+int Unpack24To32(const unsigned char *data, unsigned char *out, unsigned char *end);
+
+int Unpack32To32(const T_colormask *colormask, const unsigned int *data,
+ unsigned int *out, unsigned int *end);
+
+
+void UnpackInit()
+{
+ if (unpackInitialized == 0)
+ {
+ unpackStream.zalloc = (alloc_func) 0;
+ unpackStream.zfree = (free_func) 0;
+ unpackStream.opaque = (voidpf) 0;
+
+ unpackStream.next_in = (Bytef *) 0;
+ unpackStream.avail_in = 0;
+
+ int result = inflateInit2(&unpackStream, 15);
+
+ if (result != Z_OK)
+ {
+ #ifdef PANIC
+ *logofs << "UnpackInit: PANIC! Cannot initialize the Z stream "
+ << "for decompression. Error is '" << zError(result)
+ << "'.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Cannot initialize the Z stream for "
+ << "decompression. Error is '" << zError(result)
+ << "'.\n";
+ }
+ else
+ {
+ unpackInitialized = 1;
+ }
+ }
+}
+
+void UnpackDestroy()
+{
+ if (unpackInitialized == 1)
+ {
+ inflateEnd(&unpackStream);
+
+ unpackInitialized = 0;
+ }
+}
+
+//
+// Get bits per pixel set by client
+// according to display geometry.
+//
+
+int UnpackBitsPerPixel(T_geometry *geometry, unsigned int depth)
+{
+ switch (depth)
+ {
+ case 1:
+ {
+ return geometry -> depth1_bpp;
+ }
+ case 4:
+ {
+ return geometry -> depth4_bpp;
+ }
+ case 8:
+ {
+ return geometry -> depth8_bpp;
+ }
+ case 15:
+ case 16:
+ {
+ return geometry -> depth16_bpp;
+ }
+ case 24:
+ {
+ return geometry -> depth24_bpp;
+ }
+ case 32:
+ {
+ return geometry -> depth32_bpp;
+ }
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+int Unpack8To8(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To8: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ memcpy(out, data, end - out);
+
+ return 1;
+}
+
+int Unpack8To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To16: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned short *out16 = (unsigned short *) out;
+ unsigned short *end16 = (unsigned short *) end;
+
+ while (out16 < end16)
+ {
+ if (*data == 0)
+ {
+ *out16 = 0x0;
+ }
+ else if (*data == 0xff)
+ {
+ *out16 = 0xffff;
+ }
+ else
+ {
+ //
+ // Pixel layout:
+ //
+ // 8bits 00RRGGBB -> 16bits RR000GG0 000BB000.
+ //
+
+ *out16 = (((((*data & 0x30) << 2) | colormask -> correction_mask) << 8) & 0xf800) |
+ (((((*data & 0xc) << 4) | colormask -> correction_mask) << 3) & 0x7e0) |
+ (((((*data & 0x3) << 6) | colormask -> correction_mask) >> 3) & 0x1f);
+ }
+
+ out16++;
+ data++;
+ }
+
+ return 1;
+}
+
+int Unpack8To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To24: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ while (out < (end - 2))
+ {
+ if (*data == 0x00)
+ {
+ out[0] = out[1] = out[2] = 0x00;
+ }
+ else if (*data == 0xff)
+ {
+ out[0] = out[1] = out[2] = 0xff;
+ }
+ else
+ {
+ //
+ // Pixel layout:
+ //
+ // 8bits 00RRGGBB -> 24bits RR000000 GG00000 BB000000.
+ //
+
+ out[0] = (((*data & 0x30) << 2) | colormask -> correction_mask);
+ out[1] = (((*data & 0x0c) << 4) | colormask -> correction_mask);
+ out[2] = (((*data & 0x03) << 6) | colormask -> correction_mask);
+ }
+
+ out += 3;
+ data += 1;
+ }
+
+ return 1;
+}
+
+int Unpack8To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To32: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ if (*data == 0)
+ {
+ *out32 = 0x0;
+ }
+ else if (*data == 0xff)
+ {
+ *out32 = 0xffffff;
+ }
+ else
+ {
+ *out32 = ((((*data & 0x30) << 2) | colormask -> correction_mask) << 16) |
+ ((((*data & 0xc) << 4) | colormask -> correction_mask) << 8) |
+ (((*data & 0x3) << 6) | colormask -> correction_mask);
+ }
+
+ out32++;
+ data++;
+ }
+
+ return 1;
+}
+
+int Unpack8(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size)
+{
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ int (*unpack)(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+ switch (dst_bpp)
+ {
+ case 8:
+ {
+ unpack = Unpack8To8;
+
+ break;
+ }
+ case 16:
+ {
+ unpack = Unpack8To16;
+
+ break;
+ }
+ case 24:
+ {
+ unpack = Unpack8To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack8To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack8: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 16/24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (dst_bpp == 24)
+ {
+ unsigned char *dst_end = dst_data;
+
+ #ifdef TEST
+ *logofs << "Unpack8: Handling 24 bits with dst_size "
+ << dst_size << ".\n" << logofs_flush;
+ #endif
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+
+ dst_end += RoundUp4(dst_width * 3);
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+
+ src_data += src_width;
+ }
+ }
+ else
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+ }
+
+ return 1;
+}
+
+int Unpack16To16(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack16To16: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ if (colormask -> correction_mask)
+ {
+ unsigned short *data16 = (unsigned short *) data;
+
+ unsigned short *out16 = (unsigned short *) out;
+ unsigned short *end16 = (unsigned short *) end;
+
+ while (out16 < end16)
+ {
+ if (*data16 == 0x0000)
+ {
+ *out16 = 0x0000;
+ }
+ else if (*data16 == 0xffff)
+ {
+ *out16 = 0xffff;
+ }
+ else
+ {
+ //
+ // Pixel layout:
+ //
+ // 16bit RRRRRGGG GG0BBBBB -> RRRRRGGG GGGBBBBB.
+ //
+
+ *out16 = (((((*data16 & 0xf100) >> 8) | colormask -> correction_mask) << 8) & 0xf800) |
+ (((((*data16 & 0x7c0) >> 3) | colormask -> correction_mask) << 3) & 0x7e0) |
+ (((((*data16 & 0x1f) << 3) | colormask -> correction_mask) >> 3) & 0x1f);
+ }
+
+ out16++;
+ data16++;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack16To16: Using bitwise copy due to null correction mask.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) out, (unsigned char *) data, end - out);
+ }
+
+ return 1;
+}
+
+int Unpack16To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack16To24: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+
+
+ while (out < end - 2)
+ {
+ if (*data16 == 0x0)
+ {
+ out[0] = 0x00;
+ out[1] = 0x00;
+ out[2] = 0x00;
+ }
+ else if (*data16 == 0xffff)
+ {
+ out[0] = 0xff;
+ out[1] = 0xff;
+ out[2] = 0xff;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack16To24: Pixel [" << *data16 << "]\n"
+ << logofs_flush;
+ #endif
+
+ //
+ // Pixel layout:
+ //
+ // 16bit 0RRRRRGG GGGBBBBB -> 24 bit RRRRR000 GGGGG000 BBBBB000
+ //
+
+ out[0] = (((*data16 & 0x7c00) >> 7) | colormask -> correction_mask);
+ out[1] = (((*data16 & 0x03e0) >> 2) | colormask -> correction_mask);
+ out[2] = (((*data16 & 0x001f) << 3) | colormask -> correction_mask);
+ }
+
+ out += 3;
+ data16 += 1;
+ }
+
+ return 1;
+}
+
+int Unpack16To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack16To32: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ if (*data16 == 0x0)
+ {
+ *out32 = 0x0;
+ }
+ else if (*data16 == 0xffff)
+ {
+ *out32 = 0xffffff;
+ }
+ else
+ {
+ *out32 = ((((*data16 & 0x7c00) >> 7) | colormask -> correction_mask) << 16) |
+ ((((*data16 & 0x3e0) >> 2) | colormask -> correction_mask) << 8) |
+ (((*data16 & 0x1f) << 3) | colormask -> correction_mask);
+ }
+
+ out32++;
+ data16++;
+ }
+
+ return 1;
+}
+
+int Unpack16(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size)
+{
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ int (*unpack)(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+ switch (dst_bpp)
+ {
+ case 16:
+ {
+ unpack = Unpack16To16;
+
+ break;
+ }
+ case 24:
+ {
+ unpack = Unpack16To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack16To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack16: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (dst_bpp == 24)
+ {
+ unsigned char *dst_end = dst_data;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+
+ dst_end += RoundUp4(dst_width * 3);
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+
+ src_data += (src_width * 2);
+ }
+
+ }
+ else
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+ }
+
+ return 1;
+}
+
+int Unpack24To24(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack24To24: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ if (colormask -> correction_mask)
+ {
+ while (out < end)
+ {
+ if (data[0] == 0x00 &&
+ data[1] == 0x00 &&
+ data[2] == 0x00)
+ {
+ out[0] = out[1] = out[2] = 0x00;
+ }
+ else if (data[0] == 0xff &&
+ data[1] == 0xff &&
+ data[2] == 0xff)
+ {
+ out[0] = out[1] = out[2] = 0xff;
+ }
+ else
+ {
+ out[0] = (data[0] | colormask -> correction_mask);
+ out[1] = (data[1] | colormask -> correction_mask);
+ out[2] = (data[2] | colormask -> correction_mask);
+ }
+
+ out += 3;
+ data += 3;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack24To24: Using bitwise copy due to null correction mask.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) out, (unsigned char *) data, end - out);
+ }
+
+ return 1;
+}
+
+int Unpack24To32(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack24To32: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ if (colormask -> color_mask == 0xff)
+ {
+ *out32 = (data[0] << 16) | (data[1] << 8) | data[2];
+ }
+ else
+ {
+ if (data[0] == 0x0 && data[1] == 0x0 && data[2] == 0x0)
+ {
+ *out32 = 0x0;
+ }
+ else if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff)
+ {
+ *out32 = 0xffffff;
+ }
+ else
+ {
+ *out32 = (((unsigned int) data[0] | colormask -> correction_mask) << 16) |
+ (((unsigned int) data[1] | colormask -> correction_mask) << 8) |
+ ((unsigned int) data[2] | colormask -> correction_mask);
+ }
+ }
+
+ out32 += 1;
+ data += 3;
+ }
+
+ return 1;
+}
+
+int Unpack24(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size)
+{
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ int (*unpack)(const T_colormask *colormask, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+ switch (dst_bpp)
+ {
+ case 24:
+ {
+ unpack = Unpack24To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack24To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack24: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 32 is supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (dst_bpp == 24)
+ {
+ unsigned char *dst_end;
+ unsigned long scanline_size = RoundUp4(dst_width * dst_bpp / 8);
+
+ dst_end = dst_data;
+
+ #ifdef TEST
+ *logofs << "Unpack24: Handling 24 bits with dst_height "
+ << dst_height << " scanline_size " << scanline_size
+ << " dst_size " << dst_size << ".\n" << logofs_flush;
+ #endif
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+
+ dst_end += scanline_size;
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+
+ src_data += scanline_size;
+ }
+ }
+ else
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(colormask, src_data, dst_data, dst_end);
+ }
+
+ return 1;
+}
+
+int Unpack8To8(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To8: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ while (out < end)
+ {
+ *(out++) = (unsigned char) colormap -> data[*(data++)];
+ }
+
+ return 1;
+}
+
+int Unpack8To16(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To16: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned short *out16 = (unsigned short *) out;
+ unsigned short *end16 = (unsigned short *) end;
+
+ while (out16 < end16)
+ {
+ *(out16++) = (unsigned short) colormap -> data[*(data++)];
+ }
+
+ return 1;
+}
+
+int Unpack8To24(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To24: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int value;
+
+ while (out < end)
+ {
+ value = colormap -> data[*(data++)];
+
+ *(out++) = value;
+ *(out++) = value >> 8;
+ *(out++) = value >> 16;
+ }
+
+ return 1;
+}
+
+int Unpack8To32(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack8To32: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ *(out32++) = colormap -> data[*(data++)];
+ }
+
+ return 1;
+}
+
+int Unpack8(T_geometry *geometry, T_colormap *colormap, int src_depth, int src_width, int src_height,
+ unsigned char *src_data, int src_size, int dst_depth, int dst_width,
+ int dst_height, unsigned char *dst_data, int dst_size)
+{
+ if (src_depth != 8)
+ {
+ #ifdef PANIC
+ *logofs << "Unpack8: PANIC! Cannot unpack colormapped image of source depth "
+ << src_depth << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ int (*unpack)(T_colormap *colormap, const unsigned char *data,
+ unsigned char *out, unsigned char *end);
+
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ switch (dst_bpp)
+ {
+ case 8:
+ {
+ unpack = Unpack8To8;
+
+ break;
+ }
+ case 16:
+ {
+ unpack = Unpack8To16;
+
+ break;
+ }
+ case 24:
+ {
+ unpack = Unpack8To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack8To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack8: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 8/16/24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (src_width == dst_width &&
+ src_height == dst_height)
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(colormap, src_data, dst_data, dst_end);
+ }
+ else if (src_width >= dst_width &&
+ src_height >= dst_height)
+ {
+ unsigned char *dst_end = dst_data;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+
+ dst_end += RoundUp4(dst_width * dst_bpp / 8);
+
+ (*unpack)(colormap, src_data, dst_data, dst_end);
+
+ src_data += src_width;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Unpack8: PANIC! Cannot unpack image. "
+ << "Destination area " << dst_width << "x"
+ << dst_height << " is not fully contained in "
+ << src_width << "x" << src_height << " source.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Unpack15To16(const unsigned char *data, unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack15To16: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+ unsigned short *out16 = (unsigned short *) out;
+ unsigned short *end16 = (unsigned short *) end;
+
+ while (out16 < end16)
+ {
+ if (*data16 == 0x0)
+ {
+ *out16 = 0x0;
+ }
+ else if (*data16 == 0x7fff)
+ {
+ *out16 = 0xffff;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack15To16: Pixel [" << *data16 << "]\n"
+ << logofs_flush;
+ #endif
+
+ *out16 = ((*data16 & 0x7ff0) << 1) |
+ (*data16 & 0x001f);
+ }
+
+ out16 += 1;
+ data16 += 1;
+ }
+
+ return 1;
+}
+
+int Unpack15To24(const unsigned char *data, unsigned char *out, unsigned char *end)
+
+{
+ #ifdef TEST
+ *logofs << "Unpack15To24: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+
+ while (out < end - 2)
+ {
+ if (*data16 == 0x0)
+ {
+ out[0] = 0x00;
+ out[1] = 0x00;
+ out[2] = 0x00;
+ }
+ else if (*data16 == 0x7fff)
+ {
+ out[0] = 0xff;
+ out[1] = 0xff;
+ out[2] = 0xff;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack15To24: Pixel [" << *data16 << "]\n"
+ << logofs_flush;
+ #endif
+
+ out[0] = ((*data16 >> 7) & 0xf8) | ((*data16 >> 12) & 0x07);
+ out[1] = ((*data16 >> 2) & 0xf8) | ((*data16 >> 8) & 0x07);
+ out[2] = ((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07);
+ }
+
+ out += 3;
+ data16 += 1;
+ }
+
+ return 1;
+}
+
+int Unpack15To32(const unsigned char *data, unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack15To32: Unpacking " << end - out
+ << " bytes of data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ if (*data16 == 0x0)
+ {
+ *out32 = 0x0;
+ }
+ else if (*data16 == 0xffff)
+ {
+ *out32 = 0xffffff;
+ }
+ else
+ {
+ *out32 = ((((*data16 >> 7) & 0xf8) | ((*data16 >> 12) & 0x07)) << 16) |
+ ((((*data16 >> 2) & 0xf8) | ((*data16 >> 8) & 0x07)) << 8) |
+ (((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07));
+ }
+
+ out32++;
+ data16++;
+ }
+
+ return 1;
+}
+
+int Unpack15(T_geometry *geometry, int src_depth, int src_width, int src_height,
+ unsigned char *src_data, int src_size, int dst_depth, int dst_width,
+ int dst_height, unsigned char *dst_data, int dst_size)
+{
+ if (src_depth != 16)
+ {
+ #ifdef PANIC
+ *logofs << "Unpack15: PANIC! Cannot unpack colormapped image of source depth "
+ << src_depth << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ int (*unpack)(const unsigned char *data, unsigned char *out, unsigned char *end);
+
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ switch (dst_bpp)
+ {
+ case 16:
+ {
+ unpack = Unpack15To16;
+
+ break;
+ }
+ case 24:
+ {
+ unpack = Unpack15To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack15To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack15: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 16/24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (src_width == dst_width && src_height == dst_height)
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(src_data, dst_data, dst_end);
+ }
+ else if (src_width >= dst_width && src_height >= dst_height)
+ {
+ unsigned char *dst_end = dst_data;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+ dst_end += RoundUp4(dst_width * dst_bpp / 8);
+
+ (*unpack)(src_data, dst_data, dst_end);
+
+ src_data += src_width * 2;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Unpack15: PANIC! Cannot unpack image. "
+ << "Destination area " << dst_width << "x"
+ << dst_height << " is not fully contained in "
+ << src_width << "x" << src_height << " source.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Unpack16To16(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder)
+{
+ #ifdef TEST
+ *logofs << "Unpack16To16: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) out, (unsigned char *) data, end - out);
+
+ return 1;
+}
+
+int Unpack16To24(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder)
+
+{
+ #ifdef TEST
+ *logofs << "Unpack16To24: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+
+ while (out < end - 2)
+ {
+ if (*data16 == 0x0)
+ {
+ out[0] = 0x00;
+ out[1] = 0x00;
+ out[2] = 0x00;
+ }
+ else if (*data16 == 0xffff)
+ {
+ out[0] = 0xff;
+ out[1] = 0xff;
+ out[2] = 0xff;
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack16To24: Pixel [" << *data16 << "]\n"
+ << logofs_flush;
+ #endif
+
+ out[0] = ((*data16 >> 8) & 0xf8) | ((*data16 >> 13) & 0x07);
+ out[1] = ((*data16 >> 3) & 0xfc) | ((*data16 >> 9) & 0x03);
+ out[2] = ((*data16 << 3) & 0xf8) | ((*data16 >> 2) & 0x07);
+ }
+
+ out += 3;
+ data16 += 1;
+ }
+
+ return 1;
+}
+
+
+int Unpack16To32(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder)
+{
+ #ifdef TEST
+ *logofs << "Unpack16To32: Unpacking " << end - out
+ << " bytes of data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned short *data16 = (unsigned short *) data;
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ unsigned short pixel16;
+ unsigned int pixel32;
+
+ while (out32 < end32)
+ {
+
+ pixel16 = GetUINT((unsigned char *)data16, 0);
+
+ if (pixel16 == 0x0)
+ {
+ PutULONG(0x0, (unsigned char *) out32, imageByteOrder);
+ }
+ else if (pixel16 == 0xffff)
+ {
+ PutULONG(0xffffff, (unsigned char *) out32, imageByteOrder);
+ }
+ else
+ {
+ pixel32 = ((((pixel16 >> 8) & 0xf8) | ((pixel16 >> 13) & 0x07)) << 16) |
+ ((((pixel16 >> 3) & 0xfc) | ((pixel16 >> 9) & 0x03)) << 8) |
+ (((pixel16 << 3) & 0xf8) | ((pixel16 >> 2) & 0x07));
+
+ PutULONG(pixel32, (unsigned char *) out32, imageByteOrder);
+ }
+
+ out32++;
+ data16++;
+ }
+ return 1;
+}
+
+int Unpack16(T_geometry *geometry, int src_depth, int src_width, int src_height,
+ unsigned char *src_data, int src_size, int dst_depth, int dst_width,
+ int dst_height, unsigned char *dst_data, int dst_size)
+{
+ int imageByteOrder = geometry -> image_byte_order;
+
+ if (src_depth != 16)
+ {
+ #ifdef PANIC
+ *logofs << "Unpack16: PANIC! Cannot unpack colormapped image of source depth "
+ << src_depth << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ int (*unpack)(const unsigned char *data, unsigned char *out,
+ unsigned char *end, int imageByteOrder);
+
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ switch (dst_bpp)
+ {
+ case 16:
+ {
+ unpack = Unpack16To16;
+
+ break;
+ }
+ case 24:
+ {
+ unpack = Unpack16To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack16To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack16: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 16/24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (src_width == dst_width && src_height == dst_height)
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(src_data, dst_data, dst_end, imageByteOrder);
+ }
+ else if (src_width >= dst_width && src_height >= dst_height)
+ {
+ unsigned char *dst_end = dst_data;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+ dst_end += RoundUp4(dst_width * dst_bpp / 8);
+
+ (*unpack)(src_data, dst_data, dst_end, imageByteOrder);
+
+ src_data += src_width * 2;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Unpack16: PANIC! Cannot unpack image. "
+ << "Destination area " << dst_width << "x"
+ << dst_height << " is not fully contained in "
+ << src_width << "x" << src_height << " source.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Unpack24To24(const unsigned char *data,
+ unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack124To24: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ while (out < end)
+ {
+ *(out++) = *(data++);
+ }
+
+ return 1;
+}
+
+int Unpack24To32(const unsigned char *data, unsigned char *out, unsigned char *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack24To32: Unpacking " << end - out
+ << " bytes of colormapped data.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int *out32 = (unsigned int *) out;
+ unsigned int *end32 = (unsigned int *) end;
+
+ while (out32 < end32)
+ {
+ if (data[0] == 0x0 && data[1] == 0x0 && data[2] == 0x0)
+ {
+ *out32 = 0x0;
+ }
+ else if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff)
+ {
+ *out32 = 0xffffff;
+ }
+ else
+ {
+ *out32 = (data[2] << 16) | (data[1] << 8) | data[0];
+ }
+
+ out32 += 1;
+ data += 3;
+ }
+
+ return 1;
+}
+
+int Unpack24(T_geometry *geometry, int src_depth, int src_width, int src_height,
+ unsigned char *src_data, int src_size, int dst_depth, int dst_width,
+ int dst_height, unsigned char *dst_data, int dst_size)
+
+{
+ if (src_depth != 24)
+ {
+ #ifdef PANIC
+ *logofs << "Unpack24: PANIC! Cannot unpack colormapped image of source depth "
+ << src_depth << ".\n" << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ int (*unpack)(const unsigned char *data, unsigned char *out, unsigned char *end);
+
+ int dst_bpp = UnpackBitsPerPixel(geometry, dst_depth);
+
+ switch (dst_bpp)
+ {
+ case 24:
+ {
+ unpack = Unpack24To24;
+
+ break;
+ }
+ case 32:
+ {
+ unpack = Unpack24To32;
+
+ break;
+ }
+ default:
+ {
+ #ifdef PANIC
+ *logofs << "Unpack24: PANIC! Bad destination bits per pixel "
+ << dst_bpp << ". Only 24/32 are supported.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+ }
+
+ if (src_width == dst_width && src_height == dst_height)
+ {
+ unsigned char *dst_end = dst_data + dst_size;
+
+ (*unpack)(src_data, dst_data, dst_end);
+ }
+ else if (src_width >= dst_width && src_height >= dst_height)
+ {
+ unsigned char *dst_end = dst_data;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ dst_data = dst_end;
+ dst_end += RoundUp4(dst_width * dst_bpp / 8);
+
+ (*unpack)(src_data, dst_data, dst_end);
+
+ src_data += src_width * 3;
+ }
+ }
+ else
+ {
+ #ifdef PANIC
+ *logofs << "Unpack24: PANIC! Cannot unpack image. "
+ << "Destination area " << dst_width << "x"
+ << dst_height << " is not fully contained in "
+ << src_width << "x" << src_height << " source.\n"
+ << logofs_flush;
+ #endif
+
+ return -1;
+ }
+
+ return 1;
+}
+
+int Unpack32To32(const T_colormask *colormask, const unsigned int *data,
+ unsigned int *out, unsigned int *end)
+{
+ #ifdef TEST
+ *logofs << "Unpack32To32: Unpacking " << end - out
+ << " bytes of data.\n" << logofs_flush;
+ #endif
+
+ if (colormask -> correction_mask)
+ {
+ while (out < end)
+ {
+ if (*data == 0x00000000)
+ {
+ *out = 0x00000000;
+ }
+ else if (*data == 0xFFFFFFFF)
+ {
+ *out = 0xFFFFFFFF;
+ }
+ else
+ {
+ *out = *data | ((colormask -> correction_mask << 16) |
+ (colormask -> correction_mask << 8) |
+ colormask -> correction_mask);
+ }
+
+ out += 1;
+ data += 1;
+ }
+ }
+ else
+ {
+ #ifdef TEST
+ *logofs << "Unpack32To32: Using bitwise copy due to null correction mask.\n"
+ << logofs_flush;
+ #endif
+
+ memcpy((unsigned char *) out, (unsigned char *) data, end - out);
+ }
+
+ return 1;
+}
diff --git a/nxcomp/src/Unpack.h b/nxcomp/src/Unpack.h
new file mode 100644
index 000000000..faaa41d82
--- /dev/null
+++ b/nxcomp/src/Unpack.h
@@ -0,0 +1,149 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Unpack_H
+#define Unpack_H
+
+#include "NXpack.h"
+
+#include "Z.h"
+
+#define LSBFirst 0
+#define MSBFirst 1
+
+#define SPLIT_PATTERN 0x88
+
+typedef ColorMask T_colormask;
+
+//
+// Pixel geometry of channel's display.
+//
+
+typedef struct
+{
+ unsigned int depth1_bpp;
+ unsigned int depth4_bpp;
+ unsigned int depth8_bpp;
+ unsigned int depth16_bpp;
+ unsigned int depth24_bpp;
+ unsigned int depth32_bpp;
+
+ unsigned int red_mask;
+ unsigned int green_mask;
+ unsigned int blue_mask;
+
+ unsigned int image_byte_order;
+ unsigned int bitmap_bit_order;
+ unsigned int scanline_unit;
+ unsigned int scanline_pad;
+
+} T_geometry;
+
+//
+// Colormap is used to remap colors
+// from source to destination depth.
+//
+
+typedef struct
+{
+ unsigned int entries;
+ unsigned int *data;
+
+} T_colormap;
+
+//
+// Alpha channel data is added to 32
+// bits images at the time they are
+// unpacked.
+//
+
+typedef struct
+{
+ unsigned int entries;
+ unsigned char *data;
+
+} T_alpha;
+
+//
+// The ZLIB stream structure used for
+// the decompression.
+//
+
+extern z_stream unpackStream;
+
+//
+// Initialize the ZLIB stream used for
+// decompression.
+//
+
+void UnpackInit();
+
+//
+// Free the ZLIB stream.
+//
+
+void UnpackDestroy();
+
+//
+// Get the destination bits per pixel
+// based on the drawable depth.
+//
+
+int UnpackBitsPerPixel(T_geometry *geometry, unsigned int depth);
+
+//
+// Unpack the source data into the X
+// bitmap.
+//
+
+int Unpack8(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack16(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack24(T_geometry *geometry, const T_colormask *colormask, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack8(T_geometry *geometry, T_colormap *colormap, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack15(T_geometry *geometry, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack16(T_geometry *geometry, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+int Unpack24(T_geometry *geometry, int src_depth, int src_width,
+ int src_height, unsigned char *src_data, int src_size, int dst_depth,
+ int dst_width, int dst_height, unsigned char *dst_data, int dst_size);
+
+#endif /* Unpack_H */
diff --git a/nxcomp/src/Vars.c b/nxcomp/src/Vars.c
new file mode 100644
index 000000000..685969677
--- /dev/null
+++ b/nxcomp/src/Vars.c
@@ -0,0 +1,54 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+
+#include "NXvars.h"
+
+/*
+ * Allocate here instances of variables and
+ * pointers declared in NXvars.h.
+ */
+
+int _NXHandleDisplayError = 0;
+
+NXDisplayErrorPredicate _NXDisplayErrorFunction = NULL;
+
+int _NXUnsetLibraryPath = 0;
+
+NXLostSequenceHandler _NXLostSequenceFunction = NULL;
+
+NXDisplayBlockHandler _NXDisplayBlockFunction = NULL;
+NXDisplayWriteHandler _NXDisplayWriteFunction = NULL;
+NXDisplayFlushHandler _NXDisplayFlushFunction = NULL;
+NXDisplayStatisticsHandler _NXDisplayStatisticsFunction = NULL;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/nxcomp/src/Version.c b/nxcomp/src/Version.c
new file mode 100644
index 000000000..509bd8344
--- /dev/null
+++ b/nxcomp/src/Version.c
@@ -0,0 +1,101 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2015 Qindel Formacion y Servicios SL. */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License Version 2, as */
+/* published by the Free Software Foundation. */
+/* */
+/* This program is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTA- */
+/* BILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General */
+/* Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, you can request a copy from Qindel */
+/* or write to the Free Software Foundation, Inc., 51 Franklin Street, */
+/* Fifth Floor, Boston, MA 02110-1301 USA. */
+/* */
+/* All rights reserved. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "NX.h"
+
+
+static int _NXVersionMajor = -1;
+static int _NXVersionMinor = -1;
+static int _NXVersionPatch = -1;
+static int _NXVersionMaintenancePatch = -1;
+
+
+const char* NXVersion() {
+ const char *version = VERSION;
+ return version;
+}
+
+void _parseNXVersion() {
+ char version[32];
+ int i;
+ strcpy(version, VERSION);
+
+ char *value;
+ /* Reset values to 0 if undefined */
+ _NXVersionMajor = _NXVersionMinor = _NXVersionPatch = _NXVersionMaintenancePatch = 0;
+
+
+#define NXVERSIONSEPARATOR "."
+ value = strtok(version, NXVERSIONSEPARATOR);
+
+ for (i = 0; value != NULL && i < 4; i++)
+ {
+ switch (i)
+ {
+ case 0:
+ _NXVersionMajor = atoi(value);
+ break;
+
+ case 1:
+ _NXVersionMinor = atoi(value);
+ break;
+
+ case 2:
+ _NXVersionPatch = atoi(value);
+ break;
+
+ case 3:
+ _NXVersionMaintenancePatch = atoi(value);
+ break;
+ }
+
+ value = strtok(NULL, NXVERSIONSEPARATOR);
+ }
+}
+
+int NXMajorVersion() {
+ if (_NXVersionMajor == -1)
+ _parseNXVersion();
+ return _NXVersionMajor;
+}
+int NXMinorVersion() {
+ if (_NXVersionMinor == -1)
+ _parseNXVersion();
+ return _NXVersionMinor;
+}
+int NXPatchVersion() {
+ if (_NXVersionPatch == -1)
+ _parseNXVersion();
+ return _NXVersionPatch;
+}
+int NXMaintenancePatchVersion() {
+ if (_NXVersionMaintenancePatch == -1)
+ _parseNXVersion();
+ return _NXVersionMaintenancePatch;
+}
diff --git a/nxcomp/src/WriteBuffer.cpp b/nxcomp/src/WriteBuffer.cpp
new file mode 100644
index 000000000..70b4e3b53
--- /dev/null
+++ b/nxcomp/src/WriteBuffer.cpp
@@ -0,0 +1,500 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "Misc.h"
+#include "Control.h"
+
+#include "WriteBuffer.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+WriteBuffer::WriteBuffer()
+{
+ size_ = WRITE_BUFFER_DEFAULT_SIZE;
+ buffer_ = new unsigned char[size_];
+ length_ = 0;
+ index_ = NULL;
+
+ scratchLength_ = 0;
+ scratchBuffer_ = NULL;
+ scratchOwner_ = 1;
+
+ initialSize_ = WRITE_BUFFER_DEFAULT_SIZE;
+ thresholdSize_ = WRITE_BUFFER_DEFAULT_SIZE << 1;
+ maximumSize_ = WRITE_BUFFER_DEFAULT_SIZE << 4;
+
+ #ifdef VALGRIND
+
+ memset(buffer_, '\0', size_);
+
+ #endif
+}
+
+WriteBuffer::~WriteBuffer()
+{
+ if (scratchOwner_ == 1 &&
+ scratchBuffer_ != NULL)
+ {
+ delete [] scratchBuffer_;
+ }
+
+ delete [] buffer_;
+}
+
+void WriteBuffer::setSize(unsigned int initialSize, unsigned int thresholdSize,
+ unsigned int maximumSize)
+{
+ initialSize_ = initialSize;
+ thresholdSize_ = thresholdSize;
+ maximumSize_ = maximumSize;
+
+ #ifdef TEST
+ *logofs << "WriteBuffer: Set buffer sizes to "
+ << initialSize_ << "/" << thresholdSize_
+ << "/" << maximumSize_ << ".\n"
+ << logofs_flush;
+ #endif
+}
+
+void WriteBuffer::partialReset()
+{
+ if (scratchBuffer_ != NULL)
+ {
+ if (scratchOwner_)
+ {
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Going to delete "
+ << scratchLength_ << " bytes from the "
+ << "scratch buffer.\n" << logofs_flush;
+ #endif
+
+ delete [] scratchBuffer_;
+ }
+
+ scratchLength_ = 0;
+ scratchBuffer_ = NULL;
+ scratchOwner_ = 1;
+ }
+
+ length_ = 0;
+ index_ = NULL;
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Performed partial reset with "
+ << size_ << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+}
+
+void WriteBuffer::fullReset()
+{
+ if (scratchBuffer_ != NULL)
+ {
+ if (scratchOwner_ == 1)
+ {
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Going to delete "
+ << scratchLength_ << " bytes from the "
+ << "scratch buffer.\n" << logofs_flush;
+ #endif
+
+ delete [] scratchBuffer_;
+ }
+
+ scratchLength_ = 0;
+ scratchBuffer_ = NULL;
+ scratchOwner_ = 1;
+ }
+
+ length_ = 0;
+ index_ = NULL;
+
+ if (size_ > initialSize_)
+ {
+ #ifdef TEST
+ *logofs << "WriteBuffer: Reallocating a new buffer of "
+ << initialSize_ << " bytes.\n" << logofs_flush;
+ #endif
+
+ delete [] buffer_;
+
+ size_ = initialSize_;
+
+ buffer_ = new unsigned char[size_];
+
+ if (buffer_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't allocate memory for "
+ << "X messages in context [A].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "X messages in context [A].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef VALGRIND
+
+ memset(buffer_, '\0', size_);
+
+ #endif
+ }
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Performed full reset with "
+ << size_ << " bytes in buffer.\n"
+ << logofs_flush;
+ #endif
+}
+
+unsigned char *WriteBuffer::addMessage(unsigned int numBytes)
+{
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Adding " << numBytes << " bytes to "
+ << length_ << " bytes already in buffer.\n"
+ << logofs_flush;
+ #endif
+
+ if (numBytes > WRITE_BUFFER_OVERFLOW_SIZE)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't add a message of "
+ << numBytes << " bytes.\n" << logofs_flush;
+
+ *logofs << "WriteBuffer: PANIC! Assuming error handling "
+ << "data in context [B].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't add a message of "
+ << numBytes << " bytes to write buffer.\n";
+
+ cerr << "Error" << ": Assuming error handling "
+ << "data in context [B].\n";
+
+ HandleAbort();
+ }
+ else if (length_ + numBytes > size_)
+ {
+ unsigned int newSize = thresholdSize_;
+
+ while (newSize < length_ + numBytes)
+ {
+ newSize <<= 1;
+
+ if (newSize > maximumSize_)
+ {
+ newSize = length_ + numBytes + initialSize_;
+ }
+ }
+
+ #ifdef TEST
+ *logofs << "WriteBuffer: Growing buffer from "
+ << size_ << " to " << newSize << " bytes.\n"
+ << logofs_flush;
+ #endif
+
+ unsigned int indexOffset = 0;
+
+ if (index_ && *index_)
+ {
+ indexOffset = *index_ - buffer_;
+ }
+
+ size_ = newSize;
+
+ unsigned char *newBuffer = new unsigned char[size_];
+
+ if (newBuffer == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't allocate memory for "
+ << "X messages in context [C].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "X messages in context [C].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef TEST
+ if (newSize >= maximumSize_)
+ {
+ *logofs << "WriteBuffer: WARNING! Buffer grown to reach "
+ << "size of " << newSize << " bytes.\n"
+ << logofs_flush;
+ }
+ #endif
+
+ #ifdef VALGRIND
+
+ memset(newBuffer, '\0', size_);
+
+ #endif
+
+ memcpy(newBuffer, buffer_, length_);
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Going to delete the "
+ << "old buffer with new size " << size_
+ << ".\n" << logofs_flush;
+ #endif
+
+ delete [] buffer_;
+
+ buffer_ = newBuffer;
+
+ if (index_ && *index_)
+ {
+ *index_ = buffer_ + indexOffset;
+ }
+ }
+
+ unsigned char *result = buffer_ + length_;
+
+ length_ += numBytes;
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Bytes in buffer are "
+ << length_ << " while size is " << size_
+ << ".\n" << logofs_flush;
+ #endif
+
+ return result;
+}
+
+unsigned char *WriteBuffer::removeMessage(unsigned int numBytes)
+{
+ #ifdef TEST
+ *logofs << "WriteBuffer: Removing " << numBytes
+ << " bytes from buffer.\n" << logofs_flush;
+ #endif
+
+ if (numBytes > length_)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't remove "
+ << numBytes << " bytes with only " << length_
+ << " bytes in buffer.\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Buffer underflow handling "
+ << "write buffer in context [D].\n";
+
+ HandleAbort();
+ }
+
+ length_ -= numBytes;
+
+ #ifdef TEST
+ *logofs << "WriteBuffer: Bytes in buffer are now "
+ << length_ << " while size is "
+ << size_ << ".\n" << logofs_flush;
+ #endif
+
+ return (buffer_ + length_);
+}
+
+unsigned char *WriteBuffer::addScratchMessage(unsigned int numBytes)
+{
+ if (numBytes > WRITE_BUFFER_OVERFLOW_SIZE)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't add a message of "
+ << numBytes << " bytes.\n" << logofs_flush;
+
+ *logofs << "WriteBuffer: PANIC! Assuming error handling "
+ << "data in context [E].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't add a message of "
+ << numBytes << " bytes to write buffer.\n";
+
+ cerr << "Error" << ": Assuming error handling "
+ << "data in context [E].\n";
+
+ HandleAbort();
+ }
+ else if (scratchBuffer_ != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't add a message of "
+ << numBytes << " bytes with " << scratchLength_
+ << " bytes already in scratch buffer.\n"
+ << logofs_flush;
+
+ *logofs << "WriteBuffer: PANIC! Assuming error handling "
+ << "data in context [F].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't add a message of "
+ << numBytes << " bytes with " << scratchLength_
+ << " bytes already in scratch buffer.\n";
+
+ cerr << "Error" << ": Assuming error handling "
+ << "data in context [F].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Adding " << numBytes << " bytes "
+ << "to scratch buffer.\n" << logofs_flush;
+ #endif
+
+ unsigned char *newBuffer = new unsigned char[numBytes];
+
+ if (newBuffer == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't allocate memory for "
+ << "X messages in context [G].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't allocate memory for "
+ << "X messages in context [G].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef VALGRIND
+
+ memset(newBuffer, '\0', numBytes);
+
+ #endif
+
+ scratchBuffer_ = newBuffer;
+ scratchOwner_ = 1;
+ scratchLength_ = numBytes;
+
+ return newBuffer;
+}
+
+unsigned char *WriteBuffer::addScratchMessage(unsigned char *newBuffer, unsigned int numBytes)
+{
+ if (numBytes > WRITE_BUFFER_OVERFLOW_SIZE)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't add a message of "
+ << numBytes << " bytes.\n" << logofs_flush;
+
+ *logofs << "WriteBuffer: PANIC! Assuming error handling "
+ << "data in context [H].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't add a message of "
+ << numBytes << " bytes to write buffer.\n";
+
+ cerr << "Error" << ": Assuming error handling "
+ << "data in context [H].\n";
+
+ HandleAbort();
+ }
+ else if (scratchBuffer_ != NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't add a foreign "
+ << "message of " << numBytes << " bytes with "
+ << scratchLength_ << " bytes already in "
+ << "scratch buffer.\n" << logofs_flush;
+
+ *logofs << "WriteBuffer: PANIC! Assuming error handling "
+ << "data in context [I].\n" << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't add a foreign message of "
+ << numBytes << " bytes with " << scratchLength_
+ << " bytes already in scratch buffer.\n";
+
+ cerr << "Error" << ": Assuming error handling "
+ << "data in context [I].\n";
+
+ HandleAbort();
+ }
+
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Adding " << numBytes << " bytes "
+ << "from a foreign message to scratch buffer.\n"
+ << logofs_flush;
+ #endif
+
+ scratchBuffer_ = newBuffer;
+ scratchLength_ = numBytes;
+ scratchOwner_ = 0;
+
+ return newBuffer;
+}
+
+void WriteBuffer::removeScratchMessage()
+{
+ #ifdef TEST
+
+ if (scratchLength_ == 0 || scratchBuffer_ == NULL)
+ {
+ #ifdef PANIC
+ *logofs << "WriteBuffer: PANIC! Can't remove non existent scratch message.\n"
+ << logofs_flush;
+ #endif
+
+ cerr << "Error" << ": Can't remove non existent scratch message.\n";
+
+ HandleAbort();
+ }
+
+ *logofs << "WriteBuffer: Removing " << scratchLength_
+ << " bytes from scratch buffer.\n"
+ << logofs_flush;
+
+ #endif
+
+ if (scratchOwner_ == 1)
+ {
+ #ifdef DEBUG
+ *logofs << "WriteBuffer: Going to delete "
+ << scratchLength_ << " bytes from the "
+ << "scratch buffer.\n" << logofs_flush;
+ #endif
+
+ delete [] scratchBuffer_;
+ }
+
+ scratchLength_ = 0;
+ scratchBuffer_ = NULL;
+ scratchOwner_ = 1;
+}
diff --git a/nxcomp/src/WriteBuffer.h b/nxcomp/src/WriteBuffer.h
new file mode 100644
index 000000000..ce408e210
--- /dev/null
+++ b/nxcomp/src/WriteBuffer.h
@@ -0,0 +1,134 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef WriteBuffer_H
+#define WriteBuffer_H
+
+#include "Misc.h"
+
+#define PANIC
+#define WARNING
+#undef TEST
+#undef DEBUG
+
+#define WRITE_BUFFER_DEFAULT_SIZE 16384
+
+//
+// Adjust for the biggest reply that we could receive.
+// This is likely to be a reply to a X_ListFonts where
+// user has a large amount of installed fonts.
+//
+
+#define WRITE_BUFFER_OVERFLOW_SIZE 4194304
+
+class WriteBuffer
+{
+ public:
+
+ WriteBuffer();
+
+ ~WriteBuffer();
+
+ void setSize(unsigned int initialSize, unsigned int thresholdSize,
+ unsigned int maximumSize);
+
+ unsigned char *addMessage(unsigned int numBytes);
+
+ unsigned char *removeMessage(unsigned int numBytes);
+
+ unsigned char *addScratchMessage(unsigned int numBytes);
+
+ //
+ // This form allows user to provide its own
+ // buffer as write buffer's scratch area.
+ //
+
+ unsigned char *addScratchMessage(unsigned char *newBuffer, unsigned int numBytes);
+
+ void removeScratchMessage();
+
+ void partialReset();
+
+ void fullReset();
+
+ unsigned char *getData() const
+ {
+ return buffer_;
+ }
+
+ unsigned int getLength() const
+ {
+ return length_;
+ }
+
+ unsigned int getAvailable() const
+ {
+ return (size_ - length_);
+ }
+
+ unsigned char *getScratchData() const
+ {
+ return scratchBuffer_;
+ }
+
+ unsigned int getScratchLength() const
+ {
+ return scratchLength_;
+ }
+
+ unsigned int getTotalLength() const
+ {
+ return (length_ + scratchLength_);
+ }
+
+ void registerPointer(unsigned char **pointer)
+ {
+ index_ = pointer;
+ }
+
+ void unregisterPointer()
+ {
+ index_ = 0;
+ }
+
+ private:
+
+ unsigned int size_;
+ unsigned int length_;
+
+ unsigned char *buffer_;
+ unsigned char **index_;
+
+ unsigned int scratchLength_;
+ unsigned char *scratchBuffer_;
+
+ int scratchOwner_;
+
+ unsigned int initialSize_;
+ unsigned int thresholdSize_;
+ unsigned int maximumSize_;
+};
+
+#endif /* WriteBuffer_H */
diff --git a/nxcomp/src/XidCache.cpp b/nxcomp/src/XidCache.cpp
new file mode 100644
index 000000000..ab6574cc9
--- /dev/null
+++ b/nxcomp/src/XidCache.cpp
@@ -0,0 +1,51 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Control.h"
+
+#include "XidCache.h"
+
+XidCache::XidCache()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ base_[i] = new IntCache(8);
+ }
+
+ slot_ = 0;
+ last_ = 0;
+}
+
+XidCache::~XidCache()
+{
+ for (int i = 0; i < 256; i++)
+ {
+ delete base_[i];
+ }
+}
diff --git a/nxcomp/src/XidCache.h b/nxcomp/src/XidCache.h
new file mode 100644
index 000000000..8a09ef9b1
--- /dev/null
+++ b/nxcomp/src/XidCache.h
@@ -0,0 +1,49 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef XidCache_H
+#define XidCache_H
+
+#include "IntCache.h"
+
+class XidCache
+{
+ friend class EncodeBuffer;
+ friend class DecodeBuffer;
+
+ public:
+
+ XidCache();
+ ~XidCache();
+
+ private:
+
+ IntCache *base_[256];
+
+ unsigned int slot_;
+ unsigned int last_;
+};
+
+#endif /* XidCache_H */
diff --git a/nxcomp/src/Z.cpp b/nxcomp/src/Z.cpp
new file mode 100644
index 000000000..af3b380f2
--- /dev/null
+++ b/nxcomp/src/Z.cpp
@@ -0,0 +1,146 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "Z.h"
+#include "Misc.h"
+
+int ZCompress(z_stream *stream, unsigned char *dest, unsigned int *destLen,
+ const unsigned char *source, unsigned int sourceLen)
+{
+ //
+ // Deal with the possible overflow.
+ //
+
+ if (stream -> total_out & 0x80000000)
+ {
+ #ifdef TEST
+ *logofs << "ZCompress: Reset stream counters with "
+ << "total in " << stream -> total_in
+ << " and total out " << stream -> total_out
+ << ".\n" << logofs_flush;
+ #endif
+
+ stream -> total_in = 0;
+ stream -> total_out = 0;
+ }
+
+ unsigned int saveOut = stream -> total_out;
+
+ stream -> next_in = (Bytef *) source;
+ stream -> avail_in = sourceLen;
+
+ //
+ // Check if the source is bigger than
+ // 64K on 16-bit machine.
+ //
+
+ #ifdef MAXSEG_64K
+
+ if ((uLong) stream -> avail_in != sourceLen) return Z_BUF_ERROR;
+
+ #endif
+
+ stream -> next_out = dest;
+ stream -> avail_out = *destLen;
+
+ #ifdef MAXSEG_64K
+
+ if ((uLong) stream -> avail_out != *destLen) return Z_BUF_ERROR;
+
+ #endif
+
+ int result = deflate(stream, Z_FINISH);
+
+ if (result != Z_STREAM_END)
+ {
+ deflateReset(stream);
+
+ return (result == Z_OK ? Z_BUF_ERROR : result);
+ }
+
+ *destLen = stream -> total_out - saveOut;
+
+ result = deflateReset(stream);
+
+ return result;
+}
+
+int ZDecompress(z_stream *stream, unsigned char *dest, unsigned int *destLen,
+ const unsigned char *source, unsigned int sourceLen)
+{
+ stream -> next_in = (Bytef *) source;
+ stream -> avail_in = sourceLen;
+
+ //
+ // Deal with the possible overflow.
+ //
+
+ if (stream -> total_out & 0x80000000)
+ {
+ #ifdef TEST
+ *logofs << "ZDecompress: Reset stream counters with "
+ << "total in " << stream -> total_in
+ << " and total out " << stream -> total_out
+ << ".\n" << logofs_flush;
+ #endif
+
+ stream -> total_in = 0;
+ stream -> total_out = 0;
+ }
+
+ unsigned int saveOut = stream -> total_out;
+
+ if (stream -> avail_in != sourceLen)
+ {
+ return Z_BUF_ERROR;
+ }
+
+ stream -> next_out = dest;
+ stream -> avail_out = *destLen;
+
+ if (stream -> avail_out != *destLen)
+ {
+ return Z_BUF_ERROR;
+ }
+
+ int result = inflate(stream, Z_FINISH);
+
+ if (result != Z_STREAM_END)
+ {
+ inflateReset(stream);
+
+ return (result == Z_OK ? Z_BUF_ERROR : result);
+ }
+
+ *destLen = stream -> total_out - saveOut;
+
+ result = inflateReset(stream);
+
+ return result;
+}
diff --git a/nxcomp/src/Z.h b/nxcomp/src/Z.h
new file mode 100644
index 000000000..3a6d684c2
--- /dev/null
+++ b/nxcomp/src/Z.h
@@ -0,0 +1,37 @@
+/**************************************************************************/
+/* */
+/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */
+/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> */
+/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de> */
+/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de> */
+/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
+/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */
+/* */
+/* NXCOMP, NX protocol compression and NX extensions to this software */
+/* are copyright of the aforementioned persons and companies. */
+/* */
+/* Redistribution and use of the present software is allowed according */
+/* to terms specified in the file LICENSE.nxcomp which comes in the */
+/* source distribution. */
+/* */
+/* All rights reserved. */
+/* */
+/* NOTE: This software has received contributions from various other */
+/* contributors, only the core maintainers and supporters are listed as */
+/* copyright holders. Please contact us, if you feel you should be listed */
+/* as copyright holder, as well. */
+/* */
+/**************************************************************************/
+
+#ifndef Z_H
+#define Z_H
+
+#include <zlib.h>
+
+int ZCompress(z_stream *stream, unsigned char *dest, unsigned int *destLen,
+ const unsigned char *source, unsigned int sourceLen);
+
+int ZDecompress(z_stream *stream, unsigned char *dest, unsigned int *destLen,
+ const unsigned char *source, unsigned int sourceLen);
+
+#endif /* Z_H */